GitLab CI/CD est puissant, mais les configurations par défaut peuvent exposer des secrets, accorder des permissions excessives ou laisser l'infrastructure accessible sur le réseau. Ce guide propose un chemin pratique et vérifiable pour durcir GitLab CI/CD sans casser la livraison quotidienne. Vous allez :
- Faire l'inventaire des versions, des runners et de la visibilité pour savoir exactement ce que vous sécurisez.
- Appliquer des changements de configuration sûrs par petits lots : contrôle d'accès, secrets, isolation des runners, hygiène des pipelines et restrictions réseau.
- Vérifier chaque changement avec des contrôles observables et des résultats attendus.
- Comprendre les modes de défaillance courants et comment récupérer rapidement.
- Adopter une liste de contrôle opérationnelle reproductible pour maintenir une posture saine.
Les exemples supposent que vous avez les droits Maintainer ou Owner sur un projet de test, et un accès administratif lorsque les changements l'exigent. Commencez avec un projet non critique, confirmez le comportement, puis déployez sur les dépôts critiques.
Inventaire des versions et de l'environnement
Le durcissement de la sécurité dépend de votre édition GitLab, de la version, des exécuteurs de runners et de la visibilité du projet. Capturez ces détails avant d'apporter des modifications. Utilisez les valeurs d'exemple ci-dessous uniquement comme placeholders pour votre propre inventaire.
| Élément | Valeur d'exemple (construite) | Pourquoi c'est important |
|---|---|---|
| Édition et version GitLab | GitLab EE 16.10 | Les fonctionnalités disponibles et l'emplacement des réglages varient selon la version et l'édition |
| Exécuteurs et versions des runners | 2 runners Shell partagés ; 1 runner Docker verrouillé au projet | L'isolation, le tagging et les options de config.toml dépendent de l'exécuteur et de la portée |
| Visibilité du projet | Privé | Contrôle qui peut voir les pipelines, les logs et les artefacts |
| Nom de la branche par défaut | main | Les protections de branche et les règles de revue reposent sur la branche par défaut |
| Branches/tags protégés définis | main protégé ; tags v* protégés | Détermine qui peut exécuter des jobs sensibles et publier des releases |
| Stockage des variables et secrets | Variables de groupe masquées et protégées | Définit comment les secrets sont scopés et hérités |
| Services externes utilisés | Registre de paquets interne ; hôte de staging en liste d'autorisation | La liste d'autorisation réseau doit inclure uniquement les endpoints requis |
| Administration des runners | Les Maintainers de projet gèrent le runner de projet ; l'Admin gère les runners partagés | Qui peut changer l'isolation et les tags affecte le placement des jobs |
Conseil : stockez cet inventaire dans un README de dépôt ou un runbook ops et mettez-le à jour après chaque changement.
Chemin de configuration sûr
Appliquez les changements par étapes scopées. Après chaque étape, exécutez les étapes de vérification de la section suivante.
1) Contrôle d'accès et protection des branches/tags
Objectif : garantir que seuls les utilisateurs de confiance et les refs protégés peuvent déclencher des jobs sensibles.
Actions :
- Mettez la visibilité du projet sur Privé sauf raison claire de ne pas le faire.
- Protégez la branche par défaut et les tags de release (par exemple, v*). Limitez les push/merge aux Maintainers ou à un rôle de release dédié.
- Exigez des revues de merge request et des vérifications de statut avant de merger vers les branches protégées.
Exemple : jobs uniquement sur branches/tags protégés dans .gitlab-ci.yml
stages: [test, build, deploy]
# S'exécute uniquement sur les branches/tags protégés
secure_job_template: &secure_job_rules
rules:
- if: '$CI_COMMIT_REF_PROTECTED == "true"'
when: on_success
- when: never
unit_tests:
stage: test
script:
- echo Exécution des tests
rules:
- if: '$CI_PIPELINE_SOURCE == "push"'
when: on_success
build_release:
stage: build
script:
- echo Construction de l'artefact de release
<<: *secure_job_rules
deploy_prod:
stage: deploy
script:
- echo Déploiement en production
environment:
name: production
when: manual
allow_failure: false
<<: *secure_job_rules
Résultat attendu : build_release et deploy_prod ne s'exécutent pas sur les branches non protégées ou depuis des forks.
2) Secrets et tokens
Objectif : éliminer les tokens personnels à longue durée de vie et garder les secrets hors des logs et artefacts.
Actions :
- Utilisez des variables CI de projet ou de groupe avec Masqué et Protégé activés pour toute valeur secrète.
- Préférez
CI_JOB_TOKENaux personal access tokens pour l'API GitLab et l'accès au registre/paquets interne. Scoppez l'accès à ce dont les jobs ont réellement besoin. - Utilisez des tokens d'accès de projet ou de groupe (rotatifs) lorsque l'accès non interactif est requis hors des jobs CI.
- Rotez tous les tokens régulièrement et lors des changements de personnel.
Exemple : appel de l'API GitLab avec CI_JOB_TOKEN
list_issues:
stage: test
script:
- curl --header "JOB-TOKEN: $CI_JOB_TOKEN" "$CI_API_V4_URL/projects/$CI_PROJECT_ID/issues?per_page=1"
rules:
- if: '$CI_COMMIT_REF_PROTECTED == "true"'
Exemple : référencement de variables masquées et protégées
deploy_prod:
stage: deploy
script:
- set +x # empêche le shell d'afficher les commandes
- ./deploy.sh --user "$DEPLOY_USER" --password "$DEPLOY_PASSWORD"
<<: *secure_job_rules
Conseils pratiques :
- N'affichez jamais les secrets ; préférez les fichiers ou stdin. Si vous devez créer un fichier, assurez-vous qu'il est supprimé avant la fin du job.
- Gardez
CI_DEBUG_TRACEdésactivé sauf débogage actif. Réactivez les protections après le débogage. - Stockez les secrets spécifiques à l'environnement (staging vs production) au niveau de portée approprié (groupe, sous-groupe ou projet) et marquez-les comme Protégés pour qu'ils ne soient disponibles que sur les refs protégés.
3) Isolation et tagging des runners GitLab
Objectif : garantir que seuls les jobs prévus peuvent atterrir sur des runners spécifiques, et que les runners ne peuvent pas être détournés.
Actions pour les runners partagés (config.toml) :
- Exigez des tags de job et désactivez les jobs sans tag.
- Limitez la concurrence pour prévenir l'épuisement des ressources.
- Évitez les modes d'exécution privilégiés sauf si strictement requis par le job.
Extrait d'exemple config.toml construit (runner partagé) :
concurrent = 4
check_interval = 0
runners
name = "shared-secure"
url = "https://gitlab.example.com"
token = "REDACTED"
executor = "shell"
[runners.system]
# défauts au niveau système
[runners.cache]
# réglages de cache selon les besoins
# Exige des tags sur les jobs et n'accepte pas les jobs sans tag
run_untagged = false
# Exemple de limitation des requêtes parallèles pour ce runner
request_concurrency = 1
Actions pour les runners spécifiques au projet :
- Verrouillez le runner au projet.
- Utilisez un tag dédié (par exemple,
secure) et référencez-le dans les jobs qui ont vraiment besoin de ce runner.
Exemple : job épinglé à un runner verrouillé au projet par tag
secure_build:
stage: build
tags: [secure]
script:
- echo Construction avec runner verrouillé au projet
<<: *secure_job_rules
Isolation additionnelle :
- Pour les exécuteurs Shell, faites tourner le runner sous un utilisateur OS dédié avec des permissions minimales.
- Pour tout exécuteur supportant l'élévation de privilèges, gardez-le désactivé sauf si un job spécifique l'exige, et confinez l'usage via des tags de job sur refs protégés uniquement.
4) Permissions des jobs et moindre privilège
Objectif : les jobs doivent opérer avec la portée et les capacités minimales.
Actions :
- Utilisez des rules avec
$CI_COMMIT_REF_PROTECTEDpour clôturer les jobs sensibles. - Utilisez
allow_failure: falsepour les étapes critiques etwhen: manualpour les actions impactant la production. - Utilisez
resource_grouppour sérialiser les actions exclusives sur le même environnement.
Exemple : sérialiser les déploiements production
deploy_prod:
stage: deploy
resource_group: production
when: manual
script:
- ./deploy-prod.sh
<<: *secure_job_rules
5) Hygiène des artefacts, caches et logs
Objectif : éviter les fuites de secrets et réduire le rayon d'action des données divulguées.
Actions :
- Ne stockez pas de secrets dans les artefacts ou caches. Si des artefacts sont requis, définissez de courts
expire_inet n'incluez que les fichiers nécessaires. - Évitez de collecter des dumps d'environnement complets ou des logs verbeux par défaut.
- N'écrivez jamais de secrets dans des fichiers dotenv ou rapports similaires.
Exemple : artefacts minimaux et courts
build_release:
stage: build
script:
- ./build.sh
artifacts:
paths:
- dist/
expire_in: '1 day'
<<: *secure_job_rules
6) Contrôles d'exposition réseau
Objectif : restreindre ce que votre instance GitLab et vos jobs peuvent atteindre.
Actions :
- Dans les réglages admin, activez une liste d'autorisation des requêtes sortantes pour les webhooks et intégrations ; listez uniquement les hôtes dont vous avez vraiment besoin.
- Gardez "autoriser les requêtes vers le réseau local" désactivé sauf besoin clair et revu.
- Assurez-vous que les projets n'exposent pas de pipelines publics quand ils contiennent des logs ou artefacts sensibles.
Actions au niveau projet :
- Désactivez les pipelines publics pour les projets privés.
- Utilisez des variables scopées à l'environnement et des refs protégées pour que les identifiants de production ne soient pas disponibles depuis des sources non fiables.
7) Référence rapide des valeurs par défaut sûres
Utilisez ce tableau pour croiser les réglages de base dans un projet nouveau ou existant.
| Contrôle | Réglage recommandé | Où régler |
|---|---|---|
| Visibilité du projet | Privé | Project Settings > General > Visibility |
| Pipelines publics | Désactivé | Project Settings > CI/CD > General pipelines |
| Branches/tags protégés | Protéger la branche par défaut et les tags de release | Project Settings > Repository > Protected branches/tags |
| Variables masquées | Activé pour tous les secrets | Project ou Group Settings > CI/CD > Variables |
| Variables protégées | Activé pour identifiants prod/staging | Project ou Group Settings > CI/CD > Variables |
| Tags de jobs runner | Requis ; pas de jobs sans tag | Runner config.toml et Project Settings > CI/CD > Runners |
| Portée du runner | Verrouiller runners de projet ; restreindre partagés | Project Settings > CI/CD > Runners |
| Traçage de débogage | Désactivé par défaut | Variable CI CI_DEBUG_TRACE=false |
| Rétention artefacts | Court terme (1 jour ou moins pour sensible) | .gitlab-ci.yml artifacts.expire_in |
Vérification et diagnostics
Exécutez ces vérifications immédiatement après chaque changement. Utilisez les commandes construites ci-dessous ; remplacez les placeholders par vos valeurs.
1. Branches protégées et jobs
- Créez une branche de fonctionnalité et poussez un commit qui déclencherait normalement
build_releaseoudeploy_prod. Attendu : les jobs ne s'exécutent pas. - Créez un tag qui ne correspond pas au motif protégé et poussez. Attendu : les jobs protégés ne s'exécutent pas.
- Mergez dans la branche par défaut protégée. Attendu : les jobs protégés s'exécutent selon les rules et les gates manuelles.
2. Secrets et logs
- Ajoutez
echo $DEPLOY_PASSWORDdans un job de test jetable sur une branche non protégée. Attendu : le job échoue car la variable est protégée et non disponible, ou affiche une valeur vide. Supprimez ce job de test immédiatement après validation. - Confirmez que les logs n'affichent pas les secrets. Attendu : les variables masquées apparaissent comme
[MASKED].
3. Portée du CI_JOB_TOKEN
- Exécutez un job qui appelle l'API GitLab en utilisant
CI_JOB_TOKENcomme montré plus haut. Attendu : accès uniquement au projet courant là où autorisé. Essayez d'accéder à un autre projet que vous ne devriez pas atteindre. Attendu : 403 Forbidden.
4. Isolation des runners
- Soumettez un job sans tags. Attendu (runner partagé) : le job reste en attente avec un message qu'aucun runner n'est disponible pour les jobs sans tag.
- Soumettez un job avec
tags: [secure]dans un projet avec un runner verrouillé. Attendu : le job atterrit sur ce runner verrouillé au projet.
5. Artefacts et rétention
- Confirmez que seuls les chemins prévus sont archivés. Attendu : pas de secrets ou fichiers de config dans l'artefact.
- Attendez après
expire_inou expirez manuellement les artefacts. Attendu : les artefacts deviennent inaccessibles.
6. Exposition réseau
- Déclenchez un webhook ou une intégration vers un hôte non autorisé. Attendu : la requête est bloquée.
- Vérifiez que les pipelines ne sont pas accessibles publiquement quand le projet est privé. Attendu : authentification requise pour voir les jobs et logs.
Astuces de diagnostic :
- Revoyez les logs de job pour les rules ignorées et les messages de mismatch de tags pour identifier les mauvaises configurations.
- Inspectez Project Settings > CI/CD > Runners pour confirmer quel runner a pris un job et pourquoi.
- Pour les contrôles réseau au niveau admin, testez avec une intégration minimale (par exemple, un endpoint HTTP simple) pour valider la liste d'autorisation.
Modes de défaillance et récupération
Problèmes courants et comment les corriger proprement :
1. Variables protégées non disponibles dans les jobs
- Symptôme : les jobs de déploiement échouent avec des secrets vides sur branches non protégées.
- Cause : les variables marquées Protégées ne sont disponibles que sur les refs protégés.
- Récupération : exécutez depuis un ref protégé ou déprotégez temporairement la variable pour tester dans un projet non critique. Ne déprotégez jamais les secrets de production dans les dépôts critiques.
2. Jobs bloqués en attente à cause des tags de runner
- Symptôme : les jobs restent en attente avec "no runners found".
- Cause :
run_untagged=falseou tags requis qui ne correspondent pas. - Récupération : ajoutez les bons tags aux jobs, ou autorisez temporairement les jobs sans tag sur un runner de test pendant que vous propagez les tags. Revenez aux réglages sécurisés après vérification.
3. CI_JOB_TOKEN ne peut pas accéder à la ressource requise
- Symptôme : erreurs 403 en appelant l'API GitLab ou le registre.
- Cause : token non autorisé pour la cible, ou l'endpoint requiert une portée différente.
- Récupération : basculez vers un token d'accès de projet/groupe avec la portée minimale requise, rotez le token après test.
4. Risque de fuite de secrets dans les logs
- Symptôme : valeurs sensibles apparaissent dans la sortie du job.
- Cause : affichage des secrets ou activation du traçage de débogage.
- Récupération : supprimez les instructions echo, désactivez le débogage, rotez le secret fuité, et recherchez dans les artefacts/logs pour toute autre exposition.
5. Restrictions réseau trop agressives
- Symptôme : webhooks et intégrations échouent silencieusement ou avec 403.
- Cause : hôte non dans la liste d'autorisation sortante.
- Récupération : ajoutez uniquement l'hôte requis à la liste d'autorisation, testez, et documentez pourquoi il est nécessaire.
6. Rétention d'artefacts trop courte
- Symptôme : les consommateurs ne peuvent pas télécharger les artefacts de build à temps.
- Cause :
expire_indéfini trop bas. - Récupération : augmentez
expire_inmodestement (par exemple, de 1 jour à 3 jours) et surveillez l'usage ; évitez de rendre les artefacts effectivement permanents.
Conseils de rollback :
- Gardez les changements
.gitlab-ci.ymldans des merge requests dédiées avec un plan de revert. Utilisez des commits de revert pour restaurer le pipeline précédent si un changement cause des pannes. - Sauvegardez les
config.tomldes runners avant modifications ; restaurez depuis la sauvegarde et redémarrez le runner si les jobs arrêtent de progresser. - Pour les réglages de projet, changez un contrôle à la fois, validez, et enregistrez la valeur précédente pour pouvoir faire un rollback rapide.
Liste de contrôle opérationnelle
Utilisez cette liste mensuellement ou lors de l'intégration d'un nouveau projet. Tous les items utilisent des exemples construits ; adaptez à votre environnement.
| Étape | Ce qu'il faut faire | Preuve de succès |
|---|---|---|
| 1 | Confirmer que le projet est Privé et les pipelines publics désactivés | Les visiteurs doivent s'authentifier pour voir pipelines et logs |
| 2 | Revoir les branches et tags protégés | Seuls les Maintainers (ou plus strict) peuvent push/merge sur refs protégés |
| 3 | Auditer les variables CI | Tous les secrets sont Masqués et Protégés ; les non-secrets sont clairement étiquetés |
| 4 | Revoir l'usage des tokens | CI_JOB_TOKEN utilisé pour API/registre interne ; pas de tokens personnels à longue durée dans les jobs |
| 5 | Isolation des runners | Runners partagés exigent des tags ; runners de projet verrouillés ; pas d'exécution privilégiée sauf justification |
| 6 | Rules de pipeline | Jobs sensibles clôturés par $CI_COMMIT_REF_PROTECTED et when: manual pour la prod |
| 7 | Hygiène des artefacts | Artefacts contiennent seulement les fichiers nécessaires ; expire_in court ; pas de secrets présents |
| 8 | Hygiène des logs | CI_DEBUG_TRACE désactivé ; pas d'echo de secrets ; rédaction vérifiée |
| 9 | Liste d'autorisation réseau | Seuls les hôtes requis sont listés ; accès localhost désactivé sauf justification |
| 10 | Rotation et revues | Tokens rotés ; revues d'accès complétées ; documentation mise à jour |
Conclusion
Durcissez GitLab CI/CD en avançant par petits pas vérifiables : verrouillez l'accès aux branches et tags protégés, stockez et scopez correctement les secrets, isolez les runners avec des tags et des verrous de projet, gardez les artefacts et logs exempts de données sensibles, et restreignez l'exposition réseau. Après chaque changement, exécutez les vérifications décrites ici pour confirmer que les contrôles voulus fonctionnent et ne bloquent pas le travail légitime.
Un pilote étroit dans un projet non critique vous aide à valider les contrôles et ajuster les défauts avant de généraliser à votre organisation. Une fois vérifié, codez vos décisions dans des templates et politiques de runners, adoptez la liste de contrôle mensuelle, et suivez les écarts dans le temps. Cela maintient votre posture CI/CD forte tout en minimisant la perturbation des équipes.