Aller au contenu

Des images 10× plus petites, 0 CVE : Chainguard change la donne

Publié le :

logo chainguard

Les images conteneurs traditionnelles transportent un héritage encombrant : shells, gestionnaires de paquets, outils de debugging. Cette complexité augmente la surface d’attaque et multiplie les vulnérabilités CVE. Face à ce constat, je me concentre ici sur les images Chainguard face aux images standards, pour un usage Kubernetes en production.

Problématique : images standards lourdes et risquées

Une image Debian ou Ubuntu standard contient des centaines de paquets dont la plupart ne servent jamais à l’exécution de votre application :

Terminal window
# Image traditionnelle nginx:latest : compter les paquets installés
docker run --rm nginx:latest dpkg -l | wc -l
# ~150 paquets listés (exemple)

Conséquences directes :

  • Surface d’attaque élargie : chaque paquet est une porte d’entrée potentielle
  • CVE en cascade : vulnérabilités dans des dépendances inutilisées
  • Taille excessive : images de plusieurs centaines de Mo
  • Temps de chargement long : plus de packages = plus de temps de chargement
Terminal window
# Taille de l'image nginx:latest
docker images nginx:latest
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 60adc2e137e7 2 weeks ago 152MB

On va utiliser trivy pour scanner les vulnérabilités CVE critiques dans l’image nginx:latest :

Terminal window
# Scanner les vulnérabilités avec Trivy
trivy image --table-mode summary --severity HIGH,CRITICAL nginx:latest
2025-12-06T09:43:55Z INFO [vuln] Vulnerability scanning is enabled
2025-12-06T09:43:55Z INFO [secret] Secret scanning is enabled
2025-12-06T09:43:55Z INFO [secret] If your scanning is slow, please try '--scanners vuln' to disable secret scanning
2025-12-06T09:43:55Z INFO [secret] Please see https://trivy.dev/docs/v0.68/guide/scanner/secret#recommendation for faster secret detection
2025-12-06T09:43:55Z INFO Detected OS family="debian" version="13.2"
2025-12-06T09:43:55Z INFO [debian] Detecting vulnerabilities... os_version="13" pkg_num=150
2025-12-06T09:43:55Z INFO Number of language-specific files num=0
2025-12-06T09:43:55Z WARN Using severities from other vendors for some vulnerabilities. Read https://trivy.dev/docs/v0.68/guide/scanner/vulnerability#severity-selection for details.
Report Summary
┌────────────────────────────┬────────┬─────────────────┬─────────┐
Target Type Vulnerabilities Secrets
├────────────────────────────┼────────┼─────────────────┼─────────┤
nginx:latest (debian 13.2) │ debian │ 4 │ - │
└────────────────────────────┴────────┴─────────────────┴─────────┘

Le build maison : une fausse bonne idée

Pour réduire la taille et les CVE, certains optent pour des images minimalistes faites maison (Alpine, Scratch, distroless). Mais cette approche complexifie la maintenance. Je dois en effet :

  • Maintenir des Dockerfiles de base et leurs mises à jour
  • Traiter les CVE: patch, rebuild, re-scan, repush
  • Gestion des dépendances système (apt/yum), pins et backports
  • Pipeline et cache d’images à entretenir (temps opérateur)

Une solution : les images Chainguard

Les images Chainguard annonce réduire drastiquement ces risques pour vous :

En production, je vise trois gains concrets :

  • réduire la surface d’attaque (moins de paquets = moins de CVE),
  • accélérer mes pipelines (images plus petites, caches plus stables),
  • renforcer la traçabilité (SBOM + attestations de provenance vérifiables).

Architecture et principes (cloud‑native)

Les images Chainguard sont conçues spécifiquement pour les conteneurs et répondent aux limites des images standards.

Principes de conception :

  • SBOM natif : Software Bill of Materials généré automatiquement
  • Rootless par défaut : principe de moindre privilège
  • Mises à jour rapides : patchs CVE en moins de 24h
  • APK package manager : emprunté à Alpine Linux
  • glibc moderne : compatibilité maximale
  • Provenance attestations : signatures cryptographiques SLSA

Concrètement, je ne change pas mon code : je remplace l’OS utilisateur par un runtime minimal sans shell par défaut, et j’obtiens un SBOM généré à la construction. Moins de « dépendances fantômes », des patchs plus prévisibles.

Images Chainguard disponibles

Chainguard propose +1000 images maintenues. Voici une liste incomplète de ce qu’on peut y trouver:

  • Base / Systèmes minimaux
    • static
    • busybox
    • glibc-dynamic
    • glibc-static
    • wolfi-base
    • gcc-glibc
  • Langages / Runtimes
    • python
    • node
    • ruby
    • go
    • jdk
    • jre
  • Outils de build / Toolchains
    • apko
    • melange
    • bazel
    • cmake
    • cargo
    • rust
  • CLI & Outils DevOps
    • kubectl
    • helm
    • kustomize
    • skopeo
    • cosign
    • gh
  • Serveurs & Middleware
    • nginx
    • caddy
    • haproxy
    • redis
    • postgres
    • prometheus
  • Sécurité / Observabilité
    • falco
    • trivy
    • tetragon
    • vector
    • node-exporter
    • promtail
  • Images pour Kubernetes
    • pause
    • coredns
    • etcd
    • kube-apiserver
    • kube-controller-manager
    • kube-proxy

Source: Directory Chainguard (pagination et filtres par catégories disponibles).

Mise en pratique des images Chainguard : exemple Python

Pour illustrer, je crée une simple application Python Flask. Voici le Dockerfile utilisant une image Chainguard Python.

# Build sur image Chainguard -dev (avec shell et tools)
FROM cgr.dev/chainguard/python:latest-dev AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Runtime images Chainguard
FROM cgr.dev/chainguard/python:latest
WORKDIR /app
COPY --from=builder /app/.local /app/.local
COPY app.py .
ENV PATH="/app/.local/bin:$PATH"
ENTRYPOINT ["python", "app.py"]

Le modèle économique Chainguard

Selon la page Pricing, les offres sont structurées en trois volets pour les Chainguard Containers :

  • Free Images : un sous-ensemble d’images gratuites (tags :latest), idéal pour tests/déploiements limités.
  • Per Image : licence par image (base, application, AI/ML, FIPS), avec CVE Remediation SLA contractuel et accès à tous les tags supportés en amont (ex: Python 3.10–3.14).
  • Catalog : accès complet au catalogue (~1700+ images), licence basée sur la taille de l’organisation, avec SLA CVE, demandes de nouvelles images/paquets et EOL grace period.

Je compare les coûts récurrents d’un build maison à l’abonnement Per Image/Catalog :

  • Maintenance Dockerfiles (temps ingénieur), suivi OS/dépendances, rebuild/retest → récurrence mensuelle.
  • SLA CVE interne: détection/patch sous 7–14 jours, coordination, re-scan, publication → effort soutenu et risqué.
  • SBOM + provenance: outillage, intégration CI, stockage, audit.
  • Images FIPS/STIG: coûts élevés de durcissement et conformité.

Constat pragmatique: pour des équipes qui gèrent plusieurs images et des exigences compliance (FIPS/STIG), l’abonnement Per Image ou Catalog remplace une part importante de la charge opérationnelle et réduit le temps de remédiation. Le Free tier suffit pour POC et tests, pas pour un parc production hétérogène avec SLA.

Allez on vérifie tout ça !

Je compare rapidement nginx:latest (standard) et cgr.dev/chainguard/nginx:latest.

On compare la taille, le nombre de paquets et les CVE.

Terminal window
# On récupère l'image chainguard NGINX
docker pull cgr.dev/chainguard/nginx:latest
# Comparer rapidement les tailles
docker images | grep -E "(^REPOSITORY|nginx\s|chainguard/nginx)"
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 60adc2e137e7 2 weeks ago 152MB
cgr.dev/chainguard/nginx latest 24a61bdb908b 2 weeks ago 16.9MB

On va utiliser syft pour lister les paquets et trivy pour scanner les CVE.

Le nombre de paquets installés :

Terminal window
# Image standard : lister les paquets installés (dpkg)
syft nginx:latest -q | wc -l
152
# Image Chainguard : pas de shell/dpkg → passer par le SBOM
syft cgr.dev/chainguard/nginx:latest -q | wc -l
16

Les vulnérabilités CVE critiques :

Terminal window
# Avec Trivy
trivy image --severity HIGH,CRITICAL cgr.dev/chainguard/nginx:latest
2025-12-06T09:51:06Z INFO [vuln] Vulnerability scanning is enabled
2025-12-06T09:51:06Z INFO [secret] Secret scanning is enabled
2025-12-06T09:51:06Z INFO [secret] If your scanning is slow, please try '--scanners vuln' to disable secret scanning
2025-12-06T09:51:06Z INFO [secret] Please see https://trivy.dev/docs/v0.68/guide/scanner/secret#recommendation for faster secret detection
2025-12-06T09:51:06Z INFO Detected OS family="wolfi" version="20230201"
2025-12-06T09:51:06Z INFO [wolfi] Detecting vulnerabilities... pkg_num=15
2025-12-06T09:51:06Z INFO Number of language-specific files num=0
Report Summary
┌──────────────────────────────────────────────────┬───────┬─────────────────┬─────────┐
Target Type Vulnerabilities Secrets
├──────────────────────────────────────────────────┼───────┼─────────────────┼─────────┤
cgr.dev/chainguard/nginx:latest (wolfi 20230201) │ wolfi │ 0 │ - │
└──────────────────────────────────────────────────┴───────┴─────────────────┴─────────┘

Attendus observés en pratique : moins de dépendances, moins de CVE et des tailles réduites côté images Chainguard.

On vérifie l’image python de la même façon :

Terminal window
# Image Python Chainguard
docker pull cgr.dev/chainguard/python:latest
docker pull python:3.11-slim
# Comparer les tailles
docker images | grep python
cgr.dev/chainguard/python latest 95ca247652bb 3 days ago 64MB
python 3.11-slim 040af88f5bce 2 weeks ago 124MB
# Vérifier les CVE
trivy image --table-mode summary --severity HIGH,CRITICAL python:3.11-slim
2025-12-06T10:01:58Z INFO [vuln] Vulnerability scanning is enabled
2025-12-06T10:01:58Z INFO [secret] Secret scanning is enabled
2025-12-06T10:01:58Z INFO [secret] If your scanning is slow, please try '--scanners vuln' to disable secret scanning
2025-12-06T10:01:58Z INFO [secret] Please see https://trivy.dev/docs/v0.68/guide/scanner/secret#recommendation for faster secret detection
2025-12-06T10:01:58Z INFO Detected OS family="debian" version="13.2"
2025-12-06T10:01:58Z INFO [debian] Detecting vulnerabilities... os_version="13" pkg_num=87
2025-12-06T10:01:58Z INFO Number of language-specific files num=1
2025-12-06T10:01:58Z INFO [python-pkg] Detecting vulnerabilities...
2025-12-06T10:01:58Z WARN Using severities from other vendors for some vulnerabilities. Read https://trivy.dev/docs/v0.68/guide/scanner/vulnerability#severity-selection for details.
Report Summary
┌──────────────────────────────────────────────────────────────────────────────────┬────────────┬─────────────────┬─────────┐
Target Type Vulnerabilities Secrets
├──────────────────────────────────────────────────────────────────────────────────┼────────────┼─────────────────┼─────────┤
python:3.11-slim (debian 13.2) │ debian │ 0 │ - │
├──────────────────────────────────────────────────────────────────────────────────┼────────────┼─────────────────┼─────────┤
trivy image --severity HIGH,CRITICAL cgr.dev/chainguard/python:latest
2025-12-06T09:57:43Z INFO [vuln] Vulnerability scanning is enabled
2025-12-06T09:57:43Z INFO [secret] Secret scanning is enabled
2025-12-06T09:57:43Z INFO [secret] If your scanning is slow, please try '--scanners vuln' to disable secret scanning
2025-12-06T09:57:43Z INFO [secret] Please see https://trivy.dev/docs/v0.68/guide/scanner/secret#recommendation for faster secret detection
2025-12-06T09:57:43Z INFO Detected OS family="wolfi" version="20230201"
2025-12-06T09:57:43Z INFO [wolfi] Detecting vulnerabilities... pkg_num=25
2025-12-06T09:57:43Z INFO Number of language-specific files num=0
Report Summary
┌───────────────────────────────────────────────────┬───────┬─────────────────┬─────────┐
Target Type Vulnerabilities Secrets
├───────────────────────────────────────────────────┼───────┼─────────────────┼─────────┤
cgr.dev/chainguard/python:latest (wolfi 20230201) │ wolfi │ 0 │ - │
└───────────────────────────────────────────────────┴───────┴─────────────────┴─────────┘

On voit le même pattern : l’image Chainguard est plus légère et sans CVE critiques.

Rootless ou pas ?

La plupart des images Chainguard sont conçues pour tourner en non-root par défaut. Je vérifie avec une simple inspection :

Terminal window
# Vérifier l'UID par défaut
docker inspect cgr.dev/chainguard/nginx:latest --format='{{.Config.User}}'
65532

L’UID 65532 correspond à l’utilisateur nobody dans l’image. Je m’assure donc que mes manifests Kubernetes utilisent des ports non privilégiés (>=1024) et runAsNonRoot: true dans les securityContext.

Vérification de la présence du SBOM

Chaque image Chainguard intègre un SBOM natif. Je peux l’extraire avec syft :

Terminal window
# Extraire le SBOM
syft cgr.dev/chainguard/nginx:latest -o table
Loaded image cgr.dev/chainguard/nginx:latest
Parsed image sha256:24a61bdb908b89d99d4a61dc5383a3d592c07a1f9ef9e94b5fdf1dfc5e7adf48
Cataloged contents 7f9cbf3d9edd78424a9a5097be27318b8d3545319e156d54e71f278043044dc2
├── Packages [15 packages]
├── File metadata [91 locations]
├── Executables [26 executables]
└── File digests [91 files]
NAME VERSION TYPE
ca-certificates-bundle 20251003-r0 apk
glibc 2.42-r4 apk
glibc-locale-posix 2.42-r4 apk
ld-linux 2.42-r4 apk
libcrypt1 2.42-r4 apk
libcrypto3 3.6.0-r4 apk
libgcc 15.2.0-r6 apk
libpcre2-8-0 10.47-r0 apk
libssl3 3.6.0-r4 apk
libxcrypt 4.5.2-r0 apk
nginx-mainline 1.29.3-r0 apk
nginx-mainline-config 1.29.3-r0 apk
nginx-mainline-package-config 1.29.3-r0 apk
wolfi-baselayout 20230201-r24 apk
zlib 1.3.1-r51 apk

Le SBOM liste les 15 paquets APK installés, bien moins qu’une image standard.

Signatures & provenance (adoption production)

Avant de basculer une image en production, on vérifie systématiquement les signatures et l’attestation de provenance. Objectif : m’assurer que le contenu provient d’une chaîne de build de confiance.

Terminal window
# Vérifier la signature Sigstore (cléless) avec issuer + identité attendue
cosign verify \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
--certificate-identity-regexp 'https://github.com/chainguard-images/.*' \
cgr.dev/chainguard/nginx:latest

Conclusion

En production, je veux réduire les risques tout en gagnant du temps. Les images standards cachent des coûts en CVE, taille et maintenance. À l’inverse, les images Chainguard me donnent un SBOM natif, des attestations de provenance, et un SLA de remédiation CVE qui stabilise mon runbook sécurité.

Quand basculer vers Chainguard ?

  • Je gère plusieurs images et je veux un SLA CVE (7–14 jours) sans charge opérationnelle lourde.
  • J’ai des exigences compliance (FIPS/STIG) ou des audits récurrents.
  • Je veux des images rootless par défaut et un catalogue large.

Méthode pragmatique (résumé en 4 étapes):

  • Remplacer l’image de base par sa variante Chainguard (NGINX, Python, etc.).
  • Vérifier rootless et ajuster securityContext (ports >=1024).
  • Intégrer SBOM + scan (syft/trivy) et cosign verify en CI.
  • Pinner par digest (@sha256:...) pour éliminer les tags mutables.

Ma règle simple: j’utilise Chainguard pour les environnements durables et audités; je garde des images standards pour des usages éphémères/POC ou des tests ciblés. Ainsi, je diminue la surface d’attaque sans ralentir mes déploiements.

Plus d’infos