Aller au contenu

Chapitre 39. Infrastructure décrite en code et intégration continue

Objectifs pédagogiques

Gérer la configuration de Vault en code, et énoncer le problème de l'état ainsi que ses mitigations. Distinguer ce qui se décrit en code de ce qui ne le doit pas. Construire une chaîne d'intégration continue sans aucun secret statique. Configurer la méthode fédérée pour un fournisseur de CI et vérifier ses liaisons. Prévenir les fuites de secrets dans les journaux de pipeline.

Prérequis

Chapitre 10 (la méthode JWT et la démonstration fédérée), chapitre 13 (le rôle de CI déjà créé), chapitre 11 (les policies, qui deviennent ici du code), chapitre 16 (la signature d'artefacts).

Cours

39.1 Décrire Vault en code

La configuration de Vault se prête bien à la description en code : montages, rôles, policies et méthodes d'authentification sont des objets déclaratifs, et les gérer par un outil d'infrastructure apporte ce que le cursus réclame depuis le chapitre 11. La revue par pairs, l'historique, la reproductibilité entre environnements, et la comparaison entre l'état déclaré et l'état réel, devenue un signal de supervision au chapitre 32.

[Démonstration]

resource "vault_policy" "billing_kv" {
  name   = "billing-kv"
  policy = file("${path.module}/policies/billing-kv.hcl")
}

resource "vault_kubernetes_auth_backend_role" "billing" {
  backend                          = vault_auth_backend.kubernetes.path
  role_name                        = "billing-k8s"
  bound_service_account_names      = ["billing"]
  bound_service_account_namespaces = ["billing-prod"]
  audience                         = "vault"
  token_policies                   = ["billing-kv", "billing-db"]
  token_ttl                        = 3600
}

L'apport est réel, et il faut immédiatement en poser la limite.

39.2 Le problème de l'état

Un outil d'infrastructure conserve un état décrivant ce qu'il a créé, et cet état contient les attributs des ressources, y compris ceux qui sont des secrets. Créer par cet outil un identifiant secret, une clé d'API, un mot de passe ou une configuration de moteur avec ses identifiants, c'est écrire ces valeurs en clair dans le fichier d'état.

Le problème est structurel, connu de l'éditeur, et sans solution complète : le chiffrement du stockage distant protège au repos, les droits d'accès limitent la lecture, mais quiconque peut exécuter l'outil peut lire l'état, donc les secrets qu'il contient.

La règle en découle, et elle vaut pour toute installation sérieuse : le code décrit la structure, jamais les valeurs.

Géré en code Exclu du code
montages et leurs réglages identifiants secrets et credentials générés
policies valeurs de secrets statiques
méthodes d'authentification et rôles identifiants des comptes racines de moteurs
rôles des moteurs dynamiques tokens
création des clés Transit, jamais leur contenu

Les objets exclus se créent par des gestes tracés, souvent enveloppés, et l'état ne les voit pas.

Deux mitigations complètent la règle quand une valeur doit malgré tout transiter. Utiliser l'enveloppe de réponse pour que l'outil ne manipule qu'un jeton à usage unique et à courte durée, ce qui borne fortement la fuite. Et traiter le fichier d'état comme un actif du chapitre 29 : stockage chiffré, accès nominatif, journalisation, rétention décidée, parce qu'un état de plusieurs années accumule les secrets de plusieurs années.

Un mot sur les autres outils : les mécanismes de recherche de secrets d'Ansible lisent Vault au moment de l'exécution et n'écrivent pas d'état persistant, ce qui évite ce problème précis mais déplace la question ailleurs. La machine qui exécute le playbook détient les valeurs le temps de l'exécution, et les journaux verbeux peuvent les capturer.

39.3 La CI sans secret statique

Le chapitre 1 avait relevé une clé de paiement affichée dans un journal de pipeline, et le chapitre 10 avait annoncé que ce constat ne serait pas corrigé mais transformé, en supprimant le secret plutôt qu'en le protégeant. Voici l'exécution complète.

Le fournisseur d'intégration continue émet, pour chaque exécution, un jeton signé décrivant le contexte : le dépôt, la branche ou l'étiquette, le flux de travail, l'environnement. Vault vérifie la signature auprès de l'émetteur et applique des liaisons.

[Laboratoire]

vault auth enable -path=jwt-github jwt

vault write auth/jwt-github/config \
  oidc_discovery_url="https://token.actions.githubusercontent.com" \
  bound_issuer="https://token.actions.githubusercontent.com"

vault write auth/jwt-github/role/ci-billing \
  role_type="jwt" \
  user_claim="sub" \
  bound_audiences="https://github.com/astrea" \
  bound_subject="repo:astrea/billing:ref:refs/heads/main" \
  token_policies="ci-deploy" \
  token_ttl=15m token_max_ttl=30m

Trois contrôles font tout le travail. La liaison de sujet restreint l'accès à un dépôt et à une référence précise : sans elle, n'importe quel dépôt de l'organisation, voire selon la configuration n'importe quel dépôt public utilisant le même émetteur, obtiendrait un token. C'est l'erreur qui a causé des incidents réels dans l'écosystème, et elle mérite une vérification explicite en recette. La liaison d'audience empêche le rejeu d'un jeton émis pour un autre destinataire. Les durées sont taillées pour la vie d'un travail de pipeline : quinze minutes, pas quatre heures, parce qu'un token de CI qui survit à son exécution est un secret qui traîne.

Les autres plateformes suivent le même modèle avec leur vocabulaire, avec des revendications de projet, de branche et d'environnement à lier de la même façon. Les chaînes plus anciennes n'offrent pas toujours d'émetteur de jetons intégré ; il faut alors se rabattre sur une identité de machine, en assumant que le secret zéro réapparaît et en le traitant avec les outils du chapitre 13 : enveloppe, usage unique, courte durée, origine bornée.

39.4 Ce que le pipeline a le droit de faire

Un pipeline authentifié n'est pas un pipeline sans risque : il faut encore décider de ses droits. La policy réservée depuis le chapitre 11 obéit à trois principes.

Séparer par environnement. Le rôle lié à la branche principale obtient l'écriture en préproduction et rien en production ; le déploiement en production passe par un rôle distinct, lié à une étiquette de version signée. Le contrôle de branche devient ainsi un contrôle de sécurité, et la chaîne de livraison hérite de la séparation des environnements sans qu'aucun secret ne change de main.

Préférer l'écriture à la lecture. Un pipeline a rarement besoin de lire un secret de production : il a besoin de déployer une application qui, elle, lira ses secrets avec sa propre identité. Quand un pipeline demande la lecture de secrets de production, la bonne question est de savoir pourquoi l'application ne les lit pas elle-même.

Fournir des credentials éphémères pour ce que le pipeline doit vraiment faire : un accès de déploiement au cluster, un accès à un cloud par le moteur correspondant, avec des durées calées sur la durée du travail. Ils meurent à l'échéance, et le chapitre 18 rappelle que certains ne se révoquent pas, d'où des durées très courtes.

39.5 Empêcher les fuites de journaux

Un secret correctement obtenu peut encore fuir par l'endroit le plus banal : la sortie du pipeline. Quatre disciplines suffisent à couvrir l'essentiel.

Ne jamais afficher ce qui vient de Vault, ce qui suppose d'éviter les modes de trace verbeux sur les étapes sensibles, et de se souvenir que les mécanismes de masquage des plateformes ne connaissent que les valeurs qu'on leur a déclarées : une valeur obtenue dynamiquement n'est pas masquée par défaut.

Ne jamais passer un secret en argument de commande, visible dans la liste des processus et souvent dans les traces d'exécution ; les fichiers en mémoire et l'entrée standard sont les transports corrects.

Nettoyer les artefacts : un fichier temporaire contenant un credential ne doit pas se retrouver dans l'image produite ou dans les fichiers conservés.

Révoquer en fin de travail, enfin, ce qui coûte une ligne et transforme un token de quinze minutes en token de trente secondes.

Un contrôle rétrospectif complète ces disciplines : une recherche périodique de motifs de secrets dans les journaux conservés, exactement comme celle que le chapitre 1 faisait exécuter sur un dépôt. Trouver quelque chose est utile ; ne rien trouver après avoir mis les motifs à jour l'est aussi.

Exemple : la chaîne de billing, telle qu'elle est

L'état d'arrivée du fil rouge se décrit en une séquence, et elle ne contient aucun secret partagé.

Le pipeline s'authentifie par son jeton d'identité, obtient un token de quinze minutes. Il demande au moteur Kubernetes un accès de déploiement de dix minutes vers le namespace de préproduction. Il construit l'image, la signe par Transit avec la clé du chapitre 16, en obtenant le droit de signer et non la clé, la publie, puis déploie. Les machines de déploiement vérifient la signature avec la partie publique, qui ne demande aucun secret. Les pods démarrés s'authentifient ensuite par leur propre identité et lisent leurs propres secrets : le pipeline n'en a jamais vu un seul. Il révoque son token et s'arrête.

Le constat 4 du chapitre 1 est ainsi non pas corrigé mais dissous : il n'y a plus de clé dans le pipeline à protéger des journaux.

Sécurité : la chaîne de livraison comme cible

Le pipeline est un chemin vers la production. Qui contrôle le dépôt, le fichier de flux de travail ou l'exécuteur peut faire s'authentifier la chaîne avec ses propres instructions. Les liaisons de sujet et d'environnement sont donc des contrôles de sécurité, et la protection des branches en fait partie autant que les policies.

L'état d'infrastructure est un coffre secondaire. Il accumule ce que l'outil a manipulé, souvent depuis des années. Sa protection, sa rétention et sa revue relèvent du même régime que les journaux d'audit.

Les exécuteurs partagés sont un voisinage. Un exécuteur mutualisé exécute le code d'autrui sur la machine qui détient votre token ; les exécuteurs dédiés ou éphémères pour les pipelines à privilèges se justifient à ce titre seul.

Un token de CI qui survit à son travail est un secret abandonné. Durées courtes, révocation explicite en fin d'exécution, et vérification que les deux fonctionnent.

Erreurs fréquentes

Créer des secrets par l'outil d'infrastructure. Les valeurs se retrouvent en clair dans l'état, lisible de quiconque exécute l'outil (section 39.2).

Configurer la méthode fédérée sans liaison de sujet. Le rôle accepte alors des jetons d'autres dépôts, parfois de toute la plateforme (section 39.3).

Donner au pipeline la lecture des secrets de production. Le déploiement n'en a pas besoin ; l'application les lit avec sa propre identité (section 39.4).

Compter sur le masquage automatique des journaux. Il ne connaît que les valeurs déclarées à la plateforme, pas celles obtenues dynamiquement (section 39.5).

Passer un credential en argument de commande. Visible dans la liste des processus et dans les traces (section 39.5).

Oublier de révoquer le token en fin de travail. Une ligne transforme une compromission d'exécuteur en non-événement (section 39.5).

Traiter l'état d'infrastructure comme un fichier de travail. Sans chiffrement, sans droits stricts et sans rétention décidée, il devient le maillon faible de toute la description (section 39.2).

Mise en pratique : décrire, authentifier, contenir

Sur papier ; réponses commentées en fin de partie.

  1. Établissez la liste des objets Vault qui seront gérés en code et de ceux qui en sont exclus, avec la justification de chaque exclusion et le geste qui les crée à la place.
  2. Rédigez la configuration des deux rôles fédérés, préproduction sur la branche principale et production sur une étiquette signée, avec leurs policies respectives, et décrivez la recette qui prouve que la séparation tient.
  3. Un audit découvre un mot de passe de base de données dans les journaux d'un pipeline vieux de trois mois. Rédigez le plan de réponse et les quatre mesures de fond qui empêchent la répétition.

Questions de compréhension

  1. Qu'apporte la description en code, et quel signal de supervision en découle ?
  2. Énoncez le problème de l'état, sa nature structurelle, et la règle qui en découle.
  3. Quelles catégories d'objets sont exclues du code, et par quels gestes se créent-elles ?
  4. Comment l'enveloppe de réponse mitige-t-elle le passage d'une valeur par l'outil ?
  5. Décrivez les trois contrôles de la méthode fédérée pour une chaîne d'intégration continue.
  6. Pourquoi la liaison de sujet est-elle critique, et comment se vérifie-t-elle ?
  7. Énoncez les trois principes des droits d'un pipeline, et la question à poser quand il demande à lire un secret de production.
  8. Citez les quatre disciplines de prévention des fuites et le contrôle rétrospectif qui les complète.
  9. Déroulez la chaîne de billing et dites en quoi le constat 4 est dissous plutôt que corrigé.

Synthèse

Décrire Vault en code apporte revue, historique et comparaison entre le déclaré et le réel, à une condition qui commande tout : le code décrit la structure, jamais les valeurs, parce que l'état de l'outil conserve en clair ce qu'il manipule et devient un coffre secondaire. La chaîne d'intégration continue se libère du secret statique par l'identité fédérée : un jeton signé par le fournisseur, vérifié par Vault, borné par un sujet, une audience et une durée de quelques minutes. Les droits qui suivent obéissent à trois principes, dont celui qui évite le plus d'erreurs : un pipeline n'a presque jamais besoin de lire un secret de production. Restent les fuites banales, celles des journaux, que quatre disciplines couvrent et qu'un contrôle rétrospectif vérifie.