Aller au contenu

OIDC pour sécuriser les identifiants CI/CD

Les pipelines d’intégration continue et de déploiement continu (CI/CD) concentrent des privilèges capables de publier du code, modifier une infrastructure ou lire des données de production. Une clé API (Application Programming Interface) statique dans ce chemin transforme chaque build en cible d’exfiltration, avec une régularité qui ferait presque passer le problème pour une fonctionnalité.

→ Lire l’explication complète de la marmotte

Clés API statiques : le privilège qui survit à tout

Section intitulée « Clés API statiques : le privilège qui survit à tout »

Une clé API ou un jeton de déploiement est une identité machine présentée directement à un fournisseur cloud, un registre de conteneurs, un gestionnaire de paquets ou une interface d’administration. Le serveur vérifie la valeur, puis applique les autorisations attachées. Cette mécanique fonctionne, jusqu’au jour où elle fonctionne pour un attaquant.

Le défaut structurel est la persistance. Une clé créée pour un pipeline reste valable entre deux exécutions, après la suppression du projet, parfois après le départ du compte de service qui l’a engendrée. Une fuite n’est donc pas une fenêtre de quelques minutes : c’est un accès durable jusqu’à révocation. La rotation annuelle, rituel apprécié des tableurs de conformité, réduit peu cette durée d’exposition.

Le second défaut est le périmètre. Un jeton qui déploie une image dans un environnement de test ne doit pas pouvoir créer des administrateurs, lister les secrets de production ou lire tous les objets d’un stockage cloud. Accorder AdministratorAccess, Owner ou un rôle équivalent pour éviter une erreur d’autorisation est une dette technique avec détonateur. Le pipeline n’a pas besoin d’un compte cloud, il a besoin d’une action précise sur une ressource précise.

Cette identité doit être distincte par dépôt, environnement et finalité. Un jeton de publication de paquet ne déploie pas une infrastructure. Un jeton de test n’accède pas à la production. Un job de migration de base de données n’hérite pas des droits de livraison d’images. Mutualiser un secret entre tous les pipelines facilite le démarrage et rend l’inventaire d’incident aussi agréable qu’une fouille archéologique dans un dossier secrets-final-v4.

Les principes de gestion des identités s’appliquent aux machines sans exception : propriétaire identifié, finalité documentée, droits minimaux, durée limitée et révocation vérifiable. L’absence d’humain devant l’écran ne rend pas l’identité moins dangereuse.

Un runner est l’agent qui exécute les jobs du pipeline. Dès qu’un secret atteint son processus, il faut considérer que tout code exécuté par le job peut tenter de le lire. Masquer une valeur dans les logs évite une divulgation accidentelle, pas une exfiltration : printenv, un appel réseau, un artefact compressé ou une erreur volontaire suffisent. Le masquage remplace des chaînes affichées, il ne crée pas une frontière de sécurité. Un astérisque n’est pas un contrôle d’accès, malgré son excellente carrière dans les démonstrations commerciales.

Les déclenchements depuis une demande de fusion issue d’un fork sont le cas classique. Le code provient alors d’un dépôt contrôlé par un tiers. Exécuter ce code avec les secrets du dépôt cible revient à confier les clés du local technique à une personne venue signaler une ampoule grillée. Les jobs de validation sur fork ne reçoivent aucun secret, aucun jeton inscriptible et aucun accès réseau privilégié. Ils utilisent des dépendances verrouillées, des permissions en lecture minimale et des runners isolés.

Une variante moins visible concerne les événements indirects : commentaire, étiquette, approbation ou événement de fusion. Certains systèmes exécutent alors la définition de pipeline de la branche par défaut tout en récupérant du code contrôlé par la contribution. La frontière doit porter sur les deux éléments : qui contrôle le fichier de pipeline et qui contrôle le code exécuté. Autoriser un événement administratif ne suffit pas si le job lance ensuite un script fourni par le fork.

L’isolation entre jobs mérite la même sévérité. Un runner persistant peut conserver un répertoire de travail, un cache de dépendances, des couches de conteneur ou des fichiers temporaires. Un job non fiable ne partage ni espace de travail ni cache inscriptible avec un job possédant des privilèges. Préférer des exécuteurs éphémères, détruits après usage, et des caches signés ou séparés par niveau de confiance. Un cache de build est du code et des données récupérables par le prochain job, pas une boîte magique marquée « performance ».

Les protections minimales sont les suivantes :

  • interdire les secrets sur les pipelines issus de forks et sur les contributions non approuvées ;
  • définir des permissions par job, pas seulement au niveau du dépôt ;
  • réserver les runners de déploiement à des branches, environnements et règles d’approbation explicites ;
  • interdire l’affichage des environnements, fichiers de configuration et réponses HTTP contenant des en-têtes d’autorisation ;
  • analyser les journaux, artefacts et caches pour détecter les secrets, puis considérer toute détection comme une révocation, pas comme un ticket cosmétique.

Fédération OIDC : remplacer le secret par une preuve courte

Section intitulée « Fédération OIDC : remplacer le secret par une preuve courte »

OpenID Connect (OIDC) est une couche d’identité construite sur OAuth 2.0, un protocole de délégation d’autorisation. Dans un pipeline, la plateforme CI/CD émet un jeton signé à durée courte. Le fournisseur cloud vérifie la signature, l’émetteur, l’audience et les attributs du jeton, puis délivre des identifiants temporaires associés à un rôle. Aucun secret cloud longue durée n’est stocké dans les variables du dépôt.

Le compromis est net. OIDC retire le secret persistant de la plateforme CI/CD et réduit fortement la valeur d’une fuite de configuration. En échange, il impose une politique de confiance exacte et une intégration par fournisseur. Cette complexité est légitime : une autorisation de production mérite mieux qu’une case « accès global ». La recommandation est donc ferme : utiliser OIDC pour toute cible cloud ou service compatible, et n’accepter une clé statique que lorsqu’aucune fédération n’est disponible ou qu’une contrainte technique démontrable l’empêche.

La confiance ne doit jamais se limiter à l’émetteur. Un jeton est généralement un JSON Web Token (JWT), format signé contenant des déclarations. La politique cloud doit vérifier au minimum :

  • iss, l’émetteur OIDC exact de la plateforme ;
  • aud, l’audience attendue par le fournisseur cloud ;
  • sub, le sujet, borné à l’organisation, au dépôt et idéalement à la branche ou à l’environnement ;
  • les déclarations spécifiques à la plateforme, telles que référence Git, environnement protégé ou type d’événement ;
  • la durée maximale de session, inférieure ou égale à la durée réelle du déploiement.

Autoriser tous les sujets d’un émetteur revient à faire confiance à chaque dépôt de la plateforme. Autoriser tout un dépôt sans borner la branche permet à une branche de test de réclamer le rôle de production. Les permissions cloud ne corrigent pas une relation de confiance trop large : elles n’en voient que le rôle obtenu après coup.

OIDC ne protège pas un runner compromis pendant l’exécution. Un attaquant capable de lancer des commandes dans un job autorisé peut demander un jeton et utiliser les identifiants temporaires avant expiration. La fédération réduit la persistance, pas la nécessité d’isoler le code, de protéger les branches et de limiter le rôle. Le reste vient de l’architecture des environnements, du contrôle des changements et de la supervision, sujets qui dépassent une variable de pipeline, aussi élégante soit-elle.

Le schéma suit un déploiement GitHub Actions vers Amazon Web Services (AWS). Le rôle AWS doit déjà faire confiance à l’émetteur OIDC GitHub, avec une condition sur le sujet du dépôt et de la branche main. Le rôle ne reçoit que les actions nécessaires au déploiement visé. Côté pipeline, seul le job de déploiement a le droit de demander un jeton OIDC, et il s’exécute dans un environnement protégé par les règles de la plateforme.

Échange OIDC entre un pipeline GitHub Actions et AWS, en six temps, entre trois acteurs : le pipeline, l'émetteur OIDC de GitHub et le service de jetons temporaires d'AWS (STS). Un push sur la branche main lance le job de déploiement dans l'environnement protégé. 1 : le job demande un jeton. 2 : l'émetteur renvoie au job un jeton signé et court, qui porte l'émetteur GitHub, l'audience AWS STS et le sujet, c'est-à-dire le dépôt et la branche main. 3 : le job présente le jeton à AWS et demande le rôle deploy-production. 4 : AWS vérifie que l'émetteur est exact, que l'audience est STS et que le sujet correspond à ce dépôt et à la branche main ; un seul non et la demande est refusée. 5 : AWS renvoie des identifiants temporaires, valables le temps du déploiement. 6 : le job déploie le seul service api, puis tout expire. Aucune clé AWS n'est stockée dans la plateforme CI/CD : tout repose sur la précision du sujet vérifié à l'étape 4.

La relation de confiance AWS doit restreindre token.actions.githubusercontent.com:sub à repo:organisation/depot:ref:refs/heads/main ou, mieux, à un environnement protégé lorsque le modèle de livraison le permet. Restreindre aussi l’audience à sts.amazonaws.com, le service de jetons temporaires AWS. Sans ces conditions, le pipeline ne fait que remplacer une clé permanente par un distributeur de clés temporaires ouvert à tous.

Épingler les actions externes sur un identifiant immuable de révision est préférable à une étiquette de version. Une étiquette peut être déplacée. Un pipeline de déploiement qui récupère du code tiers mutable au moment de déployer a choisi le suspense comme contrôle de sécurité.

Exploitation, révocation et reprise des identités de pipeline

Section intitulée « Exploitation, révocation et reprise des identités de pipeline »

Une identité fédérée demande une supervision différente d’une clé statique. Journaliser chaque hypothèse de rôle, avec sujet OIDC, dépôt, référence Git, environnement, rôle demandé, actions cloud et résultat. Les journaux de la plateforme CI/CD et ceux du fournisseur cloud doivent porter un identifiant de corrélation commun, par exemple l’identifiant d’exécution du pipeline. Sans cela, reconstituer quel commit a supprimé quelle ressource devient une activité contemplative.

Définir des alertes sur l’hypothèse de rôle depuis une branche inattendue, hors fenêtre de déploiement ou avec un sujet absent de l’inventaire. Détecter aussi les tentatives répétées refusées : elles révèlent souvent une politique trop large en cours de sondage, ou une configuration devenue incohérente.

La révocation OIDC consiste à modifier ou supprimer la relation de confiance, désactiver l’environnement de déploiement, suspendre le runner concerné et invalider les sessions cloud lorsque le fournisseur le permet. Révoquer la confiance n’annule pas toujours les identifiants temporaires déjà émis. La durée de session doit donc rester courte, typiquement inférieure à une heure et idéalement limitée au temps du job. Une session de douze heures pour un déploiement de cinq minutes est un secret statique qui a fait du yoga.

Conserver une procédure de reprise hors pipeline, protégée par une identité humaine à authentification forte et soumise à approbation. Cette procédure ne doit pas réintroduire une clé d’administration dans le dépôt « pour dépanner ». Les accès d’urgence sont séparés, journalisés et testés. Sauvegarder les politiques de rôle, les relations de confiance et les règles d’environnement dans l’infrastructure comme code, puis tester leur restauration dans un compte ou projet isolé. Les fondations d’architecture cyber commencent souvent là où les secrets cessent d’être des chaînes copiées dans une console.

Sans fédération à durée courte, chaque appel au fournisseur cloud est attribué à une même identité statique : les journaux montrent la clé, jamais le dépôt, la branche, l’événement ni l’exécution qui l’a employée. L’enquête perd donc son point de départ, et la corrélation entre une ressource supprimée et un commit précis devient une reconstitution manuelle. La révocation cesse d’être un geste isolé : une clé partagée entre plusieurs pipelines interrompt tous les déploiements qui en dépendent, ce qui encourage à repousser la rotation. Les identifiants survivent aux dépôts archivés et aux runners désactivés, si bien que l’inventaire des accès valides diverge silencieusement de la réalité. Enfin, aucune borne technique n’empêche une branche quelconque d’utiliser le même secret que la production, faute de condition vérifiable sur le sujet présenté.

Un outil de livraison automatique exécute des tâches quand le code change : vérifier le programme, fabriquer un paquet, puis le publier ou modifier une application en ligne. Pour agir sur un service externe, cet outil doit prouver son identité. Avec une clé permanente, la preuve est une valeur secrète enregistrée dans la configuration du projet. Si le programme lancé par l’outil lit cette valeur, si elle apparaît dans un journal ou si un fichier temporaire la conserve, une personne malveillante peut l’utiliser tant qu’elle n’est pas annulée.

Le danger augmente quand cette clé donne accès à tout le compte informatique au lieu d’une seule action. Un changement de code proposé par une personne extérieure peut aussi contenir des instructions visant à récupérer les secrets. Il faut donc empêcher ces changements non approuvés de recevoir des secrets, séparer les machines qui exécutent les tâches, et limiter les droits de chaque tâche.

Avec OpenID Connect, le service de livraison demande une preuve signée, valable peu de temps, pour une tâche précise. Le fournisseur informatique vérifie qui a émis cette preuve, de quel projet elle vient, quelle branche de code l’a déclenchée et pour quel service elle est demandée. S’il accepte, il donne un accès temporaire limité. Si une vérification échoue, l’accès est refusé. Cette méthode ne protège pas une tâche déjà piratée pendant son exécution : il faut encore surveiller les actions, limiter les droits et arrêter rapidement les accès suspects.

À lire aussi

Pages voisines

: les deux pages se citent l’une l’autre.