Aller au contenu

Images Docker de base et confiance héritée

Une image de base Docker n’est pas un détail de Dockerfile : elle détermine une part massive du code exécuté, des bibliothèques chargées et des vulnérabilités héritées. Le compromis central est brutal : réutiliser une base réduit le travail d’assemblage, mais délègue une partie de la sécurité à une chaîne externe qu’il faut pouvoir identifier, vérifier et remplacer.

→ Lire l’explication complète de la marmotte

Une image conforme à OCI (Open Container Initiative) est un assemblage de couches immuables référencées par leur condensat cryptographique, ou digest. Chaque couche représente une différence de système de fichiers : ajout d’un binaire, installation d’une bibliothèque, suppression d’un fichier. Une image finale référence un manifeste, qui référence les couches et sa configuration d’exécution.

Cette structure produit une généalogie, pas une provenance automatiquement fiable. Une image FROM python:3.12 hérite du système de base choisi par le mainteneur Python, de ses paquets, de leurs mainteneurs, du registre qui les distribue et de la politique de mise à jour associée. Ajouter trois lignes dans un Dockerfile ne réduit pas cette dépendance. Cela ajoute trois lignes au sommet d’une pile dont le fond reste actif dans chaque conteneur.

Le tag est un pointeur mutable. alpine:3.20, node:22 ou latest peuvent désigner des manifestes différents selon la date, l’architecture et la politique du registre. Un déploiement construit à partir d’un tag non figé n’est pas reproductible : deux reconstructions peuvent produire des exécutables distincts sans modification du dépôt. Le mot latest est particulièrement honnête sur ce point : il signifie seulement « quelqu’un a déplacé le panneau récemment ».

La référence par digest fixe l’identité exacte :

registry.example/base/runtime@sha256:<digest-du-manifeste>

Le digest protège contre la substitution silencieuse après sélection de l’image. Il ne prouve ni l’identité de l’auteur, ni la qualité du code, ni l’absence de vulnérabilité. Un contenu malveillant peut parfaitement avoir un digest valide. Le hash constate une identité, il ne décerne pas un diplôme de vertu.

La frontière de confiance doit donc être explicitée à trois niveaux :

  1. Le registre : contrôle d’accès, protection contre l’écrasement de tags, journalisation et disponibilité.
  2. Le producteur : identité de signature, processus de construction, source des paquets et revue des changements.
  3. Le contenu : composants embarqués, droits d’exécution, configuration par défaut et vulnérabilités connues.

Une image officielle réduit généralement l’incertitude sur le producteur, sans supprimer les deux autres niveaux. Pour les charges exposées, la recommandation est de ne consommer que des images issues d’un registre sous contrôle ou recopiées dans un registre interne, signées et référencées par digest. Tirer directement Internet en production transforme le registre public en dépendance d’exécution. C’est un choix d’architecture, pas une commodité innocente.

Cette discipline complète les règles d’architecture cyber : l’identité du composant exécuté doit être vérifiable avant de discuter de son durcissement.

Images de base, surface d’attaque et vulnérabilités héritées

Section intitulée « Images de base, surface d’attaque et vulnérabilités héritées »

Une base riche, telle qu’une distribution généraliste, apporte un gestionnaire de paquets, un shell, des certificats, des bibliothèques de diagnostic et parfois des compilateurs. Elle réduit le coût de construction et facilite le dépannage. Elle augmente aussi le nombre de binaires exécutables, de bibliothèques chargeables et de CVE (Common Vulnerabilities and Exposures) potentiellement présentes.

Réduire la taille ne garantit pas la réduction du risque. Une image minimale peut supprimer le shell et les outils de diagnostic, mais conserver une bibliothèque vulnérable réellement chargée par l’application. À l’inverse, une vulnérabilité dans un utilitaire absent du chemin d’exécution peut gonfler un tableau de bord sans ouvrir de chemin d’attaque réaliste. Le scanner compte des composants, pas des scénarios d’exploitation. Les deux informations sont nécessaires.

Trois familles d’attaques doivent guider le choix de la base :

  • Exploitation d’une bibliothèque héritée : une bibliothèque de compression, de chiffrement ou de traitement d’image vulnérable devient atteignable si l’application traite des données contrôlées par un attaquant. La contre-mesure est la mise à jour de la base, puis la reconstruction de l’image applicative. Redémarrer un conteneur avec une image ancienne ne corrige strictement rien.
  • Évasion post-compromission : un shell, curl, un interpréteur ou un gestionnaire de paquets facilitent le téléchargement d’outils et la reconnaissance locale après exploitation. Une base minimale réduit cette capacité, mais ne remplace ni un système de fichiers en lecture seule, ni l’absence de privilèges, ni les restrictions réseau.
  • Substitution de dépendance : un tag déplacé, un compte éditeur compromis ou un registre empoisonné peut diffuser une image différente. Les digests, les signatures et une politique d’admission bloquante répondent à ce risque. Un scan après déploiement, lui, arrive au mariage après l’incendie.

La recommandation est de choisir la base la plus étroite qui supporte réellement le runtime, puis de documenter pourquoi chaque couche existe. Une image distroless, sans distribution interactive complète, convient à un binaire autonome ou à un runtime bien maîtrisé. Elle devient pénible lorsqu’une application installe des extensions au démarrage, invoque un shell, ou dépend de certificats ajoutés à la volée. Dans ce cas, une base minimale maintenue vaut mieux qu’une image théoriquement pure mais exploitée au tournevis.

Les images scratch méritent une prudence supplémentaire. Elles éliminent presque tout, y compris les certificats racines, les fichiers de zone horaire, les bibliothèques dynamiques et les comptes système. Elles conviennent aux binaires statiquement liés qui n’ont besoin d’aucun de ces éléments. Elles ne rendent pas un binaire sûr. Un binaire vulnérable dans scratch reste vulnérable, mais dans un décor très vide.

La composition logicielle doit être livrée sous forme de SBOM (Software Bill of Materials), inventaire machine-readable des composants, versions et relations de dépendance. Une SBOM permet de rechercher quelles images contiennent une bibliothèque donnée après la publication d’une CVE. Elle ne garantit pas que l’inventaire est exact, ni qu’un composant est exploitable, ni que l’image en cours d’exécution correspond à l’artefact analysé. Pour cette dernière propriété, il faut lier digest, attestation et déploiement.

Référencement par digest et exemple de construction

Section intitulée « Référencement par digest et exemple de construction »

Le bon modèle consiste à séparer la découverte d’une nouvelle base de sa consommation. Une chaîne automatisée peut surveiller un tag approuvé, scanner et valider le nouveau manifeste, puis ouvrir une modification qui remplace explicitement le digest dans le dépôt. Le changement devient révisable, traçable et annulable. L’actualisation automatique directe du tag dans les environnements critiques est une loterie avec un logo de robot.

L’exemple suivant récupère l’image locale associée à un tag, extrait son digest, puis construit une image qui référence cette identité immuable. Les commandes nécessitent Docker avec BuildKit activé et un accès au registre Docker Hub.

Fenêtre de terminal
# Télécharger le tag choisi après validation de sa provenance.
docker pull alpine:3.20
# Lire la référence immuable enregistrée localement après le pull.
BASE_REF="$(docker image inspect alpine:3.20 \
--format '{{index .RepoDigests 0}}')"
# Générer un Dockerfile dont la base est figée par digest.
cat > Dockerfile <<EOF
ARG BASE_REF
FROM \${BASE_REF}
RUN adduser -D -H -s /sbin/nologin app
USER app
CMD ["id"]
EOF
# Construire sans réintroduire de tag mutable dans la résolution de base.
docker build --pull=false \
--build-arg "BASE_REF=${BASE_REF}" \
--tag exemple/base-fixee:local .

--pull=false évite que le moteur cherche une autre base pendant la construction. Cette option ne remplace pas le contrôle du cache de build : un cache partagé doit être considéré comme une dépendance de chaîne d’approvisionnement. Si un cache peut injecter des couches sans preuve de provenance, le digest de la base n’empêche pas l’injection dans les étapes suivantes.

Le Dockerfile doit aussi éviter d’installer des paquets sans version. apk add curl ou apt-get install curl laisse le résultat dépendre du miroir et de l’instant de build. Verrouiller les versions réduit cette variabilité, mais impose une stratégie de mise à jour : un verrou non révisé devient un musée de vulnérabilités. La reproductibilité sans maintenance est seulement une forme plus élégante d’abandon.

Signatures, attestations et politiques d’admission

Section intitulée « Signatures, attestations et politiques d’admission »

Une signature relie un digest à une identité de producteur. Avec Sigstore Cosign, par exemple, une signature peut être vérifiée contre une identité attendue et une autorité de certification configurée. La vérification doit porter sur le digest exact déployé, pas sur un tag résolu avant la vérification. Vérifier runtime:stable, puis déployer ce même tag plus tard, laisse une fenêtre de substitution.

Une attestation de provenance décrit comment un artefact a été construit : dépôt source, révision, système de build, paramètres et parfois résultats de contrôles. Sa valeur dépend de la protection de l’identité de build et de la politique de vérification. Une attestation générée par un processus compromis décrit très correctement un compromis. La cryptographie ne transforme pas une forge ouverte en usine fiable.

La politique d’admission du cluster doit refuser les images qui ne respectent pas les critères définis : registre autorisé, digest obligatoire, signature valide, éventuellement SBOM ou provenance requise. Kubernetes ne fait pas cette vérification par défaut. Sans contrôleur d’admission, une règle écrite dans une procédure reste une suggestion décorative, au même rang que les mots de passe dans un fichier Excel chiffré par optimisme.

Ces contrôles ne garantissent pas :

  • l’absence de vulnérabilité inconnue ;
  • l’absence de comportement malveillant dans une dépendance légitime ;
  • la sécurité du code applicatif ajouté au-dessus de la base ;
  • la sécurité du noyau hôte ou du runtime de conteneur ;
  • l’isolement réseau, l’autorisation applicative ou la protection des secrets.

Les secrets ne doivent jamais entrer dans une couche d’image, y compris via ARG, un fichier supprimé dans une étape ultérieure ou une variable exportée pendant le build. L’historique et le cache peuvent conserver ces valeurs. Les injecter au démarrage depuis un gestionnaire de secrets, avec une identité de charge de travail, limite l’exposition mais impose rotation, audit et gestion des indisponibilités. Les principes de cryptographie appliquée restent les mêmes : une clé exposée dans un artefact distribué n’est plus un secret, même si le registre demande un mot de passe.

Une image de base est un actif à maintenir. Il faut inventorier les images déployées par digest, leur associer leur SBOM, connaître leurs propriétaires techniques et définir un délai de reconstruction après vulnérabilité critique. Le délai doit inclure la validation fonctionnelle, car une mise à jour de bibliothèque peut casser un protocole, une autorité de certification ou une extension native. La sécurité qui interdit le redémarrage n’a pas gagné grand-chose.

La rotation suit une séquence contrôlée : détecter une nouvelle base, évaluer les composants modifiés, reconstruire les descendants, scanner l’artefact final, signer, déployer progressivement, puis conserver l’ancien digest pour un retour arrière borné dans le temps. Conserver indéfiniment toutes les bases annule la stratégie de retrait et augmente la surface de récupération d’artefacts compromis.

La supervision doit détecter au minimum les pulls depuis des registres non autorisés, les images lancées par tag, les échecs de vérification de signature et les écarts entre digest déclaré et digest exécuté. Les journaux du registre, du moteur de build et de l’orchestrateur doivent converger vers un identifiant commun : le digest. Un nom d’image est un libellé ; le digest est la pièce d’identité.

La reprise impose une copie des images approuvées dans un registre restaurable, accompagnée des signatures, attestations, SBOM et métadonnées de politique. Sauvegarder uniquement le dépôt source ne suffit pas : la reconstruction peut devenir impossible si un paquet, un miroir ou une image amont disparaît. Le dépôt source explique l’intention ; l’artefact figé permet de redémarrer.

Sans cette discipline, la porte de la ruche reste ouverte au premier manifeste qui passe. L’absence de référence par digest rend impossible de répondre à la question la plus banale après la publication d’une faille : quelles charges exécutent réellement cette bibliothèque, et depuis quand. Les journaux du registre, du build et de l’orchestrateur ne se recoupent plus, puisque seul un libellé mutable les relie, ce qui allonge le périmètre d’une analyse post incident à l’ensemble du parc. La préproduction et la production peuvent diverger sans qu’aucun commit ne l’explique, et un retour arrière devient une reconstruction au jugé. Faute de copie maîtrisée des artefacts approuvés, la disparition d’un paquet, d’un miroir ou d’une image amont suffit à rendre la remise en service dépendante d’un tiers extérieur.

Une image de base est le point de départ utilisé pour fabriquer un programme emballé afin de l’exécuter dans un conteneur. Elle contient déjà un système réduit, des fichiers et souvent des programmes nécessaires au fonctionnement de l’application. Quand une nouvelle image est construite, elle ajoute des fichiers par-dessus cette base. Les défauts présents dans la base restent donc présents dans le résultat final.

Chaque version d’image peut être désignée par un nom qui change avec le temps, ou par une empreinte calculée à partir de son contenu. Le nom pratique peut être déplacé vers une autre version sans prévenir. L’empreinte désigne toujours le même contenu. Pour reconstruire le même programme et éviter un remplacement discret, il faut enregistrer cette empreinte.

Avant d’accepter une image, il faut vérifier son origine, son contenu et les défauts connus de ses composants. Une signature permet de vérifier quel producteur a publié un contenu donné. Un inventaire des composants permet de rechercher rapidement les images touchées lorsqu’une faille est annoncée. Ces contrôles ne rendent pas le programme inattaquable : ils ne corrigent pas une erreur dans le code de l’application, ni un défaut du système qui héberge les conteneurs.

Quand une faille apparaît, la base doit être remplacée, toutes les images qui en dépendent doivent être reconstruites, puis les anciennes versions doivent être retirées. Les éléments nécessaires au redémarrage, y compris les images validées et leurs preuves d’origine, doivent rester disponibles dans un stockage maîtrisé.

À lire aussi

Pages voisines

: les deux pages se citent l’une l’autre.