Aller au contenu
medium

Supply chain des images : SBOM, scan, signature et policies

18 min de lecture

Vous buildez des images, mais savez-vous vraiment ce qu'elles contiennent ? Une image peut embarquer des centaines de dépendances, dont certaines avec des vulnérabilités connues. Ce guide vous donne les bases DevSecOps pour sécuriser votre supply chain d'images : SBOM, scan de vulnérabilités, signatures et politiques. Une introduction pragmatique pour savoir par où commencer.

  • SBOM : la liste d'ingrédients de votre image (et pourquoi c'est obligatoire dans certains contextes)
  • Scan de vulnérabilités : détecter les CVE dans vos images
  • Signature d'images : prouver l'origine et l'intégrité
  • Policies : appliquer des règles automatiques (pas d'image root, pas de CVE critique)
  • Où placer ces contrôles dans la CI/CD

Quatre contrôles se répartissent le travail, et la confusion la plus fréquente est de croire que l'un remplace l'autre. Chacun répond à une question différente : ce que contient l'image, si elle est vulnérable, qui l'a produite, et si elle respecte vos règles. Lisez la colonne du milieu, elle vous dit exactement ce que chaque outil sait faire, et par déduction ce qu'il ne couvre pas.

ContrôleQuestion à laquelle il répondExemple
SBOM"Qu'y a-t-il dans mon image ?"Liste des 234 packages avec versions
Scan"Y a-t-il des vulnérabilités connues ?"CVE critique dans OpenSSL
Signature"Qui a produit cette image ? A-t-elle été modifiée ?"Certificat lié à ci@monorg.com
Policy"Cette image respecte-t-elle nos règles ?"Bloque si non signé ou CVE critique

Cinq termes reviennent dans tout le guide et conditionnent la compréhension du reste. Les deux premiers, tag et digest, portent l'essentiel : confondre l'alias mutable et le hash immuable est la source d'erreur numéro un en production. Gardez ce tableau sous la main, les sections suivantes s'appuient sur ces définitions sans les répéter.

TermeDéfinition simple
TagAlias mutable (nginx:1.25). Peut pointer vers une image différente demain.
DigestHash immuable (sha256:abc123...). Garantit exactement la même image.
AttestationArtefact OCI attaché à l'image (signature, SBOM, provenance).
KeylessSignature sans clé privée à gérer : votre identité OIDC (GitHub, Google) + certificat éphémère.
VEXDocument indiquant si une CVE est réellement exploitable dans votre contexte.

Quand vous faites FROM python:3.11, vous héritez de :

  • L'image de base Debian (ou Alpine)
  • Des centaines de packages système
  • Python et ses dépendances C
  • Tout ce que les mainteneurs ont mis dedans

Puis votre pip install ajoute :

  • Vos dépendances Python
  • Les dépendances de vos dépendances (dépendances transitives)
  • Potentiellement des packages compromis (typosquatting, mainteneur malveillant)

Résultat : votre image de 200 Mo contient peut-être des milliers de composants dont vous ignorez l'existence, et certains peuvent avoir des vulnérabilités connues (CVE).

Un SBOM (Software Bill of Materials) est un inventaire exhaustif de tous les composants d'un logiciel : packages système, bibliothèques, versions, licences.

Le format n'est pas neutre : il oriente l'usage que vous ferez du SBOM. Retenez la ligne du milieu : CycloneDX est porté par l'OWASP et pensé pour la sécurité, c'est celui qu'attendent les scanners de vulnérabilités. SPDX, standardisé par la Linux Foundation et normalisé ISO, domine côté conformité et licences. Choisissez selon le consommateur du fichier, pas par préférence.

FormatStandardUtilisateurs typiques
SPDXLinux FoundationCompliance, licences
CycloneDXOWASPSécurité, vulnérabilités
Syft JSONAnchoreUsage interne

Syft (Anchore) analyse une image sans l'exécuter et produit le SBOM dans le format demandé par -o. La commande accepte directement une référence de registre, elle télécharge l'image, inspecte chaque couche et recense les paquets. Vérifiez le résultat avec le jq de la dernière ligne : un décompte de zéro paquet signale presque toujours une image scratch ou une erreur de référence, pas une image réellement vide.

Fenêtre de terminal
# SBOM au format SPDX (ISO standard)
syft nginx:1.25 -o spdx-json > sbom.json
# SBOM au format CycloneDX (orienté sécurité)
syft nginx:1.25 -o cyclonedx-json > sbom.json
# Voir le contenu
cat sbom.json | jq '.packages | length'
# 234 packages trouvés

Un scanner de vulnérabilités compare les composants de votre image à des bases de données de CVE (Common Vulnerabilities and Exposures).

Ces trois scanners interrogent des bases de CVE proches mais pas identiques, d'où des résultats qui divergent légèrement d'un outil à l'autre sur une même image. Trivy couvre le plus large périmètre, images, IaC, secrets et VEX, ce qui en fait le choix par défaut. Ne vous attendez pas à un décompte strictement identique entre deux scanners : c'est normal, chacun a sa cadence de mise à jour et ses sources.

OutilTypeQuand l'utiliser
TrivyCLI open sourceChoix par défaut : rapide, gratuit, SBOM + VEX + IaC
GrypeCLI open sourceAlternative légère, intégration native avec Syft
Docker ScoutDockerSi vous utilisez Docker Desktop (intégré)

Le drapeau à connaître pour la CI est --exit-code 1 : sans lui, Trivy affiche les vulnérabilités mais rend toujours un code de succès, et votre pipeline laisse passer une image critique sans broncher. Combinez-le avec --severity pour ne bloquer que sur ce qui compte, et réservez .trivyignore aux CVE dont vous avez explicitement établi qu'elles ne vous concernent pas, avec une trace de cette décision.

Fenêtre de terminal
# Scan basique
trivy image nginx:1.25
# Seulement les CVE critiques et hautes
trivy image --severity CRITICAL,HIGH nginx:1.25
# Échouer si CVE critique (utile en CI)
trivy image --exit-code 1 --severity CRITICAL nginx:1.25
# Ignorer certaines CVE (avec fichier .trivyignore)
echo "CVE-2023-12345" > .trivyignore
trivy image --ignorefile .trivyignore nginx:1.25

Dans ce rapport, la ligne de synthèse en tête donne le décompte par sévérité : c'est elle que votre --severity CRITICAL filtre. Descendez ensuite à la colonne Fixed Version du tableau, la seule réellement actionnable : quand elle est renseignée, une simple mise à jour du paquet corrige la faille ; quand elle est vide, aucun correctif n'est encore publié et il faut décider entre attendre, contourner ou changer d'image de base.

nginx:1.25 (debian 12.1)
=========================
Total: 42 (UNKNOWN: 0, LOW: 20, MEDIUM: 18, HIGH: 3, CRITICAL: 1)
┌──────────────────┬────────────────┬──────────┬─────────────────────┐
│ Library │ Vulnerability │ Severity │ Fixed Version │
├──────────────────┼────────────────┼──────────┼─────────────────────┤
│ openssl │ CVE-XXXX-YYYY │ CRITICAL │ 3.0.12-1 │
│ curl │ CVE-XXXX-ZZZZ │ HIGH │ 7.88.1-10+deb12u1 │
└──────────────────┴────────────────┴──────────┴─────────────────────┘

VEX (Vulnerability Exploitability eXchange) indique si une CVE est réellement exploitable dans votre contexte. Exemple : une CVE dans OpenSSL peut ne pas vous concerner si vous n'utilisez pas la fonction vulnérable.

Fenêtre de terminal
# Trivy supporte OpenVEX pour filtrer les faux positifs
trivy image --vex ./vex.json myimage:latest

Le format OpenVEX (OpenSSF) est le standard recommandé. Il peut être attaché comme attestation OCI à côté du SBOM. Pour la génération de fichiers VEX, consultez la documentation Trivy sur VEX.

Scanner détecte les vulnérabilités connues, mais pas les images compromises (backdoor, modification malveillante). La signature résout ce problème.

Ce tableau oppose deux mondes pour rendre concret ce que vous gagnez. L'apport central tient dans la dernière ligne : une signature ne se contente pas d'attester une origine à un instant T, elle invalide automatiquement dès qu'un octet de l'image change. Une image modifiée après signature devient détectable, ce que ni un tag ni un simple registre privé ne garantissent.

Sans signatureAvec signature
N'importe qui peut push myapp:1.0Seul le détenteur de la clé peut signer
Pas de preuve d'origineVérification cryptographique de l'éditeur
Modification silencieuse possibleToute modification invalide la signature

Cosign (projet Sigstore) est l'outil standard pour signer des images OCI.

Fenêtre de terminal
# Mode clé : générer une paire de clés
cosign generate-key-pair
cosign sign --key cosign.key ghcr.io/mon-org/mon-app:1.0.0
cosign verify --key cosign.pub ghcr.io/mon-org/mon-app:1.0.0
# Mode keyless (recommandé en CI/CD) : identité OIDC + certificat éphémère
cosign sign ghcr.io/mon-org/mon-app:1.0.0
cosign verify \
--certificate-identity=ci@monorg.com \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com \
ghcr.io/mon-org/mon-app:1.0.0

Les policies (ou admission policies) bloquent le déploiement d'images non conformes. Elles peuvent vérifier :

  • L'image est signée
  • Aucune CVE critique
  • L'image vient d'un registry autorisé
  • L'image n'utilise pas root
  • Un SBOM est attaché

La distinction utile de ce tableau est le niveau où s'applique la règle. OPA/Gatekeeper et Kyverno sont des contrôleurs d'admission Kubernetes : ils tranchent au moment où un Pod est créé, donc au dernier rempart avant l'exécution. Conftest valide en amont, dès la CI, sur des fichiers. Les deux approches se combinent : filtrer tôt en CI, refuser au déploiement ce qui aurait échappé au premier filet.

OutilNiveauCas d'usage
OPA/GatekeeperKubernetesAdmission controller
KyvernoKubernetesPolicies déclaratives
ConnaisseurRegistry/K8sVérification signatures
ConftestCIValidation locale

Cette politique Kyverno refuse tout Pod dont l'image ne porte pas une signature Cosign valide. Deux champs déterminent son périmètre : matchImageReferences restreint la règle aux images d'un registre donné (ici ghcr.io/mon-org/*), et validationActions: [Deny] en fait un blocage ferme, pas un simple avertissement. Passez cette dernière valeur en mode audit pendant la phase de rodage avant de basculer sur Deny, sinon vous risquez de figer les déploiements légitimes non encore signés.

# Kyverno ImageValidatingPolicy : bloquer les images non signées
apiVersion: policies.kyverno.io/v1
kind: ImageValidatingPolicy
metadata:
name: verify-image-signature
spec:
validationActions: [Deny]
matchConstraints:
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["pods"]
matchImageReferences:
- glob: "ghcr.io/mon-org/*"
attestors:
- name: cosign-key
cosign:
key:
data: |-
-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----
validations:
- expression: "true"
message: "Image must be signed with our Cosign key."

Les tags sont mutables : nginx:1.25 peut pointer vers une image différente demain (mise à jour du mainteneur, compromission). Le digest est immuable : sha256:abc123... garantit exactement la même image.

# ❓ Risqué : le tag peut changer
image: nginx:1.25@sha256:a484819eb60211f5299034ac80f6a681b06f89e65866ce91f356ed7c72af059c
# ✅ Sûr : le digest est immuable
image: nginx@sha256:6af79ae5de407283dcea8b00d5c37ace95441fd58a8b1d2aa1ed93f5511bb18c

Le choix de l'image de base conditionne la majorité de votre surface d'attaque, car vous héritez de tous ses paquets avant même d'écrire une ligne. Le levier le plus efficace est la réduction : une image durcie ou distroless embarque un minimum de composants, donc expose un minimum de CVE et n'offre ni shell ni gestionnaire de paquets à un attaquant. Adossez ce choix à un registre interne qui rescanne en continu les images approuvées.

  • Docker Official Images : images maintenues par Docker, auditées régulièrement.
  • Images durcies : Chainguard, Wolfi, distroless (moins de packages = moins de surface d'attaque).
  • Registry interne : miroir des images approuvées, avec scan automatique.

La signature prouve l'origine, mais pas comment l'image a été construite. Les attestations de provenance (SLSA) décrivent le pipeline de build : quel code source, quel Dockerfile, quel runner CI.

Fenêtre de terminal
# Générer une attestation de provenance (build GitHub Actions)
cosign attest --predicate provenance.json ghcr.io/mon-org/mon-app:1.0.0
# Vérifier l'attestation
cosign verify-attestation --type slsaprovenance ghcr.io/mon-org/mon-app:1.0.0

L'ordre de ces contrôles n'est pas arbitraire : chacun consomme le résultat du précédent. Le SBOM se génère au build et ne bloque jamais, il documente. Le scan est le premier point de décision, celui qui refuse une image vulnérable. La signature ne s'applique qu'à une image déjà validée, sans quoi vous authentifieriez du code défaillant. La vérification au déploiement ferme la boucle. Le schéma ci-dessous replace ces étapes dans le pipeline.

Pipeline supply chain : Build → SBOM → Scan → Sign → Deploy
ÉtapeOutilAction si échec
BuildSyftGénérer SBOM (jamais d'échec)
ScanTrivyBloquer si CVE CRITICAL
SignCosignSigner l'image validée
DeployKyvernoBloquer si non signé

Vérifions que vous avez compris les bases de la sécurisation supply chain : SBOM, scan, signature et policies.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

7 questions
5 min.
80% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

  1. Générer un SBOM à chaque build, inventaire exhaustif, exigé par certaines réglementations.
  2. Scanner avant de push, détecte les CVE connues (Trivy recommandé).
  3. Filtrer avec VEX, réduit le bruit en indiquant l'exploitabilité réelle.
  4. Signer l'image validée, prouve l'origine et l'intégrité (Cosign/Sigstore).
  5. Vérifier au déploiement, policy Kyverno/OPA bloque les images non conformes.
  6. Déployer par digest, le tag est mutable, le digest est immuable.
  7. Utiliser des base images approuvées, Official Images ou images durcies.
  8. Pipeline : Build → SBOM → Scan → Sign → Policy → Deploy.
  9. Progressivité : alerter d'abord, bloquer ensuite.

Vous avez maintenant une vue d'ensemble de la supply chain des images. Pour approfondir, explorez les outils individuels ou passez à l'orchestration avec Docker Compose ou Kubernetes.

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn