Un pipeline GitLab CI qui déploie en production manipule forcément des secrets : tokens d’API, clés SSH, credentials de registre Docker, mots de passe de base de données. GitLab propose quatre mécanismes pour protéger ces valeurs, mais aucun n’est activé par défaut. Ce guide vous montre comment configurer chaque couche de protection, éviter les pièges classiques et intégrer un gestionnaire de secrets externe quand les variables natives ne suffisent plus.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Distinguer les 4 types de variables CI/CD (standard, protégée, masquée, fichier) et leurs limites
- Configurer des secrets protégés + masqués pour empêcher l’exposition dans les logs et les branches non protégées
- Utiliser les variables de type fichier pour les clés SSH et certificats
- Intégrer HashiCorp Vault pour les secrets dynamiques à durée de vie limitée
- Détecter les fuites de secrets avec GitLab Secret Detection et gitleaks
- Mettre en place une stratégie de rotation sans interruption de pipeline
Dans quel contexte ?
Section intitulée « Dans quel contexte ? »Vous administrez une instance GitLab CE sur KVM avec un ou plusieurs runners. Vos pipelines font du build Docker, du déploiement Ansible ou du provisionning Terraform — autant de cas où des credentials transitent dans les jobs. Vous voulez garantir que :
- un développeur ne peut pas lire un secret de production dans une branche de feature ;
- un secret ne s’affiche jamais en clair dans les logs du job ;
- un token volé a une durée de vie limitée ;
- une fuite est détectée automatiquement avant le merge.
Les 4 niveaux de protection des variables GitLab CI
Section intitulée « Les 4 niveaux de protection des variables GitLab CI »Variables standard (non protégées)
Section intitulée « Variables standard (non protégées) »Par défaut, une variable CI/CD est injectée dans tous les jobs, sur toutes les branches, et apparaît en clair dans les logs si un echo ou un script la référence.
deploy: script: - echo "Deploying with token $DEPLOY_TOKEN" # Le token apparaît EN CLAIR dans les logs du jobC’est le comportement le plus dangereux, et c’est celui par défaut.
Variables protégées (Protected)
Section intitulée « Variables protégées (Protected) »Une variable marquée Protected n’est injectée que dans les jobs exécutés sur des branches protégées ou des tags protégés.
curl --header "PRIVATE-TOKEN: $GITLAB_API_TOKEN" \ "https://gitlab.example.com/api/v4/projects/$PROJECT_ID/variables" \ | jq '.[] | select(.protected == true) | {key, protected, masked}'{ "key": "DEPLOY_TOKEN", "protected": true, "masked": true}Variables masquées (Masked)
Section intitulée « Variables masquées (Masked) »Une variable marquée Masked est remplacée par [MASKED] dans les logs du job. GitLab impose des contraintes sur la valeur :
- Au moins 8 caractères
- Pas de retour à la ligne
- Composée uniquement de caractères Base64 ou URL-safe
Les clés SSH multi-lignes et les certificats PEM ne peuvent pas être masqués — utilisez une variable de type fichier pour ces cas.
Variables de type fichier (File)
Section intitulée « Variables de type fichier (File) »Au lieu d’injecter la valeur dans l’environnement, GitLab écrit le contenu dans un fichier temporaire et expose le chemin via la variable.
deploy: script: - chmod 600 "$SSH_PRIVATE_KEY" - ssh -i "$SSH_PRIVATE_KEY" deploy@prod.example.com "systemctl restart app"La variable $SSH_PRIVATE_KEY contient le chemin /tmp/gitlab_ci_var_xxx, pas la clé elle-même. La clé n’apparaît jamais dans les logs même sans masquage.
Configurer vos secrets correctement
Section intitulée « Configurer vos secrets correctement »-
Marquer chaque secret comme Protected + Masked
Dans Settings > CI/CD > Variables :
- Cocher Protected : le secret n’est injecté que sur les branches/tags protégés
- Cocher Masked : la valeur est remplacée par
[MASKED]dans les logs - Choisir le scope : projet, groupe ou instance
Créer une variable protégée + masquée via l'API curl --request POST \--header "PRIVATE-TOKEN: $GITLAB_API_TOKEN" \--form "key=DEPLOY_TOKEN" \--form "value=glpat-xxxxxxxxxxxxxxxxxxxx" \--form "protected=true" \--form "masked=true" \"https://gitlab.example.com/api/v4/projects/$PROJECT_ID/variables" -
Utiliser des variables fichier pour les clés et certificats
Dans Settings > CI/CD > Variables, sélectionner Type: File pour :
- Clés SSH (
id_rsa,id_ed25519) - Certificats TLS (
.pem,.crt) - Fichiers de configuration sensibles (
kubeconfig,.env)
- Clés SSH (
-
Restreindre les environnements
Pour chaque variable, préciser l’environnement cible :
production: uniquement les jobs déployant en prodstaging: uniquement les jobs déployant en préproduction*: tous les environnements (à éviter pour les secrets critiques)
-
Vérifier l’absence de fuites dans les logs
Rechercher des tokens exposés dans les logs d'un pipeline curl --header "PRIVATE-TOKEN: $GITLAB_API_TOKEN" \"https://gitlab.example.com/api/v4/projects/$PROJECT_ID/jobs/$JOB_ID/trace" \| grep -iE 'glpat-|ghp_|AKIA|sk-|token|password|secret'Si la commande retourne des résultats, vous avez une fuite. Révoquez le token immédiatement.
Intégrer HashiCorp Vault pour les secrets dynamiques
Section intitulée « Intégrer HashiCorp Vault pour les secrets dynamiques »Quand les variables CI/CD natives ne suffisent plus — parce que vous avez besoin de secrets à durée de vie limitée, de rotation automatique ou de traçabilité fine — Vault est l’intégration recommandée.
Activer l’intégration Vault dans GitLab
Section intitulée « Activer l’intégration Vault dans GitLab »GitLab CI supporte nativement Vault via le mot-clé secrets: (GitLab 13.4+). Le runner demande un token éphémère à Vault via l’authentification JWT.
deploy: secrets: DATABASE_PASSWORD: vault: engine: name: kv-v2 path: secret path: production/db field: password script: - echo "Le mot de passe est injecté dans $DATABASE_PASSWORD" - ansible-playbook deploy.ymlConfigurer l’authentification JWT dans Vault
Section intitulée « Configurer l’authentification JWT dans Vault »-
Activer l’auth JWT dans Vault
Configuration Vault vault auth enable jwtvault write auth/jwt/config \jwks_url="https://gitlab.example.com/-/jwks" \bound_issuer="gitlab.example.com" -
Créer une policy pour le pipeline
policy-deploy.hcl path "secret/data/production/*" {capabilities = ["read"]}Fenêtre de terminal vault policy write deploy policy-deploy.hcl -
Créer un rôle lié au projet GitLab
Rôle Vault lié au projet vault write auth/jwt/role/deploy \role_type="jwt" \policies="deploy" \token_explicit_max_ttl=300 \bound_claims='{"project_id": "42", "ref_protected": "true"}' \user_claim="user_email"Le
bound_claimsrestreint l’accès aux pipelines du projet 42, sur branches protégées uniquement. Le TTL de 5 minutes limite la fenêtre d’exploitation en cas de vol.
Détecter les fuites de secrets
Section intitulée « Détecter les fuites de secrets »GitLab Secret Detection (SAST)
Section intitulée « GitLab Secret Detection (SAST) »GitLab intègre un scanner de secrets dans les pipelines CI/CD. Il détecte les tokens, clés API et mots de passe committé dans le code.
include: - template: Jobs/Secret-Detection.gitlab-ci.ymlLe rapport apparaît dans Security & Compliance > Vulnerability Report (GitLab Ultimate) ou dans les artefacts du job (GitLab CE).
gitleaks en alternative open source
Section intitulée « gitleaks en alternative open source »Pour les instances GitLab CE sans la licence Ultimate :
secret-scan: stage: test image: zricethezav/gitleaks:latest script: - gitleaks detect --source . --report-format sarif --report-path gitleaks.sarif artifacts: reports: sast: gitleaks.sarif allow_failure: falsegitleaks detect --source . --verbose○ INFO no leaks foundRotation des secrets sans interruption
Section intitulée « Rotation des secrets sans interruption »La rotation d’un secret CI/CD sans couper les pipelines en cours suit ce processus :
-
Créer la nouvelle valeur sous un nom temporaire
Dans Settings > CI/CD > Variables, créer
DEPLOY_TOKEN_V2avec la nouvelle valeur (Protected + Masked). -
Mettre à jour les pipelines pour accepter les deux
.gitlab-ci.yml — Double source de token deploy:script:- TOKEN="${DEPLOY_TOKEN_V2:-$DEPLOY_TOKEN}"- curl -H "Authorization: Bearer $TOKEN" https://api.example.com/deploy -
Vérifier que les nouveaux pipelines utilisent la v2
Lancez un pipeline sur la branche protégée et vérifiez dans les logs que la connexion fonctionne.
-
Supprimer l’ancien secret et renommer
Supprimez
DEPLOY_TOKEN, puis recréezDEPLOY_TOKENavec la valeur deDEPLOY_TOKEN_V2. SupprimezDEPLOY_TOKEN_V2. -
Révoquer l’ancien token côté fournisseur
Selon le service : GitLab (révoquer le PAT), AWS (désactiver l’access key), Docker Hub (révoquer le token).
Dépannage
Section intitulée « Dépannage »| Symptôme | Cause probable | Solution |
|---|---|---|
| Secret visible en clair dans les logs | Variable non marquée Masked | Cocher Masked dans Settings > CI/CD > Variables |
| Variable absente sur branche de feature | Variable marquée Protected, branche non protégée | Normal — c’est le comportement attendu. Vérifier la liste des branches protégées |
Masked variable cannot be set (erreur) | Valeur < 8 caractères ou contient des retours à la ligne | Utiliser une variable de type fichier pour les clés multi-lignes |
Job Vault échoue avec permission denied | bound_claims ne correspond pas au projet ou à la branche | Vérifier project_id et ref_protected dans le rôle Vault |
| Secret Detection ne détecte rien | Template non inclus ou image non disponible | Vérifier l’include Jobs/Secret-Detection.gitlab-ci.yml et l’accès au registre |
| Token rotationné, anciens pipelines échouent | L’ancien token a été supprimé avant la fin des pipelines | Utiliser la stratégie double-token décrite ci-dessus |
À retenir
Section intitulée « À retenir »- Les variables CI/CD GitLab ne sont ni protégées ni masquées par défaut — la configuration initiale est la plus dangereuse
- Combinez toujours Protected + Masked pour les secrets réels, et utilisez des variables fichier pour les clés SSH et certificats
- Les variables Protected ne sont injectées que sur les branches et tags protégés — c’est la première ligne de défense
- Le mot-clé
secrets:avec Vault via JWT fournit des tokens éphémères à TTL court, limitant la fenêtre d’exploitation - gitleaks en CI/CD détecte les secrets committés avant le merge — même sur GitLab CE sans licence Ultimate
- La rotation sécurisée passe par un schéma double-token : créer la nouvelle valeur, migrer les pipelines, supprimer l’ancienne
- GitLab CI/CD Variables — Documentation officielle
- Using external secrets in CI — GitLab Docs
- Vault JWT/OIDC Auth Method — HashiCorp
- GitLab Secret Detection — GitLab Docs
- gitleaks — Open source secret scanner
- OWASP Top 10 CI/CD — CICD-SEC-02: Inadequate Identity and Access Management — OWASP
Testez vos connaissances
Section intitulée « Testez vos connaissances »Contrôle de connaissances
Validez vos connaissances avec ce quiz interactif
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
Vérification
(0/0)Profil de compétences
Quoi faire maintenant
Ressources pour progresser
Des indices pour retenter votre chance ?
Nouveau quiz complet avec des questions aléatoires
Retravailler uniquement les questions ratées
Retour à la liste des certifications