1. Pourquoi durcir l'image de base ? #
Ce que contient vraiment une image.
Le constat #
Une image hérite de tout ce que contient son image de base : shell, gestionnaire de paquets, bibliothèques. Chaque paquet est une vulnérabilité potentielle et un outil de plus pour un attaquant qui aurait pris pied dans le conteneur. Nous avons scanné deux images avec Trivy :
| Image | Vulnérabilités HIGH et CRITICAL | Avec correctif disponible |
|---|---|---|
| Image PHP sur Alpine 3.18, construite en 2023, jamais reconstruite | 23 (3 CRITICAL) | 23 : toutes corrigées depuis |
| nginx officielle 1.30.6 (Debian), novembre 2026 | 71 (1 CRITICAL) | 2 seulement |
Deux leçons : une image non reconstruite accumule des failles déjà corrigées en amont, et une image complète traîne des dizaines de vulnérabilités sans correctif, qu'un scanner remontera sans que vous puissiez agir. La solution : partir d'une base minimale et reconstruire souvent.
2. Les familles d’images #
Du plus simple au plus durci.
Comparatif #
| Famille | Exemple | Atouts | Limites |
|---|---|---|---|
| -slim | node:22.11-bookworm-slim | Compatible, moins de paquets | Shell et apt toujours présents |
| Alpine | python:3.13-alpine | Très petite | musl : incompatibilités possibles avec certaines bibliothèques |
| Distroless (Google) | gcr.io/distroless/nodejs22-debian12:nonroot | Pas de shell ni de gestionnaire de paquets, variante nonroot | Débogage moins direct |
| Docker Hardened Images | dhi.io/… | Plus de 1 000 images Debian et Alpine, libres (Apache 2.0) depuis décembre 2025, SBOM et attestations | Connexion Docker Hub requise ; FIPS et support étendu payants |
| Chainguard | cgr.dev/chainguard/… | Images minimales reconstruites en continu | Versions anciennes réservées à l’offre payante |
Le Dockerfile durci #
Le principe : construire dans une image complète, puis copier seulement le résultat dans une image minimale, exécutée sans root.
# syntax=docker/dockerfile:1
FROM node:22.11-bookworm-slim AS build
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
COPY . .
FROM gcr.io/distroless/nodejs22-debian12:nonroot
WORKDIR /app
COPY --from=build /app /app
USER 65532:65532
EXPOSE 3000
CMD ["server.js"]- Versions fixées : jamais
latest, pour des constructions reproductibles. - Multi-stage : les outils de construction restent dans l'étape
build. - Utilisateur numérique :
USER 65532:65532, que Kubernetes peut vérifier avecrunAsNonRoot.
3. Exploiter une image sans shell #
Déboguer et maintenir.
Déboguer #
Sans shell, docker exec -it … sh ne fonctionne plus. Utilisez un conteneur de débogage qui partage les espaces de noms :
docker run --rm -it --pid=container:app --network=container:app busybox:1.37 sh
# Kubernetes
kubectl debug -it pod/app --image=busybox:1.37 --target=appReconstruire régulièrement. Une image durcie vieillit comme les autres : reconstruisez-la au moins chaque semaine, et automatisez la mise à jour de l'image de base (Renovate, Dependabot).
