Aller au contenu
CI/CD & Automatisation medium

GitLab CI : audit, conformité et traçabilité des pipelines

20 min de lecture

La question d'un auditeur n'est jamais "est-ce que vous êtes sécurisés ?" mais "prouvez-le". GitLab enregistre des centaines de types d'événements d'audit : qui a modifié une branche protégée, qui a approuvé un déploiement, qui a changé les permissions d'un runner. Ce guide vous montre comment exploiter ces mécanismes pour répondre aux exigences SOC 2, ISO 27001 ou NIS2 avec des données concrètes.

À la fin de ce guide, vous saurez :

  • Consulter les audit events de GitLab (projet, groupe, instance)
  • Requêter l'API d'audit pour extraire des événements précis
  • Mettre en place un compliance framework pour appliquer des politiques par projet
  • Configurer des compliance pipelines pour forcer l'exécution de jobs de sécurité
  • Streamer les événements vers un SIEM externe (Splunk, ELK, Datadog)

L'audit et la conformité deviennent nécessaires quand :

  • Un auditeur externe (SOC 2, ISO 27001) demande la preuve que les déploiements suivent un processus de validation
  • Votre organisation est soumise à NIS2 et doit démontrer la traçabilité de sa chaîne logicielle
  • Un incident de sécurité survient et vous devez reconstituer qui a fait quoi, quand
  • Vous gérez des dizaines de projets et devez garantir que chacun applique les mêmes règles de sécurité
  • Des développeurs contournent les contrôles (désactivation d'une branche protégée, suppression d'une approbation) et vous devez le détecter

GitLab enregistre des événements à trois niveaux :

NiveauVisibilitéExemples d'événements
ProjetMaintainer+MR mergée, pipeline lancé, variable modifiée, branche protégée changée
GroupeOwnerMembre ajouté/retiré, permission changée, runner partagé configuré
InstanceAdminUtilisateur créé, 2FA désactivé, paramètre global modifié

Projet : Settings → General → Audit events (GitLab Premium+).

Vous voyez un journal chronologique avec l'auteur, l'action et la cible.

ÉvénementPourquoi le surveillerCatégorie
protected_branch_created / removedQuelqu'un a modifié la politique de branchesContrôle d'accès
approval_rule_created / deletedLes règles d'approbation MR ont changéProcessus de validation
environment_protected / unprotectedL'environnement de production a perdu sa protectionDéploiement
variable_created / updated / deletedUn secret CI a été modifiéSecrets
runner_registrationUn nouveau runner a été enregistréInfrastructure
member_added / removedLes permissions d'accès au projet ont changéContrôle d'accès
deploy_key_addedUne clé de déploiement a été ajoutéeAccès externe
merge_request_approvedUne MR a été approuvée (par qui ?)Traçabilité
pipeline_schedule_createdUn pipeline planifié a été crééAutomatisation

Les auditeurs posent des questions précises. Voici comment y répondre avec l'API :

"Qui a modifié les branches protégées ce mois-ci ?"

Fenêtre de terminal
curl -s --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
"$GITLAB_URL/api/v4/projects/$PROJECT_ID/audit_events?created_after=2026-03-01" \
| jq '[.[] | select(.details.change == "protected_branch")]
| .[] | {date: .created_at, author: .details.author_name, action: .details.custom_message}'

"Qui a approuvé les MR déployées en production ?"

Fenêtre de terminal
# Lister les MR mergées sur main ce mois
curl -s --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
"$GITLAB_URL/api/v4/projects/$PROJECT_ID/merge_requests?state=merged&target_branch=main&updated_after=2026-03-01" \
| jq '.[] | {iid: .iid, title: .title, merged_by: .merged_by.name, approved_by: [.approved_by[].user.name]}'

"Y a-t-il eu des déploiements pendant le freeze ?"

Fenêtre de terminal
curl -s --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
"$GITLAB_URL/api/v4/projects/$PROJECT_ID/deployments?environment=production&updated_after=2026-03-28&updated_before=2026-04-02" \
| jq '.[] | {id: .id, created_at: .created_at, status: .status, deployed_by: .deployable.user.name}'

Pour générer un rapport d'audit régulier :

#!/usr/bin/env bash
# audit-report.sh — Rapport d'audit mensuel pour un projet GitLab
set -euo pipefail
PROJECT_ID="${1:?Usage: $0 <project_id>}"
SINCE=$(date -d "30 days ago" +%Y-%m-%d)
API="$GITLAB_URL/api/v4/projects/$PROJECT_ID"
AUTH="PRIVATE-TOKEN: $GITLAB_TOKEN"
echo "=== Rapport d'audit — Projet $PROJECT_ID — depuis $SINCE ==="
echo ""
echo "--- Modifications de branches protégées ---"
curl -s -H "$AUTH" "$API/audit_events?created_after=$SINCE" \
| jq -r '.[] | select(.details.change == "protected_branch")
| "\(.created_at) | \(.details.author_name) | \(.details.custom_message)"'
echo ""
echo "--- Modifications de variables CI ---"
curl -s -H "$AUTH" "$API/audit_events?created_after=$SINCE" \
| jq -r '.[] | select(.details.change == "ci_variable")
| "\(.created_at) | \(.details.author_name) | \(.details.custom_message)"'
echo ""
echo "--- Déploiements en production ---"
curl -s -H "$AUTH" "$API/deployments?environment=production&updated_after=$SINCE" \
| jq -r '.[] | "\(.created_at) | \(.deployable.user.name // "système") | \(.status) | ref:\(.ref)"'
echo ""
echo "--- MR mergées sur main ---"
curl -s -H "$AUTH" "$API/merge_requests?state=merged&target_branch=main&updated_after=$SINCE" \
| jq -r '.[] | "!\(.iid) | \(.merged_at) | \(.merged_by.name) | \(.title)"'

Un compliance framework est un label appliqué à un projet pour indiquer qu'il est soumis à une politique spécifique. Il sert deux objectifs :

  1. Visibilité : identifier rapidement quels projets sont soumis à quelle réglementation
  2. Enforcement : attacher un compliance pipeline qui s'exécute automatiquement
  1. Créer le framework au niveau du groupe

    Group → Settings → General → Compliance frameworks → New framework.

    ChampExemple
    NameSOC 2, Production
    DescriptionProjets soumis aux contrôles SOC 2 Type II
    Color#FF0000
    Compliance pipelineURL du fichier .gitlab-ci.yml de conformité
  2. Attacher le framework à un projet

    Project → Settings → General → Compliance framework → sélectionner le framework.

    Le label apparaît sur la page du projet et dans les listes de projets.

  3. Vérifier via l'API

    Fenêtre de terminal
    curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
    "$GITLAB_URL/api/v4/projects/$PROJECT_ID" \
    | jq '.compliance_frameworks'

    Résultat attendu :

    ["SOC 2 — Production"]

Un développeur peut modifier ou supprimer les jobs de sécurité dans son .gitlab-ci.yml. Les includes partagés peuvent être commentés. Rien ne garantit que le scan de vulnérabilités s'exécute réellement.

Un compliance pipeline est un .gitlab-ci.yml maintenu par l'équipe sécurité, exécuté automatiquement sur tout projet attaché à un compliance framework. Le développeur ne peut pas le modifier ni le désactiver.

compliance-pipeline.yml (dans un projet dédié)
stages:
- compliance-check
- .pre
- build
- test
- deploy
- .post
- compliance-report
# Ce job s'exécute AVANT le pipeline du développeur
compliance-secret-scan:
stage: compliance-check
image:
name: zricethezav/gitleaks:latest
entrypoint: [""]
script:
- gitleaks detect --source . --verbose --exit-code 1
allow_failure: false
# Ce job s'exécute APRÈS le pipeline du développeur
compliance-report:
stage: compliance-report
image: alpine:3.20
script:
- echo "Pipeline $CI_PIPELINE_ID — Projet $CI_PROJECT_PATH"
- echo "Commit $CI_COMMIT_SHA — Auteur $GITLAB_USER_NAME"
- echo "Branche $CI_COMMIT_BRANCH — Ref $CI_COMMIT_REF_NAME"
- echo "Compliance check passed at $(date -u)"
artifacts:
paths:
- compliance-*.log
expire_in: 1 year

Le compliance pipeline remplace le pipeline du projet. Il utilise include: pour incorporer le .gitlab-ci.yml du développeur entre ses propres stages :

compliance-pipeline.yml (version avec include)
include:
- project: '$CI_PROJECT_PATH'
ref: '$CI_COMMIT_SHA'
file: '.gitlab-ci.yml'
stages:
- compliance-check
- build
- test
- deploy
- compliance-report
compliance-secret-scan:
stage: compliance-check
# ... (comme ci-dessus)
compliance-report:
stage: compliance-report
# ... (comme ci-dessus)

Le développeur garde la main sur ses stages build, test, deploy. L'équipe sécurité contrôle les stages compliance-check et compliance-report.

Pour une surveillance continue, GitLab peut streamer les audit events vers une destination externe :

  1. Configurer la destination

    Group → Settings → General → Audit events → Streaming → Add streaming destination.

    ChampValeur
    Destination URLhttps://siem.example.com/api/gitlab-events
    Verification tokenToken partagé pour authentifier les requêtes
  2. Format des événements

    GitLab envoie un POST HTTP avec un payload JSON pour chaque événement :

    {
    "id": 5678,
    "author_id": 42,
    "entity_id": 7,
    "entity_type": "Project",
    "event_type": "audit_events",
    "details": {
    "change": "protected_branch",
    "from": "Developers + Maintainers",
    "to": "No one",
    "author_name": "Alice Martin",
    "target_details": "main"
    },
    "created_at": "2026-04-15T10:30:00Z"
    }
  3. Vérifier la réception

    Utilisez un endpoint de test pour valider le flux :

    Fenêtre de terminal
    # Lister les destinations de streaming
    curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
    "$GITLAB_URL/api/v4/groups/$GROUP_ID/audit_events/streaming/destinations"
SIEMMéthodeConfiguration
SplunkHTTP Event CollectorURL HEC + token
ELK / OpenSearchEndpoint HTTPLogstash HTTP input ou direct Elasticsearch
DatadogHTTP Log endpointhttps://http-intake.logs.datadoghq.eu/api/v2/logs + API key
Google ChronicleWebhookIngestion API endpoint

Combinez les données d'audit avec les informations de configuration pour produire un score de conformité :

#!/usr/bin/env bash
# compliance-score.sh — Score de conformité d'un projet GitLab
set -euo pipefail
PROJECT_ID="${1:?Usage: $0 <project_id>}"
API="$GITLAB_URL/api/v4/projects/$PROJECT_ID"
AUTH="PRIVATE-TOKEN: $GITLAB_TOKEN"
score=0
total=0
check() {
local label="$1" result="$2"
total=$((total + 1))
if [[ "$result" == "true" ]]; then
score=$((score + 1))
echo " [OK] $label"
else
echo " [KO] $label"
fi
}
echo "=== Compliance score — Projet $PROJECT_ID ==="
# Branche main protégée ?
main_protected=$(curl -s -H "$AUTH" "$API/protected_branches" \
| jq 'any(.[]; .name == "main")')
check "Branche main protégée" "$main_protected"
# Force push interdit ?
no_force=$(curl -s -H "$AUTH" "$API/protected_branches" \
| jq '[.[] | select(.name == "main")] | .[0].allow_force_push == false')
check "Force push interdit sur main" "$no_force"
# Tags protégés ?
tags_protected=$(curl -s -H "$AUTH" "$API/protected_tags" \
| jq 'length > 0')
check "Tags protégés configurés" "$tags_protected"
# MR obligatoire sur main ?
no_push=$(curl -s -H "$AUTH" "$API/protected_branches" \
| jq '[.[] | select(.name == "main")] | .[0].push_access_levels | any(.[]; .access_level == 0)')
check "Push direct interdit sur main" "$no_push"
# Approbation MR requise ?
approvals=$(curl -s -H "$AUTH" "$API/approval_rules" \
| jq 'any(.[]; .approvals_required > 0)')
check "Approbation MR requise" "$approvals"
# Pipeline actif ?
has_pipeline=$(curl -s -H "$AUTH" "$API/pipelines?per_page=1&status=success" \
| jq 'length > 0')
check "Pipeline CI actif" "$has_pipeline"
echo ""
echo "Score : $score / $total"

Résultat attendu :

=== Compliance score — Projet 42 ===
[OK] Branche main protégée
[OK] Force push interdit sur main
[KO] Tags protégés configurés
[OK] Push direct interdit sur main
[KO] Approbation MR requise
[OK] Pipeline CI actif
Score : 4 / 6
SymptômeCause probableCorrection
Audit events vides (API retourne [])Tier CE sans PremiumMettre à niveau ou utiliser l'API MR/deployments comme alternative
Compliance framework non disponibleGitLab Ultimate requisUtiliser des topics + includes partagés comme alternative
Streaming : pas de réception côté SIEMURL incorrecte ou token invalideTester avec curl direct, vérifier les logs SIEM
Compliance pipeline ne se déclenche pasFramework non attaché au projetVérifier Settings → General → Compliance framework
Script audit-report.sh : jq: errorPas d'événements pour le filtreAjouter // empty au filtre jq pour gérer les résultats vides
403 Forbidden sur /audit_eventsToken sans droits admin ou OwnerUtiliser un token Maintainer pour les events projet, Owner pour groupe
Événements manquants dans le journalRétention par défaut limitéeConfigurer la rétention dans Admin → Settings (instance)

L'audit et la conformité dans GitLab CI reposent sur cinq principes :

  1. Tout est enregistré : GitLab trace les modifications de branches protégées, de variables, de permissions, de runners. Ces événements sont votre source de vérité pour répondre aux auditeurs.
  2. L'API est votre outil d'investigation : l'interface montre un journal, l'API permet des requêtes ciblées. Automatisez les rapports avec des scripts pour ne pas dépendre d'une consultation manuelle.
  3. Les compliance frameworks imposent des politiques : attacher un framework à un projet garantit que le compliance pipeline s'exécute, sans dépendre de la bonne volonté du développeur (GitLab Ultimate).
  4. Le streaming transforme l'audit en surveillance : envoyer les événements vers un SIEM permet de détecter les anomalies en temps réel, pas lors de l'audit annuel.
  5. La conformité se mesure : un score de conformité par projet, calculé automatiquement, permet d'identifier les projets à risque avant l'auditeur.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

10 questions
5 min.
70% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn