{"id":482,"date":"2026-02-05T21:30:47","date_gmt":"2026-02-05T20:30:47","guid":{"rendered":"https:\/\/secure-pipelines.com\/?p=482"},"modified":"2026-03-24T12:54:57","modified_gmt":"2026-03-24T11:54:57","slug":"secrets-management-ci-cd-pipelines-patterns-vault-2","status":"publish","type":"post","link":"https:\/\/secure-pipelines.com\/fr\/ci-cd-security\/secrets-management-ci-cd-pipelines-patterns-vault-2\/","title":{"rendered":"Gestion des Secrets dans les Pipelines CI\/CD : Patterns, Anti-Patterns et Int\u00e9gration Vault"},"content":{"rendered":"<h2>Introduction : Pourquoi les Secrets Sont la Cause N\u00b01 de Compromission CI\/CD<\/h2>\n<p>Si vous examinez la cause profonde de presque chaque br\u00e8che majeure CI\/CD de ces derni\u00e8res ann\u00e9es \u2014 de l\u2019attaque de la cha\u00eene d\u2019approvisionnement Codecov \u00e0 l\u2019incident de s\u00e9curit\u00e9 CircleCI \u2014 vous trouverez le m\u00eame coupable : des secrets compromis. Cl\u00e9s API, identifiants cloud, mots de passe de bases de donn\u00e9es, certificats de signature \u2014 ce sont les passe-partout que les attaquants recherchent, et les pipelines CI\/CD sont l\u2019endroit o\u00f9 ils concentrent leurs efforts.<\/p>\n<p>La raison est structurelle. Les pipelines occupent une position singuli\u00e8rement dangereuse : ils <strong>doivent<\/strong> avoir acc\u00e8s aux identifiants de production pour d\u00e9ployer les logiciels, tout en \u00e9tant par nature \u00e9ph\u00e9m\u00e8res, multi-tenants et expos\u00e9s \u00e0 du code non fiable. Chaque pull request, chaque mise \u00e0 jour de d\u00e9pendance, chaque push d\u2019un contributeur d\u00e9clenche l\u2019ex\u00e9cution du pipeline \u2014 et chaque ex\u00e9cution est un vecteur potentiel d\u2019exfiltration de secrets.<\/p>\n<p>Le d\u00e9fi ne se r\u00e9sume pas \u00e0 \u00ab ne pas mettre de secrets dans le code \u00bb. Il est bien plus profond. Comment donner \u00e0 un environnement de calcul \u00e9ph\u00e9m\u00e8re et jetable l\u2019acc\u00e8s \u00e0 vos identifiants les plus sensibles sans que ces identifiants ne fuient dans les logs, les artefacts, les jobs en aval ou entre les mains d\u2019acteurs malveillants ? C\u2019est la question \u00e0 laquelle ce guide r\u00e9pond.<\/p>\n<p>Nous couvrirons comment les secrets sont expos\u00e9s, comment les injecter en toute s\u00e9curit\u00e9, comment int\u00e9grer HashiCorp Vault et la f\u00e9d\u00e9ration d\u2019identit\u00e9 cloud-native, et quels anti-patterns \u00e9viter. Ceci est un guide de praticien \u2014 attendez-vous \u00e0 du vrai YAML, de vraies commandes CLI et de vraies d\u00e9cisions architecturales.<\/p>\n<h2>Comment les Secrets Sont Expos\u00e9s dans les Pipelines CI\/CD<\/h2>\n<p>Avant de discuter des solutions, nous devons comprendre le paysage des menaces. Les secrets fuient des pipelines par plusieurs vecteurs bien document\u00e9s.<\/p>\n<h3>Secrets Cod\u00e9s en Dur dans les Configs de Pipeline et l\u2019IaC<\/h3>\n<p>Le vecteur de fuite le plus basique \u2014 et encore \u00e9tonnamment courant \u2014 est celui des identifiants cod\u00e9s en dur directement dans les fichiers de configuration de pipeline ou les templates d\u2019Infrastructure as Code. Un d\u00e9veloppeur testant un d\u00e9ploiement pourrait ins\u00e9rer une cl\u00e9 d\u2019acc\u00e8s AWS dans un fichier <code>.github\/workflows\/deploy.yml<\/code> ou un fichier Terraform <code>main.tf<\/code>, le commiter et l\u2019oublier. M\u00eame supprim\u00e9 dans un commit ult\u00e9rieur, le secret vit \u00e9ternellement dans l\u2019historique Git.<\/p>\n<pre><code># NE FAITES JAMAIS CELA \u2014 identifiants cod\u00e9s en dur dans un fichier workflow\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n    env:\n      AWS_ACCESS_KEY_ID: AKIAIOSFODNN7EXAMPLE\n      AWS_SECRET_ACCESS_KEY: wJalrXUtnFEMI\/K7MDENG\/bPxRfiCYEXAMPLEKEY\n    steps:\n      - run: aws s3 sync .\/build s3:\/\/my-bucket<\/code><\/pre>\n<h3>Secrets dans les Variables d\u2019Environnement Affich\u00e9s dans les Logs<\/h3>\n<p>Les plateformes CI injectent g\u00e9n\u00e9ralement les secrets sous forme de variables d\u2019environnement. Le probl\u00e8me survient lorsque les \u00e9tapes du pipeline affichent par inadvertance ces variables sur stdout. Une commande <code>env<\/code> n\u00e9gligente, un <code>printenv<\/code> de d\u00e9bogage, ou un outil verbeux qui affiche sa configuration peuvent exposer des secrets dans les logs de build qui sont souvent conserv\u00e9s pendant des jours ou des semaines et accessibles \u00e0 tous les membres du projet.<\/p>\n<pre><code># Dangereux : cela affiche TOUTES les variables d'environnement, y compris les secrets\n- run: printenv | sort\n\n# \u00c9galement dangereux : les flags verbeux des outils qui affichent la config\n- run: terraform plan -debug<\/code><\/pre>\n<h3>Secrets Persistants dans les Artefacts de Build ou les Couches de Conteneur<\/h3>\n<p>Un secret inject\u00e9 pendant un build Docker peut persister dans une couche interm\u00e9diaire m\u00eame apr\u00e8s avoir \u00e9t\u00e9 supprim\u00e9 dans une instruction <code>RUN<\/code> ult\u00e9rieure. De m\u00eame, les artefacts de build \u2014 JARs, ZIPs, binaires compil\u00e9s \u2014 peuvent embarquer des fichiers de configuration contenant des identifiants qui \u00e9taient pr\u00e9sents au moment du build.<\/p>\n<pre><code># MAUVAIS : Le secret persiste dans la couche cr\u00e9\u00e9e par l'instruction COPY\nCOPY .env \/app\/.env\nRUN \/app\/setup.sh\nRUN rm \/app\/.env   # Trop tard \u2014 il est toujours dans une couche pr\u00e9c\u00e9dente<\/code><\/pre>\n<h3>Secrets Accessibles aux Workflows de PR Non Fiables<\/h3>\n<p>C\u2019est l\u2019un des vecteurs les plus dangereux, particuli\u00e8rement dans les projets open-source. GitHub Actions, par exemple, ne fournit pas de secrets aux workflows d\u00e9clench\u00e9s par <code>pull_request<\/code> depuis des forks \u2014 par conception. Cependant, l\u2019\u00e9v\u00e9nement <code>pull_request_target<\/code> <em>a<\/em> acc\u00e8s aux secrets, et si le workflow fait un checkout et ex\u00e9cute le code de l\u2019auteur de la PR, cela cr\u00e9e un chemin direct d\u2019exfiltration des secrets.<\/p>\n<h3>Port\u00e9es de Secrets Trop Larges<\/h3>\n<p>De nombreuses organisations configurent les secrets au niveau de l\u2019organisation ou du groupe alors qu\u2019ils devraient \u00eatre limit\u00e9s \u00e0 des d\u00e9p\u00f4ts individuels ou des environnements. Un secret au niveau de l\u2019organisation dans GitHub Actions est disponible pour <em>chaque d\u00e9p\u00f4t<\/em> de cette organisation. Si l\u2019un de ces d\u00e9p\u00f4ts est compromis \u2014 ou a simplement un workflow mal configur\u00e9 \u2014 tous les secrets au niveau de l\u2019organisation sont en danger.<\/p>\n<h2>Patterns d\u2019Injection de Secrets<\/h2>\n<p>Maintenant que nous comprenons comment les secrets fuient, examinons comment les int\u00e9grer dans les pipelines en toute s\u00e9curit\u00e9.<\/p>\n<h3>Secrets Natifs de la Plateforme<\/h3>\n<p>Chaque grande plateforme CI\/CD fournit un m\u00e9canisme int\u00e9gr\u00e9 de gestion des secrets. GitHub Actions dispose de secrets au niveau du d\u00e9p\u00f4t, de l\u2019environnement et de l\u2019organisation. GitLab CI dispose de variables CI\/CD au niveau du projet et du groupe avec masquage et protection optionnels. Ce sont le point de d\u00e9part le plus simple.<\/p>\n<pre><code># GitHub Actions : r\u00e9f\u00e9rencer un secret de d\u00e9p\u00f4t\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Deploy to production\n        env:\n          API_KEY: ${{ secrets.PRODUCTION_API_KEY }}\n        run: .\/deploy.sh<\/code><\/pre>\n<pre><code># GitLab CI : utiliser une variable masqu\u00e9e et prot\u00e9g\u00e9e\ndeploy:\n  stage: deploy\n  script:\n    - echo \"Deploying with masked credentials\"\n    - .\/deploy.sh\n  variables:\n    API_KEY: $PRODUCTION_API_KEY\n  only:\n    - main<\/code><\/pre>\n<p>Les secrets natifs de la plateforme sont ad\u00e9quats pour de nombreux cas d\u2019usage, mais ils pr\u00e9sentent des limitations significatives : pas de g\u00e9n\u00e9ration dynamique, journalisation d\u2019audit limit\u00e9e, rotation manuelle et pas de gestion centralis\u00e9e sur plusieurs plateformes.<\/p>\n<h3>Gestionnaires de Secrets Externes<\/h3>\n<p>Pour les organisations ayant des exigences de s\u00e9curit\u00e9 matures, les gestionnaires de secrets externes \u2014 HashiCorp Vault, AWS Secrets Manager, GCP Secret Manager, Azure Key Vault \u2014 fournissent un contr\u00f4le centralis\u00e9, une journalisation d\u2019audit, la g\u00e9n\u00e9ration dynamique de secrets, la rotation automatique et des politiques d\u2019acc\u00e8s granulaires. Nous approfondirons l\u2019int\u00e9gration de Vault dans la section suivante.<\/p>\n<h3>Injection Just-in-Time vs Secrets Pr\u00e9-charg\u00e9s<\/h3>\n<p>Les secrets pr\u00e9-charg\u00e9s sont configur\u00e9s une fois et rendus disponibles pour toutes les ex\u00e9cutions du pipeline. C\u2019est le fonctionnement de la plupart des secrets natifs de plateforme. L\u2019injection just-in-time (JIT) r\u00e9cup\u00e8re les secrets au moment o\u00f9 ils sont n\u00e9cessaires, souvent avec des TTL courts. L\u2019injection JIT est sup\u00e9rieure car elle r\u00e9duit la fen\u00eatre d\u2019exposition, permet l\u2019utilisation d\u2019identifiants dynamiques et fournit des traces d\u2019audit par ex\u00e9cution.<\/p>\n<pre><code># Injection JIT : r\u00e9cup\u00e9rer le secret uniquement quand c'est n\u00e9cessaire\n- name: Get database credentials\n  run: |\n    DB_CREDS=$(vault kv get -format=json secret\/data\/myapp\/db)\n    export DB_USER=$(echo $DB_CREDS | jq -r '.data.data.username')\n    export DB_PASS=$(echo $DB_CREDS | jq -r '.data.data.password')\n    .\/run-migrations.sh<\/code><\/pre>\n<h3>Secrets Masqu\u00e9s vs Chiffr\u00e9s<\/h3>\n<p>Une id\u00e9e re\u00e7ue courante : \u00ab masqu\u00e9 \u00bb ne signifie pas \u00ab s\u00e9curis\u00e9 \u00bb. Lorsque GitHub Actions masque un secret, il effectue un remplacement de cha\u00eene dans la sortie des logs. Si la valeur du secret est courte (par exemple, un token de 4 caract\u00e8res), le masquage peut ne pas s\u2019activer. Si le secret est encod\u00e9 en base64 ou transform\u00e9 de quelque mani\u00e8re que ce soit, la valeur transform\u00e9e ne sera <em>pas<\/em> masqu\u00e9e. Le masquage est une commodit\u00e9, pas une fronti\u00e8re de s\u00e9curit\u00e9. Les secrets chiffr\u00e9s au repos (que toutes les grandes plateformes fournissent) prot\u00e8gent contre la compromission du stockage c\u00f4t\u00e9 plateforme mais ne font rien pour emp\u00eacher l\u2019exfiltration \u00e0 l\u2019ex\u00e9cution.<\/p>\n<h2>Int\u00e9grer HashiCorp Vault avec le CI\/CD<\/h2>\n<p>HashiCorp Vault est le gestionnaire de secrets externe le plus largement adopt\u00e9 pour les pipelines CI\/CD. Il prend en charge plusieurs m\u00e9thodes d\u2019authentification adapt\u00e9es aux syst\u00e8mes automatis\u00e9s, la g\u00e9n\u00e9ration dynamique de secrets et des politiques granulaires. Voici comment l\u2019int\u00e9grer avec les deux plateformes CI\/CD les plus courantes.<\/p>\n<h3>Authentification Vault AppRole pour les Runners CI<\/h3>\n<p>AppRole est la m\u00e9thode d\u2019authentification orient\u00e9e machine de Vault. Elle utilise un Role ID (comme un nom d\u2019utilisateur) et un Secret ID (comme un mot de passe) pour s\u2019authentifier. Le Secret ID peut \u00eatre configur\u00e9 pour un usage unique et avec un TTL, ce qui le rend adapt\u00e9 aux runners CI.<\/p>\n<pre><code># Activer la m\u00e9thode d'authentification AppRole\nvault auth enable approle\n\n# Cr\u00e9er une politique pour le CI\nvault policy write ci-deploy - &lt;&lt;EOF\npath \"secret\/data\/myapp\/*\" {\n  capabilities = [\"read\"]\n}\npath \"database\/creds\/myapp-role\" {\n  capabilities = [\"read\"]\n}\nEOF\n\n# Cr\u00e9er un AppRole avec la politique CI\nvault write auth\/approle\/role\/ci-deploy \\\n  token_policies=\"ci-deploy\" \\\n  token_ttl=15m \\\n  token_max_ttl=30m \\\n  secret_id_ttl=10m \\\n  secret_id_num_uses=1\n\n# R\u00e9cup\u00e9rer le Role ID (stocker dans la plateforme CI comme variable non sensible)\nvault read auth\/approle\/role\/ci-deploy\/role-id\n\n# G\u00e9n\u00e9rer un Secret ID \u00e0 usage unique (stocker dans la plateforme CI comme secret)\nvault write -f auth\/approle\/role\/ci-deploy\/secret-id<\/code><\/pre>\n<h3>Authentification Vault JWT\/OIDC avec GitHub Actions<\/h3>\n<p>L\u2019approche moderne et pr\u00e9f\u00e9r\u00e9e pour GitHub Actions est l\u2019authentification JWT\/OIDC. GitHub Actions peut \u00e9mettre un token OIDC pour chaque ex\u00e9cution de workflow, et Vault peut valider ce token pour authentifier le pipeline \u2014 \u00e9liminant le besoin de stocker des identifiants Vault dans GitHub.<\/p>\n<pre><code># Configurer l'authentification JWT de Vault pour GitHub Actions\nvault auth enable jwt\n\nvault write auth\/jwt\/config \\\n  bound_issuer=\"https:\/\/token.actions.githubusercontent.com\" \\\n  oidc_discovery_url=\"https:\/\/token.actions.githubusercontent.com\"\n\n# Cr\u00e9er un r\u00f4le li\u00e9 \u00e0 un d\u00e9p\u00f4t et une branche sp\u00e9cifiques\nvault write auth\/jwt\/role\/github-deploy \\\n  role_type=\"jwt\" \\\n  bound_audiences=\"https:\/\/github.com\/my-org\" \\\n  bound_claims_type=\"glob\" \\\n  bound_claims='{\"sub\": \"repo:my-org\/my-repo:ref:refs\/heads\/main\"}' \\\n  user_claim=\"repository_owner\" \\\n  token_policies=\"ci-deploy\" \\\n  token_ttl=\"10m\"<\/code><\/pre>\n<p>Ensuite, dans votre workflow GitHub Actions, utilisez <code>hashicorp\/vault-action<\/code> :<\/p>\n<pre><code>jobs:\n  deploy:\n    runs-on: ubuntu-latest\n    permissions:\n      id-token: write\n      contents: read\n    steps:\n      - name: Import secrets from Vault\n        uses: hashicorp\/vault-action@v3\n        with:\n          url: https:\/\/vault.mycompany.com\n          method: jwt\n          role: github-deploy\n          jwtGithubAudience: https:\/\/github.com\/my-org\n          secrets: |\n            secret\/data\/myapp\/db username | DB_USER ;\n            secret\/data\/myapp\/db password | DB_PASS\n\n      - name: Run deployment\n        run: |\n          echo \"Deploying with fetched credentials\"\n          .\/deploy.sh<\/code><\/pre>\n<h3>Authentification JWT de Vault avec GitLab CI<\/h3>\n<p>GitLab CI offre un support natif pour l\u2019int\u00e9gration Vault en utilisant <code>id_tokens<\/code>. GitLab peut g\u00e9n\u00e9rer un JWT que Vault valide, de mani\u00e8re similaire \u00e0 l\u2019approche GitHub Actions.<\/p>\n<pre><code># Configurer Vault pour l'authentification JWT GitLab\nvault auth enable -path=gitlab jwt\n\nvault write auth\/gitlab\/config \\\n  bound_issuer=\"https:\/\/gitlab.com\" \\\n  jwks_url=\"https:\/\/gitlab.com\/-\/jwks\" \\\n  supported_algs=\"RS256\"\n\nvault write auth\/gitlab\/role\/gitlab-deploy \\\n  role_type=\"jwt\" \\\n  bound_claims='{\"project_id\": \"12345\", \"ref_protected\": \"true\"}' \\\n  user_claim=\"user_email\" \\\n  token_policies=\"ci-deploy\" \\\n  token_ttl=\"10m\"<\/code><\/pre>\n<p>Et dans votre <code>.gitlab-ci.yml<\/code> :<\/p>\n<pre><code>deploy:\n  stage: deploy\n  id_tokens:\n    VAULT_ID_TOKEN:\n      aud: https:\/\/vault.mycompany.com\n  secrets:\n    DB_USER:\n      vault: myapp\/db\/username@secret\n      token: $VAULT_ID_TOKEN\n    DB_PASS:\n      vault: myapp\/db\/password@secret\n      token: $VAULT_ID_TOKEN\n  script:\n    - .\/deploy.sh<\/code><\/pre>\n<h3>Secrets Dynamiques<\/h3>\n<p>L\u2019une des fonctionnalit\u00e9s les plus puissantes de Vault est la g\u00e9n\u00e9ration dynamique de secrets. Au lieu de stocker des mots de passe de base de donn\u00e9es statiques, Vault peut g\u00e9n\u00e9rer des identifiants \u00e0 courte dur\u00e9e de vie \u00e0 la demande. Lorsque le pipeline se termine, les identifiants expirent automatiquement.<\/p>\n<pre><code># Activer le moteur de secrets de base de donn\u00e9es\nvault secrets enable database\n\n# Configurer une connexion PostgreSQL\nvault write database\/config\/myapp-db \\\n  plugin_name=postgresql-database-plugin \\\n  connection_url=\"postgresql:\/\/{{username}}:{{password}}@db.mycompany.com:5432\/myapp\" \\\n  allowed_roles=\"myapp-role\" \\\n  username=\"vault_admin\" \\\n  password=\"vault_admin_password\"\n\n# Cr\u00e9er un r\u00f4le qui g\u00e9n\u00e8re des identifiants avec un TTL d'1 heure\nvault write database\/roles\/myapp-role \\\n  db_name=myapp-db \\\n  creation_statements=\"CREATE ROLE \\\"{{name}}\\\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT, INSERT, UPDATE ON ALL TABLES IN SCHEMA public TO \\\"{{name}}\\\";\" \\\n  default_ttl=\"1h\" \\\n  max_ttl=\"2h\"\n\n# Dans votre pipeline, r\u00e9cup\u00e9rez les identifiants dynamiques\n# vault read database\/creds\/myapp-role\n# Retourne une paire nom d'utilisateur\/mot de passe unique valide pendant 1 heure<\/code><\/pre>\n<p>Les secrets dynamiques \u00e9liminent enti\u00e8rement le probl\u00e8me de la rotation des identifiants. Chaque ex\u00e9cution de pipeline obtient ses propres identifiants uniques, et les identifiants compromis expirent automatiquement.<\/p>\n<h2>Identifiants \u00e0 Courte Dur\u00e9e de Vie et Workload Identity<\/h2>\n<p>L\u2019avanc\u00e9e la plus significative en mati\u00e8re de gestion des secrets CI\/CD ces derni\u00e8res ann\u00e9es est la f\u00e9d\u00e9ration de workload identity \u2014 la capacit\u00e9 pour une plateforme CI\/CD de s\u2019authentifier directement aupr\u00e8s d\u2019un fournisseur cloud en utilisant sa propre identit\u00e9, sans aucun identifiant stock\u00e9.<\/p>\n<h3>GitHub Actions OIDC avec AWS<\/h3>\n<p>GitHub Actions peut assumer un r\u00f4le AWS IAM directement en utilisant la f\u00e9d\u00e9ration OIDC. Aucune cl\u00e9 d\u2019acc\u00e8s AWS n\u2019est stock\u00e9e nulle part.<\/p>\n<pre><code># D'abord, cr\u00e9er un fournisseur d'identit\u00e9 OIDC dans AWS (via Terraform)\nresource \"aws_iam_openid_connect_provider\" \"github\" {\n  url             = \"https:\/\/token.actions.githubusercontent.com\"\n  client_id_list  = [\"sts.amazonaws.com\"]\n  thumbprint_list = [\"6938fd4d98bab03faadb97b34396831e3780aea1\"]\n}\n\n# Cr\u00e9er un r\u00f4le IAM que GitHub Actions peut assumer\nresource \"aws_iam_role\" \"github_actions\" {\n  name = \"github-actions-deploy\"\n\n  assume_role_policy = jsonencode({\n    Version = \"2012-10-17\"\n    Statement = [{\n      Effect = \"Allow\"\n      Principal = {\n        Federated = aws_iam_openid_connect_provider.github.arn\n      }\n      Action = \"sts:AssumeRoleWithWebIdentity\"\n      Condition = {\n        StringEquals = {\n          \"token.actions.githubusercontent.com:aud\" = \"sts.amazonaws.com\"\n        }\n        StringLike = {\n          \"token.actions.githubusercontent.com:sub\" = \"repo:my-org\/my-repo:ref:refs\/heads\/main\"\n        }\n      }\n    }]\n  })\n}<\/code><\/pre>\n<pre><code># Workflow GitHub Actions utilisant OIDC\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n    permissions:\n      id-token: write\n      contents: read\n    steps:\n      - uses: aws-actions\/configure-aws-credentials@v4\n        with:\n          role-to-assume: arn:aws:iam::123456789012:role\/github-actions-deploy\n          aws-region: us-east-1\n          role-duration-seconds: 900   # 15 minutes\n\n      - name: Deploy\n        run: aws s3 sync .\/build s3:\/\/my-bucket<\/code><\/pre>\n<h3>GitHub Actions OIDC avec GCP<\/h3>\n<p>Google Cloud prend en charge le m\u00eame pattern via Workload Identity Federation.<\/p>\n<pre><code># Cr\u00e9er un Workload Identity Pool et un Provider (gcloud CLI)\ngcloud iam workload-identity-pools create \"github-pool\" \\\n  --project=\"my-project\" \\\n  --location=\"global\" \\\n  --display-name=\"GitHub Actions Pool\"\n\ngcloud iam workload-identity-pools providers create-oidc \"github-provider\" \\\n  --project=\"my-project\" \\\n  --location=\"global\" \\\n  --workload-identity-pool=\"github-pool\" \\\n  --display-name=\"GitHub Provider\" \\\n  --attribute-mapping=\"google.subject=assertion.sub,attribute.repository=assertion.repository\" \\\n  --attribute-condition=\"assertion.repository_owner == 'my-org'\" \\\n  --issuer-uri=\"https:\/\/token.actions.githubusercontent.com\"\n\n# Accorder au Workload Identity la capacit\u00e9 d'usurper un compte de service\ngcloud iam service-accounts add-iam-policy-binding \\\n  deploy-sa@my-project.iam.gserviceaccount.com \\\n  --role=\"roles\/iam.workloadIdentityUser\" \\\n  --member=\"principalSet:\/\/iam.googleapis.com\/projects\/123456\/locations\/global\/workloadIdentityPools\/github-pool\/attribute.repository\/my-org\/my-repo\"<\/code><\/pre>\n<pre><code># Workflow GitHub Actions pour GCP\njobs:\n  deploy:\n    runs-on: ubuntu-latest\n    permissions:\n      id-token: write\n      contents: read\n    steps:\n      - uses: google-github-actions\/auth@v2\n        with:\n          workload_identity_provider: projects\/123456\/locations\/global\/workloadIdentityPools\/github-pool\/providers\/github-provider\n          service_account: deploy-sa@my-project.iam.gserviceaccount.com\n\n      - name: Deploy to Cloud Run\n        run: gcloud run deploy my-service --image=gcr.io\/my-project\/my-app:latest<\/code><\/pre>\n<h3>F\u00e9d\u00e9ration OIDC avec GitLab CI<\/h3>\n<p>GitLab CI prend en charge le m\u00eame pattern de f\u00e9d\u00e9ration OIDC avec AWS, GCP et Azure. La configuration est similaire \u2014 vous configurez le fournisseur cloud pour faire confiance \u00e0 l\u2019\u00e9metteur OIDC de GitLab et liez l\u2019acc\u00e8s \u00e0 des IDs de projet, des branches ou des environnements sp\u00e9cifiques.<\/p>\n<pre><code># GitLab CI avec AWS OIDC\nassume_role:\n  stage: deploy\n  id_tokens:\n    AWS_OIDC_TOKEN:\n      aud: https:\/\/sts.amazonaws.com\n  script:\n    - >\n      STS_CREDS=$(aws sts assume-role-with-web-identity\n      --role-arn arn:aws:iam::123456789012:role\/gitlab-deploy\n      --role-session-name \"gitlab-ci-${CI_PIPELINE_ID}\"\n      --web-identity-token \"${AWS_OIDC_TOKEN}\"\n      --duration-seconds 900)\n    - export AWS_ACCESS_KEY_ID=$(echo $STS_CREDS | jq -r '.Credentials.AccessKeyId')\n    - export AWS_SECRET_ACCESS_KEY=$(echo $STS_CREDS | jq -r '.Credentials.SecretAccessKey')\n    - export AWS_SESSION_TOKEN=$(echo $STS_CREDS | jq -r '.Credentials.SessionToken')\n    - aws s3 sync .\/build s3:\/\/my-bucket<\/code><\/pre>\n<h3>Pourquoi les Identifiants \u00e0 Courte Dur\u00e9e de Vie l\u2019Emportent<\/h3>\n<p>Les avantages des identifiants f\u00e9d\u00e9r\u00e9s \u00e0 courte dur\u00e9e de vie par rapport aux secrets statiques \u00e0 longue dur\u00e9e de vie sont consid\u00e9rables :<\/p>\n<ul>\n<li><strong>Aucun secret \u00e0 voler.<\/strong> Il n\u2019y a pas d\u2019identifiants stock\u00e9s \u00e0 exfiltrer. Le pipeline s\u2019authentifie avec un JWT sign\u00e9 qui n\u2019est valide que pour cette ex\u00e9cution sp\u00e9cifique.<\/li>\n<li><strong>Pas de rotation n\u00e9cessaire.<\/strong> Les identifiants sont g\u00e9n\u00e9r\u00e9s par ex\u00e9cution et expirent automatiquement. Il n\u2019y a rien \u00e0 faire tourner.<\/li>\n<li><strong>Port\u00e9e granulaire.<\/strong> L\u2019acc\u00e8s peut \u00eatre restreint \u00e0 des d\u00e9p\u00f4ts, branches, environnements sp\u00e9cifiques, et m\u00eame \u00e0 des jobs de workflow pr\u00e9cis.<\/li>\n<li><strong>Trace d\u2019audit compl\u00e8te.<\/strong> Les logs du fournisseur cloud montrent exactement quelle ex\u00e9cution de pipeline a acc\u00e9d\u00e9 \u00e0 quelles ressources, li\u00e9e au claim OIDC.<\/li>\n<li><strong>R\u00e9duction du rayon d\u2019impact.<\/strong> M\u00eame si un identifiant est exfiltr\u00e9 d\u2019une mani\u00e8re ou d\u2019une autre, il expire en quelques minutes, pas en plusieurs mois.<\/li>\n<\/ul>\n<h2>Anti-Patterns \u00e0 \u00c9viter<\/h2>\n<p>Savoir ce qu\u2019il ne faut <em>pas<\/em> faire est aussi important que conna\u00eetre les bons patterns. Ces anti-patterns sont r\u00e9guli\u00e8rement observ\u00e9s dans les environnements de production.<\/p>\n<h3>Utiliser des Personal Access Tokens dans le CI<\/h3>\n<p>Les personal access tokens (PATs) li\u00e9s \u00e0 des comptes de d\u00e9veloppeurs individuels sont l\u2019un des patterns les plus courants et les plus dangereux. Lorsqu\u2019un d\u00e9veloppeur quitte l\u2019organisation, son PAT peut continuer \u00e0 fonctionner. Les PATs ont g\u00e9n\u00e9ralement des permissions larges \u2014 bien plus que ce dont le pipeline a besoin. S\u2019ils sont exfiltr\u00e9s, l\u2019attaquant obtient acc\u00e8s \u00e0 tout ce \u00e0 quoi le d\u00e9veloppeur pouvait acc\u00e9der.<\/p>\n<p><strong>\u00c0 la place :<\/strong> Utilisez des comptes machine avec des tokens \u00e0 port\u00e9e limit\u00e9e, ou mieux encore, utilisez des tokens d\u2019installation GitHub App ou la f\u00e9d\u00e9ration OIDC.<\/p>\n<h3>Partager les Secrets entre Environnements<\/h3>\n<p>Utiliser le m\u00eame mot de passe de base de donn\u00e9es pour le d\u00e9veloppement, le staging et la production \u2014 ou la m\u00eame cl\u00e9 API pour tous les environnements \u2014 signifie qu\u2019une compromission de votre environnement le moins s\u00e9curis\u00e9 (g\u00e9n\u00e9ralement le dev) donne aux attaquants acc\u00e8s \u00e0 la production. La s\u00e9paration des environnements n\u2019a aucun sens si les identifiants sont les m\u00eames.<\/p>\n<p><strong>\u00c0 la place :<\/strong> Utilisez des secrets scop\u00e9s par environnement. Dans GitHub Actions, configurez des environnements de d\u00e9ploiement avec leurs propres magasins de secrets. Dans GitLab, utilisez des variables prot\u00e9g\u00e9es scop\u00e9es \u00e0 des environnements sp\u00e9cifiques.<\/p>\n<h3>Ne Pas Faire Tourner les Secrets Apr\u00e8s une Exposition<\/h3>\n<p>Lorsqu\u2019un secret est accidentellement journalis\u00e9, commit\u00e9 dans un d\u00e9p\u00f4t ou expos\u00e9 dans un artefact de build, de nombreuses \u00e9quipes se contentent de supprimer le log ou de retirer le commit sans faire tourner l\u2019identifiant. C\u2019est insuffisant. Vous devez consid\u00e9rer que le secret a \u00e9t\u00e9 observ\u00e9 et le faire tourner imm\u00e9diatement.<\/p>\n<p><strong>\u00c0 la place :<\/strong> Traitez toute exposition comme une compromission. Faites la rotation imm\u00e9diatement. Automatisez la rotation lorsque possible. Utilisez des secrets dynamiques pour rendre le probl\u00e8me sans objet.<\/p>\n<h3>Faire Confiance \u00e0 pull_request_target avec les Secrets<\/h3>\n<p>L\u2019\u00e9v\u00e9nement <code>pull_request_target<\/code> dans GitHub Actions s\u2019ex\u00e9cute dans le contexte de la branche de base, ce qui signifie qu\u2019il a acc\u00e8s aux secrets. Ceci est pr\u00e9vu pour des op\u00e9rations s\u00fbres comme l\u2019\u00e9tiquetage des PRs. Cependant, si votre workflow fait un checkout de la ref head de la PR et ex\u00e9cute ce code, vous avez donn\u00e9 \u00e0 un contributeur externe un acc\u00e8s complet \u00e0 vos secrets.<\/p>\n<pre><code># DANGEREUX : Cela donne \u00e0 l'auteur de la PR acc\u00e8s \u00e0 tous les secrets du d\u00e9p\u00f4t\non: pull_request_target\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\/checkout@v4\n        with:\n          ref: ${{ github.event.pull_request.head.sha }}  # Checkout de code non fiable !\n      - run: make test  # Ex\u00e9cution de code non fiable avec acc\u00e8s aux secrets !<\/code><\/pre>\n<p><strong>\u00c0 la place :<\/strong> Ne faites jamais un checkout et n\u2019ex\u00e9cutez jamais le code d\u2019une PR dans un workflow <code>pull_request_target<\/code>. Si vous devez ex\u00e9cuter des tests sur du code de PR avec des secrets, utilisez une approche \u00e0 deux workflows : ex\u00e9cutez le code non fiable dans un workflow <code>pull_request<\/code> (sans secrets), puis utilisez un d\u00e9clencheur <code>workflow_run<\/code> s\u00e9par\u00e9 pour les op\u00e9rations de confiance.<\/p>\n<h2>D\u00e9fense en Profondeur : Une Approche en Couches<\/h2>\n<p>Aucun contr\u00f4le unique n\u2019est suffisant. Une gestion efficace des secrets n\u00e9cessite plusieurs couches de d\u00e9fense superpos\u00e9es.<\/p>\n<h3>Analyse de Secrets (Secret Scanning)<\/h3>\n<p>Impl\u00e9mentez l\u2019analyse \u00e0 trois \u00e9tapes :<\/p>\n<ul>\n<li><strong>Pr\u00e9-commit :<\/strong> Utilisez des outils comme <code>gitleaks<\/code> ou <code>detect-secrets<\/code> comme hooks de pr\u00e9-commit pour emp\u00eacher les secrets d\u2019entrer dans le d\u00e9p\u00f4t.<\/li>\n<li><strong>Dans le pipeline :<\/strong> Ex\u00e9cutez l\u2019analyse de secrets comme \u00e9tape CI sur chaque pull request. Des outils comme <code>trufflehog<\/code> peuvent scanner les diffs, l\u2019historique des commits et m\u00eame les fichiers binaires.<\/li>\n<li><strong>Post-commit :<\/strong> Activez l\u2019analyse de secrets int\u00e9gr\u00e9e de GitHub ou la d\u00e9tection de secrets de GitLab pour scanner en continu tout le contenu du d\u00e9p\u00f4t et alerter sur les r\u00e9sultats.<\/li>\n<\/ul>\n<pre><code># Hook de pr\u00e9-commit avec gitleaks\n# .pre-commit-config.yaml\nrepos:\n  - repo: https:\/\/github.com\/gitleaks\/gitleaks\n    rev: v8.21.2\n    hooks:\n      - id: gitleaks<\/code><\/pre>\n<pre><code># Analyse dans le pipeline avec trufflehog\n- name: Scan for secrets\n  run: |\n    docker run --rm -v \"$PWD:\/repo\" trufflesecurity\/trufflehog:latest \\\n      git file:\/\/\/repo --only-verified --fail<\/code><\/pre>\n<h3>Journalisation d\u2019Audit pour l\u2019Acc\u00e8s aux Secrets<\/h3>\n<p>Chaque acc\u00e8s \u00e0 un secret doit \u00eatre journalis\u00e9. Vault fournit des logs d\u2019audit d\u00e9taill\u00e9s par d\u00e9faut. Les gestionnaires de secrets des fournisseurs cloud (AWS Secrets Manager, GCP Secret Manager) s\u2019int\u00e8grent respectivement avec CloudTrail et Cloud Audit Logs. Pour les secrets natifs de la plateforme, activez les fonctionnalit\u00e9s de journalisation d\u2019audit disponibles dans GitHub Enterprise ou GitLab Ultimate.<\/p>\n<pre><code># Activer la journalisation d'audit de Vault\nvault audit enable file file_path=\/var\/log\/vault\/audit.log\n\n# Chaque acc\u00e8s g\u00e9n\u00e8re une entr\u00e9e de log comme :\n# {\"type\": \"response\", \"auth\": {\"token_type\": \"service\", \"policies\": [\"ci-deploy\"]},\n#  \"request\": {\"path\": \"secret\/data\/myapp\/db\", \"operation\": \"read\"}, ...}<\/code><\/pre>\n<h3>Port\u00e9e au Moindre Privil\u00e8ge<\/h3>\n<p>Appliquez le principe du moindre privil\u00e8ge de mani\u00e8re agressive :<\/p>\n<ul>\n<li>Limitez les secrets au d\u00e9p\u00f4t sp\u00e9cifique qui en a besoin, pas \u00e0 l\u2019organisation.<\/li>\n<li>Utilisez des secrets au niveau de l\u2019environnement pour que les identifiants de production ne soient disponibles qu\u2019aux workflows qui d\u00e9ploient en production.<\/li>\n<li>Configurez la protection des branches pour que seuls les workflows ex\u00e9cut\u00e9s sur des branches prot\u00e9g\u00e9es puissent acc\u00e9der aux secrets de production.<\/li>\n<li>Dans Vault, \u00e9crivez des politiques qui accordent l\u2019acc\u00e8s au chemin le plus restreint possible avec des capacit\u00e9s en lecture seule.<\/li>\n<\/ul>\n<pre><code># Politique Vault : acc\u00e8s minimal pour le CI d'un microservice sp\u00e9cifique\npath \"secret\/data\/payments-service\/production\" {\n  capabilities = [\"read\"]\n}\n\n# Refuser l'acc\u00e8s \u00e0 tout le reste par d\u00e9faut (comportement par d\u00e9faut de Vault)\n# Pas de wildcards, pas de chemins larges<\/code><\/pre>\n<h3>Rotation Automatis\u00e9e<\/h3>\n<p>Les secrets statiques doivent \u00eatre soumis \u00e0 une rotation r\u00e9guli\u00e8re et imm\u00e9diatement apr\u00e8s toute exposition suspect\u00e9e. Automatisez ce processus :<\/p>\n<ul>\n<li>Utilisez les secrets dynamiques de Vault pour \u00e9liminer enti\u00e8rement le besoin de rotation.<\/li>\n<li>Pour les secrets qui doivent \u00eatre statiques (par exemple, des cl\u00e9s API tierces), utilisez la rotation int\u00e9gr\u00e9e d\u2019AWS Secrets Manager avec des fonctions Lambda ou des solutions cloud-native similaires.<\/li>\n<li>Impl\u00e9mentez des alertes pour les secrets qui n\u2019ont pas \u00e9t\u00e9 soumis \u00e0 une rotation dans le d\u00e9lai pr\u00e9vu.<\/li>\n<\/ul>\n<pre><code># AWS Secrets Manager : configurer la rotation automatique\naws secretsmanager rotate-secret \\\n  --secret-id myapp\/api-key \\\n  --rotation-lambda-arn arn:aws:lambda:us-east-1:123456789012:function:rotate-api-key \\\n  --rotation-rules '{\"ScheduleExpression\": \"rate(30 days)\"}'<\/code><\/pre>\n<h2>Conclusion : La Gestion des Secrets Est un Processus Continu<\/h2>\n<p>La gestion des secrets n\u2019est pas une case \u00e0 cocher lors de la configuration initiale du pipeline. C\u2019est une discipline continue qui doit \u00e9voluer au fur et \u00e0 mesure que votre infrastructure grandit, que de nouvelles techniques d\u2019attaque \u00e9mergent et que votre \u00e9quipe change. Les patterns d\u00e9crits dans ce guide \u2014 f\u00e9d\u00e9ration OIDC, secrets dynamiques, injection just-in-time, port\u00e9e au moindre privil\u00e8ge et analyse en couches \u2014 repr\u00e9sentent l\u2019\u00e9tat de l\u2019art actuel, mais ils n\u00e9cessitent une attention continue.<\/p>\n<p>Commencez par auditer vos pipelines actuels. Identifiez chaque identifiant stock\u00e9. Pour chacun d\u2019eux, demandez-vous : peut-il \u00eatre remplac\u00e9 par un identifiant \u00e0 courte dur\u00e9e de vie ou une f\u00e9d\u00e9ration de workload identity ? Sa port\u00e9e peut-elle \u00eatre r\u00e9duite ? Ce secret est-il journalis\u00e9 quelque part ? Existe-t-il une trace d\u2019audit pour chaque acc\u00e8s ?<\/p>\n<p>Les organisations qui subissent des br\u00e8ches CI\/CD ne sont pas celles qui n\u2019ont jamais stock\u00e9 de secret \u2014 c\u2019est impossible. Ce sont celles qui ont trait\u00e9 la gestion des secrets comme une t\u00e2che de configuration ponctuelle plut\u00f4t que comme une pratique de s\u00e9curit\u00e9 vivante. Construisez l\u2019automatisation, appliquez les politiques, surveillez les logs d\u2019acc\u00e8s et it\u00e9rez. Vos pipelines seront consid\u00e9rablement plus difficiles \u00e0 compromettre en cons\u00e9quence.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Introduction : Pourquoi les Secrets Sont la Cause N\u00b01 de Compromission CI\/CD Si vous examinez la cause profonde de presque chaque br\u00e8che majeure CI\/CD de ces derni\u00e8res ann\u00e9es \u2014 de l\u2019attaque de la cha\u00eene d\u2019approvisionnement Codecov \u00e0 l\u2019incident de s\u00e9curit\u00e9 CircleCI \u2014 vous trouverez le m\u00eame coupable : des secrets compromis. Cl\u00e9s API, identifiants cloud, &#8230; <a title=\"Gestion des Secrets dans les Pipelines CI\/CD : Patterns, Anti-Patterns et Int\u00e9gration Vault\" class=\"read-more\" href=\"https:\/\/secure-pipelines.com\/fr\/ci-cd-security\/secrets-management-ci-cd-pipelines-patterns-vault-2\/\" aria-label=\"En savoir plus sur Gestion des Secrets dans les Pipelines CI\/CD : Patterns, Anti-Patterns et Int\u00e9gration Vault\">Lire la suite<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[49,51],"tags":[],"post_folder":[],"class_list":["post-482","post","type-post","status-publish","format-standard","hentry","category-ci-cd-security","category-pipeline-hardening"],"_links":{"self":[{"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/posts\/482","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/comments?post=482"}],"version-history":[{"count":1,"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/posts\/482\/revisions"}],"predecessor-version":[{"id":487,"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/posts\/482\/revisions\/487"}],"wp:attachment":[{"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/media?parent=482"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/categories?post=482"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/tags?post=482"},{"taxonomy":"post_folder","embeddable":true,"href":"https:\/\/secure-pipelines.com\/fr\/wp-json\/wp\/v2\/post_folder?post=482"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}