Skip to Content
SécuritéPartie 14 — Durcir l'infra & le déploiement14.4 — CI/CD sécurisé & scan d'images

Chapitre 14.4 — CI/CD sécurisé & scan d’images

⏱️ TL;DR — La CI/CD est une cible de choix : elle a accès à tout — le code, les secrets (déploiement, cloud, registres), et la prod. La compromettre (via une dépendance vérolée exécutée en postinstall, un workflow trop permissif, un secret exposé) donne à l’attaquant les clés du royaume. On la durcit : droits minimaux pour les jobs, secrets en store dédié (jamais en clair, jamais logués), préférer l’OIDC (identités fédérées à courte durée) aux credentials long-lived, épingler les actions/images à des versions/digests, isoler les builds (Partie 12-14.3). On scanne les images de conteneurs (Trivy…) pour les CVE, et on signe les artefacts (provenance, Partie 12). Enfin, on protège la branche et on revoit ce qui déploie.

🎯 Objectifs

  • Comprendre pourquoi la CI/CD est une cible critique (accès au code, secrets, prod).
  • Appliquer le moindre privilège aux pipelines et gérer les secrets de CI proprement.
  • Préférer l’OIDC aux credentials long-lived pour l’accès au cloud/registres.
  • Scanner les images de conteneurs et signer les artefacts (intégrité/provenance).

La CI/CD : les clés du royaume

Un pipeline de CI/CD (GitHub Actions, GitLab CI… — Serveur Linux) construit, teste et déploie ton code. Pour ça, il détient un pouvoir considérable : accès au code source, aux secrets (clés de déploiement, tokens cloud, identifiants de registre), et souvent un accès direct à la production. C’est donc une cible de très grande valeur : compromettre la CI, c’est potentiellement pouvoir injecter du code dans les artefacts déployés (empoisonner tous les utilisateurs), exfiltrer les secrets, et atteindre la prod.

Les vecteurs de compromission de CI :

  • Une dépendance vérolée dont le postinstall s’exécute dans la CI (Partie 12) et vole les secrets/tokens du pipeline.
  • Un workflow trop permissif (droits d’écriture larges, déclenché par des sources non fiables comme une PR d’un fork).
  • Un secret exposé (logué, dans un artefact, dans une variable d’environnement lisible).
  • Une action tierce compromise (le même problème de supply chain, appliqué aux étapes du pipeline).

Durcir le pipeline

Moindre privilège des jobs

Chaque job ne reçoit que les permissions strictement nécessaires. Sur GitHub Actions, par ex., déclarer des permissions minimales (lecture seule par défaut, écriture seulement là où c’est requis) plutôt que le jeton par défaut large. Un job de test n’a pas besoin des droits de déploiement.

# Permissions minimales par defaut (GitHub Actions) permissions: contents: read # lecture seule par defaut # on n'accorde plus (ex. packages: write) que sur le job qui en a VRAIMENT besoin

Secrets de CI

  • Stockés dans le store de secrets de la plateforme (chiffrés), jamais en clair dans le YAML ni committés (Partie 13).
  • Jamais logués : attention aux echo, aux commandes verbeuses, aux erreurs qui impriment l’environnement (les plateformes masquent les secrets connus, mais pas toujours les dérivés).
  • Portée limitée : un secret par usage (Partie 13), accessible seulement aux jobs/environnements qui en ont besoin (environnements protégés).
  • Attention aux PR de forks : ne pas exposer les secrets aux workflows déclenchés par des contributions externes non fiables.

OIDC plutôt que credentials long-lived

Le meilleur durcissement pour l’accès au cloud/registres : remplacer les clés statiques à longue durée (une clé d’accès cloud stockée en secret de CI, valable des années, catastrophique si elle fuite) par une fédération d’identité OIDC. Le pipeline prouve son identité au cloud (via un jeton OIDC signé émis par la plateforme CI) et reçoit des credentials temporaires à courte durée, sans qu’aucune clé long-lived ne soit stockée.

CI (identite OIDC) --prouve son identite--> Cloud --credentials TEMPORAIRES (courte duree)--> # Aucune cle statique a stocker ni a rotationner ; rien a voler qui reste valide longtemps.

Avantages : rien à voler qui reste valable (les credentials expirent vite), pas de clé à rotationner, portée restreinte au pipeline précis. C’est la direction recommandée quand la plateforme le permet.

Épingler les dépendances du pipeline

Comme pour les paquets (Partie 12), épingler les actions/étapes tierces et les images de base à des versions précises — idéalement à un digest (empreinte) plutôt qu’à un tag mutable (@v3 peut changer ; @<sha> non). Une action tierce compromise dont tu suis le tag mutable exécute son code malveillant dans ta CI.

Scanner les images de conteneurs

Une image de conteneur (Partie 14.3) embarque un OS et des paquets qui ont leurs CVE (Partie 12). On scanne les images pour détecter les vulnérabilités connues avant de déployer :

  • Outils : Trivy, Grype, les scanners intégrés aux registres.
  • Intégrer le scan à la CI (bloquer si vulnérabilité critique), comme npm audit (Partie 12).
  • Préférer des images minimales (moins de paquets = moins de CVE) et les reconstruire régulièrement (pour intégrer les correctifs de base).
# Scanner une image pour les vulnerabilites connues (exemple) trivy image mon-app:1.2.3 # liste les CVE des paquets de l'image

Signer les artefacts (provenance)

Pour garantir qu’un artefact déployé est bien celui qu’on a construit (et pas une version substituée), on signe les artefacts/images (provenance, Partie 12) et on vérifie la signature au déploiement. Ainsi, un attaquant ne peut pas glisser une image piégée dans le registre sans que la vérification échoue. C’est la boucle : build reproductible → artefact signé → déploiement qui vérifie.

⚠️ Piège — La CI/CD est souvent le maillon négligé : on sécurise l’appli et l’infra, mais on laisse un pipeline avec un token cloud long-lived en secret, des permissions par défaut larges, des actions tierces en tag mutable, et des builds qui exécutent du code de dépendances non vérifiées. Or compromettre la CI, c’est compromettre tout ce qu’elle déploie — bien plus grave qu’une faille dans un endpoint. Traite le pipeline avec le même niveau d’exigence que la prod : moindre privilège, secrets courts (OIDC), dépendances épinglées, scans, signatures.

🎯 Côté attaquant — La CI est un objectif stratégique : plutôt que d’attaquer une appli, l’attaquant vise le pipeline qui la construit — car s’y introduire lui permet d’empoisonner les artefacts (atteindre tous les utilisateurs d’un coup, comme dans les grandes attaques supply chain) et de rafler les secrets (accès cloud, prod). Ses voies : une dépendance dont le postinstall s’exécute en CI, une action tierce compromise, un workflow déclenché par une PR de fork qui accède aux secrets, un secret logué. Une CI durcie (droits minimaux, OIDC, dépendances épinglées, pas de secrets exposés aux forks) lui ferme ces portes.

🧭 Sur FormaCampus — La CI/CD de FormaCampus est traitée comme un système de production : permissions minimales par job (lecture seule par défaut), secrets dans le store chiffré (jamais logués, portée par environnement, non exposés aux PR de forks), et accès au cloud/registre via OIDC (credentials temporaires, aucune clé long-lived stockée). Les actions tierces et images de base sont épinglées à des digests. Le pipeline exécute npm ci/composer install (Partie 12), lance npm audit/composer audit (bloquant) et scanne les images avec Trivy (bloquant sur CVE critique). Les images sont signées (provenance) et la vérification a lieu au déploiement. La branche de prod est protégée (revue obligatoire). Ainsi, empoisonner ce que FormaCampus déploie exigerait de franchir plusieurs contrôles indépendants.

✏️ Exercices

Exercice 1 — OIDC vs clé statique. Un pipeline déploie sur le cloud avec une clé d’accès cloud stockée en secret de CI (valable indéfiniment). Pourquoi est-ce risqué, et que propose l’OIDC ?

✅ Solution

Risque : une clé statique à longue durée stockée en secret de CI est une cible de forte valeur qui, si elle fuite (dépendance vérolée exécutée en CI, log accidentel, workflow exposé à une PR de fork), donne à l’attaquant un accès cloud durable (valable jusqu’à révocation manuelle) — potentiellement des mois. Elle doit être rotationnée manuellement et traîne dans le store de secrets. OIDC propose une fédération d’identité : le pipeline prouve son identité au cloud via un jeton OIDC signé émis par la plateforme CI, et reçoit des credentials temporaires à courte durée, avec une portée restreinte au job précis. Avantages : aucune clé statique à stocker ni à rotationner, et rien de durable à voler (les credentials expirent en minutes). Même si un attaquant capte un credential, sa fenêtre est minuscule. C’est la direction recommandée quand la plateforme le permet.

Exercice 2 — Durcis le pipeline. Cite trois mesures pour réduire le risque qu’une dépendance vérolée compromette ta CI et exfiltre tes secrets.

✅ Solution

(1) Droits minimaux + secrets à portée limitée : ne pas exposer les secrets sensibles (surtout de prod) aux jobs qui n’en ont pas besoin, ni aux workflows déclenchés par des PR de forks non fiables ; permissions du jeton par défaut en lecture seule. Ainsi, un postinstall malveillant qui s’exécute en CI a peu à voler. (2) Installation verrouillée (npm ci/composer install depuis le lockfile, Partie 12) et éventuellement --ignore-scripts / builds isolés (conteneur aux droits minimaux) pour limiter ce qu’un script d’install peut faire. (3) OIDC au lieu de clés statiques (credentials temporaires — rien de durable à exfiltrer) et épingler les actions tierces/images à des digests (une action compromise en tag mutable n’entre pas). Bonus : scan de secrets/dépendances en CI, et alerte sur les accès anormaux. Ces mesures cloisonnent ce qu’une dépendance vérolée peut atteindre dans le pipeline.

🧠 Quiz de révision

1. Pourquoi la CI/CD est-elle une cible critique ?

Parce qu’elle a accès à tout : le code, les secrets (déploiement, cloud, registres) et la prod. La compromettre permet d’injecter du code dans les artefacts déployés (atteindre tous les utilisateurs) et d’exfiltrer les secrets. C’est plus grave qu’une faille dans un endpoint.

2. Comment gère-t-on les secrets de CI ?

Dans le store de secrets chiffré de la plateforme (jamais en clair dans le YAML), jamais logués, à portée limitée (par usage, par environnement protégé), et non exposés aux workflows de PR de forks non fiables. Idéalement, éviter les secrets long-lived au profit de l’OIDC.

3. Quel est l’avantage de l’OIDC sur une clé cloud long-lived ?

L’OIDC fournit au pipeline des credentials temporaires à courte durée (via une identité fédérée), sans stocker de clé statique. Résultat : rien de durable à voler (expiration rapide), pas de clé à rotationner, portée restreinte. Une clé long-lived fuitée donne au contraire un accès durable.

4. Pourquoi épingler les actions tierces et images à un digest ?

Parce qu’un tag mutable (@v3) peut être modifié : une action tierce compromise exécuterait son code malveillant dans ta CI. Épingler à un digest (empreinte immuable) garantit d’exécuter exactement la version vérifiée — c’est l’intégrité de supply chain (Partie 12) appliquée au pipeline.

5. Que fait le scan d’images et comment l’intégrer ?

Il détecte les vulnérabilités connues (CVE) des paquets embarqués dans une image de conteneur (Trivy, Grype…). On l’intègre à la CI de façon bloquante (échec sur CVE critique), comme npm audit, et on privilégie des images minimales reconstruites régulièrement pour intégrer les correctifs.


Fin de la Partie 14. L’infra est durcie : transport et en-têtes au proxy, rate limiting/WAF/fail2ban contre l’abus, isolation et moindre privilège pour borner l’impact, CI/CD sécurisée. Reste à voir les attaques et à réagir : la Partie 15 — Détection & réponse à incident.

Last updated on