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 tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn