Aller au contenu

Comptes de service Git à révoquer réellement

Un compte de service Git donne à une automatisation le droit de lire, écrire, déclencher ou publier du code sans identité humaine interactive. Le compromis est brutal : on gagne une continuité d’exécution, on paie une identité persistante dont les privilèges survivent volontiers aux projets, aux équipes et aux tableurs censés la suivre.

→ Lire l’explication complète de la marmotte

Identités Git : préférer le jeton éphémère au compte permanent

Section intitulée « Identités Git : préférer le jeton éphémère au compte permanent »

La recommandation est nette : pour les pipelines et les déploiements, utiliser une identité de charge de travail fédérée, à durée de vie courte, plutôt qu’un compte Git dédié avec un mot de passe, une clé SSH (Secure Shell) ou un jeton statique. Un jeton obtenu par fédération est lié à une exécution, à un dépôt, à une branche ou à un environnement. Sa compromission a donc une fenêtre d’exploitation bornée. Un jeton statique dort dans une variable secrète jusqu’au jour où il se réveille dans un fork, un log ou un poste d’administration. Le folklore du « secret bien rangé » finit presque toujours en export CSV.

OpenID Connect (OIDC) est le mécanisme à privilégier lorsque la forge Git et la cible l’acceptent. Le pipeline émet une preuve d’identité signée, la cible vérifie l’émetteur, l’audience, le dépôt et la référence Git, puis délivre un jeton court. Il n’y a pas de secret longue durée à copier dans le dépôt ou dans l’outil d’intégration continue et de déploiement continu (CI/CD).

Cette approche ne fonctionne que si les revendications sont contrôlées côté cible. Autoriser toute identité émise par une forge, sans imposer le dépôt exact et la branche attendue, revient à accepter qu’un dépôt voisin demande les mêmes droits. L’audience doit désigner le service consommateur, jamais une valeur générique réutilisée entre environnements. Une audience permissive est une porte de garage avec un autocollant « authentification moderne ».

Un compte de bot permanent reste acceptable dans trois cas : une intégration ne supporte pas OIDC, un miroir Git doit s’authentifier en permanence, ou une opération humaine contrôlée nécessite une identité non personnelle. Dans ce cas, le compte doit être non interactif, sans adresse de récupération autonome, sans authentification multifacteur partagée et sans appartenance à des groupes larges. Le compte n’est pas un employé fictif avec une boîte mail générique, cette invention administrative produit surtout des mots de passe connus de trop de monde.

Les identités de service doivent être séparées par finalité : lecture de dépendances, écriture sur un dépôt de publication, création de versions, déclenchement de déploiement. Une seule identité capable de modifier le code, les secrets CI/CD et les règles de protection de branche transforme un vol de jeton en prise de contrôle complète de la chaîne logicielle.

Pour cadrer cette séparation, l’inventaire des identités doit rejoindre la gouvernance décrite dans la page Identités et les dépendances de build doivent être traitées comme des composants de sécurité, pas comme de la plomberie invisible dans la section Développement.

Jetons d’accès et clés SSH : surface d’attaque réelle

Section intitulée « Jetons d’accès et clés SSH : surface d’attaque réelle »

Un jeton d’accès personnel, ou PAT (Personal Access Token), hérite souvent des droits d’un humain. Le recycler dans un pipeline est une erreur de conception, même s’il porte un nom rassurant comme ci-bot-token. Sa révocation dépend du cycle de vie de l’employé, pas de celui de l’automatisation. Les droits sont en général trop larges et les journaux attribuent les actions à une personne qui n’a rien exécuté. L’audit devient un théâtre de marionnettes, avec des permissions administratives.

Les jetons de projet ou de groupe sont préférables aux PAT quand une forge impose un jeton statique. Leur portée doit être limitée à un dépôt, à une API (Application Programming Interface) précise et à une date d’expiration. Interdire les scopes d’administration, d’écriture globale et de gestion des variables secrètes sauf nécessité démontrée. Un jeton de lecture de paquets n’a aucune raison de pousser une branche protégée.

Les clés SSH posent un problème différent : elles authentifient une clé, pas l’intention qui l’utilise. Une clé de déploiement en lecture seule, attachée à un seul dépôt, est acceptable pour récupérer une source. Une clé rattachée à un compte bot ayant accès à tous les dépôts d’une organisation est un passe-partout. Elle contourne fréquemment les contrôles attendus sur les jetons, notamment les dates d’expiration et les autorisations fines.

Les attaques connues suivent des chemins banals :

  • exfiltration d’un jeton depuis les journaux CI/CD, une image de conteneur, un artefact ou un fichier de configuration ;
  • vol d’une clé privée depuis un agent de build partagé ;
  • injection de pipeline via une demande de fusion qui force l’exécution de code non fiable ;
  • abus d’un jeton disposant du droit de modifier les règles de protection ou les secrets ;
  • conservation d’un accès après l’abandon d’un dépôt ou le départ du propriétaire fonctionnel ;
  • confusion entre environnements, quand le même secret donne accès aux dépôts de développement et de production.

La contre-mesure ne consiste pas à chiffrer davantage le même secret. Le chiffrement protège le stockage, pas l’usage par un pipeline compromis. Il faut réduire la durée de vie, réduire la portée, empêcher l’exécution de secrets dans les contributions non fiables, puis journaliser chaque émission et chaque utilisation. Les secrets de production ne doivent être injectés que dans des exécuteurs dédiés, déclenchés par des références protégées et vérifiées.

Un compte Git ne garantit pas l’intégrité du code produit. Il authentifie une action auprès de la forge, rien de plus. Il ne prouve ni que le pipeline était sain, ni que les dépendances n’ont pas été substituées, ni que l’auteur d’une modification était légitime. La signature des artefacts, la protection des branches, les revues obligatoires et l’isolation des exécuteurs doivent venir compléter le dispositif.

L’exemple suivant crée un jeton de projet GitLab à lecture seule pour cloner un dépôt privé. Il suppose GitLab avec la fonction de jetons de projet activée, un compte opérateur ayant le rôle Maintainer sur le projet, curl et une variable d’environnement contenant un jeton d’administration du projet. Ce dernier reste un secret sensible, même si son nom contient ADMIN, ce qui n’a jamais constitué une mesure de sécurité reconnue.

Fenêtre de terminal
# Créer un jeton limité à la lecture du dépôt, expirant le 30 juin 2026.
# PROJECT_ID doit être l'identifiant numérique du projet GitLab.
# GITLAB_MAINTAINER_TOKEN ne doit jamais être affiché dans les logs CI/CD.
curl --fail --silent --show-error \
--request POST \
--header "PRIVATE-TOKEN: ${GITLAB_MAINTAINER_TOKEN}" \
--data "name=mirror-readonly" \
--data "expires_at=2026-06-30" \
--data "scopes[]=read_repository" \
"https://gitlab.example.net/api/v4/projects/${PROJECT_ID}/access_tokens"

La réponse contient la valeur du jeton une seule fois. La stocker dans un gestionnaire de secrets, avec un propriétaire fonctionnel, une date d’expiration et le système consommateur. Ne pas la transmettre dans un ticket, un canal de messagerie ou une variable de groupe réutilisée par quatorze pipelines. Une variable de groupe est souvent un coffre-fort collectif où la clé traîne sous le paillasson.

Tester ensuite le périmètre effectif : le jeton doit pouvoir cloner le dépôt cible, mais échouer sur un git push, sur un autre projet et sur les appels d’administration. Ce test de refus est plus instructif que le test de réussite. Les privilèges excessifs sont rarement visibles tant que personne ne les cherche.

Révocation, rotation et reprise des comptes de service Git

Section intitulée « Révocation, rotation et reprise des comptes de service Git »

La révocation doit être déclenchée par des événements techniques, pas seulement par une revue annuelle : suppression d’un dépôt, désactivation d’une intégration, remplacement d’un exécuteur, fuite présumée, changement de propriétaire fonctionnel ou élévation de privilèges. Un compte sans propriétaire fonctionnel explicite est à désactiver. L’absence de plainte après désactivation est une information d’architecture, pas une raison de remettre le compte en service par réflexe.

La rotation impose deux identifiants actifs pendant une période courte : créer le nouveau, mettre à jour le consommateur, vérifier les journaux, puis supprimer l’ancien. Écraser un secret à l’aveugle crée une panne ; conserver les deux indéfiniment crée une dette. La rotation doit donc être automatisée ou, au minimum, associée à une date de suppression planifiée et surveillée.

La sauvegarde des configurations Git doit inclure les métadonnées des comptes de service : identifiant, propriétaire, dépôts autorisés, scopes, date d’expiration, méthode d’authentification et dépendance consommatrice. La valeur secrète elle-même ne doit pas être récupérable depuis une sauvegarde Git ordinaire. Elle doit être recréée depuis le gestionnaire de secrets ou remplacée lors de la reprise. Restaurer des secrets expirés ou révoqués est une manière élégante de réintroduire un incident pendant un plan de reprise.

La supervision doit détecter les usages anormaux : authentification depuis un nouvel exécuteur, accès hors plage prévue, écriture par une identité lecture seule, création de jetons secondaires, échec répété d’authentification et appels API d’administration. Les événements doivent rejoindre la corrélation de sécurité avec l’identifiant du jeton, le projet cible, la référence Git et l’exécuteur. Sans cette granularité, l’investigation répondra seulement que « le bot a fait quelque chose », ce qui est à peu près aussi exploitable qu’une alarme indiquant qu’il y a du bruit.

Les clés privées de comptes de service exigent une protection matérielle ou un stockage chiffré dont l’accès est limité au runtime autorisé. Un HSM (Hardware Security Module) peut protéger une clé non exportable lorsque le protocole et l’agent SSH le permettent, mais il ne corrige pas une autorisation excessive. La protection cryptographique doit être articulée avec les mécanismes PKI et TLS et non utilisée comme un vernis sur une identité sans propriétaire.

Sans identités de service bornées ni procédure de révocation, la porte reste ouverte après le départ du besoin. Le premier coût est la perte de proportionnalité : faute de savoir quel jeton sert à quoi, une suspicion de fuite impose une rotation globale, donc un arrêt simultané de tous les pipelines qui en dépendent, y compris ceux qui n’étaient pas concernés. Le deuxième est la datation impossible : sans date d’émission, propriétaire fonctionnel ni horodatage d’usage, l’analyse ne peut pas borner la période de compromission, et la remédiation se limite à des suppositions. Le troisième touche la reprise : un accès non inventorié n’est pas recréé à l’identique après restauration, et les échecs d’authentification apparaissent au pire moment, pendant le redémarrage des chaînes de publication.

Un compte de service Git est une identité utilisée par un programme pour accéder à du code ou lancer une action automatique. Il peut lire un dépôt, modifier des fichiers, publier une version ou démarrer un déploiement. Pour prouver son identité, le programme utilise souvent un secret, par exemple une longue chaîne de caractères ou une clé privée.

Le risque apparaît quand ce secret dure trop longtemps. S’il est copié dans un journal, dans une configuration ou sur une machine compromise, une personne peut l’utiliser avec les mêmes droits que le programme. Si le compte a le droit de modifier le code et les réglages de sécurité, le vol devient beaucoup plus grave.

La meilleure méthode consiste à éviter les secrets permanents. Lorsqu’un pipeline démarre, il présente une preuve liée au dépôt et à la branche qui l’a déclenché. Le service cible vérifie cette preuve puis donne un droit d’accès valable peu de temps. Si le pipeline s’arrête ou si le droit expire, le jeton ne fonctionne plus.

Quand un secret permanent reste nécessaire, il faut limiter ses droits à une tâche précise, définir une date d’expiration, noter son responsable et enregistrer ses usages. Lorsqu’un projet disparaît, qu’une fuite est suspectée ou qu’une automatisation change, l’ancien accès doit être supprimé. Si la suppression casse un programme, il faut corriger le programme, pas garder indéfiniment un accès oublié.

À lire aussi

Pages voisines