Sécuriser des conteneurs revient à poser un contrôle à chacune des trois
étapes de leur vie : le build de l'image, son admission dans le cluster
et son exécution. Cette leçon s'adresse à un DevOps qui fait déjà tourner
des conteneurs et veut savoir quel contrôle placer où. Tout est mesuré sur
Kubernetes 1.37 et Docker 29.1.3 : l'image python:3.13 porte 4 122 CVE
contre 155 pour la même application en distroless, un namespace
restricted refuse un pod nginx par défaut avec quatre violations, et une
alerte Falco sort en quelques secondes après un cat /etc/shadow.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Identifier les cinq surfaces d'attaque propres aux conteneurs
- Réduire une image et mesurer le gain en CVE avec un scanner
- Durcir un namespace avec les Pod Security Standards et les NetworkPolicy
- Placer chaque outil, Trivy, Cosign, Kyverno, Falco, à la bonne phase
- Éviter les pièges qui rendent un contrôle inopérant sans le dire
- Situer ces contrôles dans les familles Déploiement et admission et Runtime et exploitation du référentiel SOCLE
Pourquoi les conteneurs sont-ils une cible ?
Section intitulée « Pourquoi les conteneurs sont-ils une cible ? »Un conteneur n'est pas une machine virtuelle : c'est un processus isolé qui partage le noyau de son hôte. Une VM embarque son propre noyau, un conteneur appelle directement celui du nœud. Si un attaquant exploite une faille du noyau depuis un conteneur, il peut compromettre tout le nœud, et avec lui tous les autres conteneurs qui y tournent. Les protections de cette leçon servent toutes à réduire ce que ce noyau partagé expose.
Le risque n'est pas théorique. En février 2018, la société RedLock a révélé que des attaquants avaient pris la main sur un cluster Kubernetes de Tesla par une console d'administration laissée sans mot de passe. Ils y faisaient tourner un mineur de cryptomonnaie, et le cluster donnait accès à des identifiants cloud et à des données de télémétrie.
Les 5 surfaces d'attaque principales
Section intitulée « Les 5 surfaces d'attaque principales »Comprendre où se situent les risques est la première étape pour les réduire. Chaque ligne du tableau correspond à un contrôle vu plus bas : le scan pour les images, les Pod Security Standards pour les configurations, les NetworkPolicy pour le réseau.
| Surface d'attaque | Risque | Exemple concret |
|---|---|---|
| Images vulnérables | CVE dans les dépendances, image de base obsolète | Log4Shell présent dans une image Java non mise à jour |
| Configurations permissives | Conteneurs root, capabilities excessives | Un conteneur avec CAP_SYS_ADMIN peut modifier le système hôte |
| Secrets exposés | Variables d'environnement en clair, ConfigMaps accessibles | Mot de passe de base visible via kubectl describe pod |
| Réseau ouvert | Absence de NetworkPolicy, trafic est-ouest libre | Un pod compromis peut scanner tout le cluster |
| Supply chain | Images tierces non vérifiées, registres publics | Image Docker Hub contenant un mineur caché |
La dernière ligne est documentée. En 2022, l'équipe de recherche de Sysdig a analysé 250 000 images Linux publiques de Docker Hub et en a identifié 1 652 malveillantes : mineurs de cryptomonnaie, secrets embarqués servant de portes dérobées, détournements DNS. Une image tirée d'un registre public n'est auditée par personne avant d'atterrir sur vos nœuds.
Qui doit sécuriser quoi ?
Section intitulée « Qui doit sécuriser quoi ? »La sécurité des conteneurs est une responsabilité partagée : le développeur construit l'image, l'exploitant durcit le cluster, l'équipe DevSecOps automatise les contrôles qui relient les deux. Le tableau dit qui agit, et la colonne de droite donne le geste concret attendu de chacun, pas une intention.
| Profil | Responsabilité principale | Actions concrètes |
|---|---|---|
| Développeur | Construire des images sécurisées | Images de base minimales, aucun secret embarqué, USER non-root |
| Ops / SRE | Durcir l'infrastructure | Pod Security Standards, NetworkPolicy, mises à jour |
| DevSecOps | Automatiser les contrôles | Scan en CI, admission bloquante, alertes runtime |
| RSSI / Conformité | Garantir la conformité | Audit CIS, exceptions documentées, preuves pour les audits |
Détecter tôt coûte moins cher, et c'est mesurable. Sur ce poste, Trivy 0.74.0 scanne l'image de l'application en 9 secondes, téléchargement de sa base de CVE compris, puis en moins d'une seconde une fois la base en cache. Corriger la même faille après un incident en production se compte en jours.
La stratégie de défense en profondeur
Section intitulée « La stratégie de défense en profondeur »La sécurité des conteneurs ne repose pas sur un seul outil. Elle s'articule autour de trois phases qui se complètent : Build, Deploy et Runtime. Si une couche échoue, les autres limitent l'impact.
Le schéma suit une image de sa construction à son exécution. Regardez les deux sorties latérales : l'admission refuse ce qui n'est pas conforme ou pas signé avant que rien ne tourne, le runtime ne fait qu'alerter sur ce qui tourne déjà.
Phase 1 : Build, sécuriser les images
Section intitulée « Phase 1 : Build, sécuriser les images »C'est le moment le plus efficace pour agir : une image vulnérable ne devrait jamais atteindre la production. Quatre gestes y suffisent, détaillés dans les leçons sur les bonnes pratiques Dockerfile et sur le scanner Trivy.
- Images de base minimales : moins de paquets, moins de CVE à traiter.
- Scanner systématiquement : un scanner comme Trivy ou Grype en CI, bloquant sur les CVE critiques.
- Ne jamais tourner en root : un
USERnon-root dans le Dockerfile. - Signer les images : Cosign prouve l'origine et l'intégrité, et le cluster peut refuser une image non signée.
L'image minimale est le geste qui rapporte le plus, et il se mesure. Le
multi-stage build ci-dessous installe les dépendances dans une image
complète, puis ne recopie que le résultat dans une image distroless, qui
ne contient ni shell ni pip. Les deux étapes utilisent Python 3.13, la
version de distroless/python3-debian13 : une dépendance compilée pour une
autre version de Python ne se chargerait pas.
# Étape 1 : installer les dépendances avec pip, dans une image complèteFROM python:3.13-slim@sha256:7c61056e61ac89e852de05f3dc6fa51a6dd2181797bceed46aa725dd7cb2cd3b AS builderWORKDIR /appCOPY requirements.txt .RUN pip install --no-cache-dir --target=/app/deps -r requirements.txt
# Étape 2 : ne garder que le résultat, sans shell ni pipFROM gcr.io/distroless/python3-debian13:nonroot@sha256:774595d652a294b54c9bd575b2d9fdd1a4b47547dc17b8bfa4c0e953c64855b3WORKDIR /appENV PYTHONPATH=/app/depsCOPY --from=builder /app/deps /app/depsCOPY app.py .USER 65532CMD ["app.py"]Le point qui fait échouer la plupart des tentatives : pip install écrit dans
site-packages, pas dans /app. Sans --target et sans PYTHONPATH,
l'image se construit sans erreur mais l'application meurt au démarrage sur un
ModuleNotFoundError. Vérifiez donc toujours l'image en l'exécutant : un
docker build qui réussit ne prouve rien sur la présence des dépendances.
$ docker run --rm mon-app:1.0.0requests 2.32.5 yaml 6.0.3 uid 65532Le gain, mesuré avec Trivy 0.74.0 sur la même application (requests et
PyYAML) :
| Image | Taille sur disque | CVE détectées | dont CRITICAL |
|---|---|---|---|
python:3.13 | 1,62 Go | 4 122 | 19 |
application en distroless/python3-debian13 | 99,5 Mo | 155 | 0 |
Vingt-six fois moins de CVE à trier, et aucune critique : tout ce qui
disparaît, shell, gestionnaire de paquets, compilateurs, est autant de
surface d'attaque en moins. L'utilisateur 65532 est celui que la variante
nonroot de distroless prévoit déjà.
Phase 2 : Deploy, durcir les configurations
Section intitulée « Phase 2 : Deploy, durcir les configurations »Même une image sûre peut être déployée de façon dangereuse. Kubernetes offre les protections, mais n'en active aucune par défaut : sans label, un namespace accepte un pod privilégié. Trois contrôles s'y ajoutent, et tous se posent sur le namespace ou le cluster, pas sur l'image.
Les Pod Security Standards (PSS) sont trois niveaux de restriction que
Kubernetes applique par un simple label de namespace. Le niveau
restricted interdit l'exécution en root, exige l'abandon des
capabilities et un profil seccomp. La leçon suivante les détaille.
apiVersion: v1kind: Namespacemetadata: name: production labels: pod-security.kubernetes.io/enforce: restricted pod-security.kubernetes.io/warn: restrictedUne faute de frappe ne se comporte pas pareil selon où elle tombe. Mesuré
sur Kubernetes 1.37 : une valeur mal écrite (restrictd) est refusée
par l'API server, qui liste les valeurs admises. Une clé mal écrite
(pod-security.kubernetes.io/enforse) est acceptée comme un label quelconque,
et le namespace reste sans aucune contrainte. Vérifiez donc l'effet, pas
seulement le label : un pod non conforme doit être refusé.
The Namespace "pss-typo" is invalid: metadata.labels[pod-security.kubernetes.io/enforce]:Invalid value: "restrictd": must be one of privileged, baseline, restrictedLes NetworkPolicy ferment le réseau. Par défaut, tous les pods d'un cluster peuvent se joindre : la règle ci-dessous refuse tout trafic entrant dans le namespace tant qu'une autre règle ne l'autorise pas. Elle n'a d'effet que si le plugin réseau (CNI) du cluster applique les NetworkPolicy, ce que la documentation de chaque CNI précise.
apiVersion: networking.k8s.io/v1kind: NetworkPolicymetadata: name: deny-all-ingress namespace: productionspec: podSelector: {} policyTypes: - IngressDeux réglages complètent le tableau. Un ResourceQuota et un LimitRange empêchent un pod compromis de consommer toutes les ressources du nœud, ce qui serait un déni de service. Un RBAC au moindre privilège limite ce qu'un compte de service volé permet de faire.
Phase 3 : Runtime, détecter et bloquer
Section intitulée « Phase 3 : Runtime, détecter et bloquer »Les phases Build et Deploy ne suffisent pas : de nouvelles CVE sortent chaque jour, et une faille zero-day n'est connue d'aucun scanner. La protection runtime surveille les conteneurs en cours d'exécution, le plus souvent en observant leurs appels système.
| Signal | Ce qu'il détecte | Outil |
|---|---|---|
| Appels système anormaux | Shell dans un conteneur, lecture de fichiers sensibles | Falco |
| Connexions réseau | Trafic vers des IP suspectes, scan de ports | NeuVector, Cilium |
| Processus inattendus | Mineur de cryptomonnaie, reverse shell | Falco, Sysdig |
| Dérive de configuration | Fichiers modifiés par rapport à l'image d'origine | NeuVector |
La détection runtime arrive après l'intrusion : elle limite les
dégâts mais ne les empêche pas. Mesuré avec Falco 0.45.0 sur un cluster
kind : un cat /etc/shadow dans un pod produit l'alerte Read sensitive file untrusted en quelques secondes, mais le fichier a bien été lu. Les phases
Build et Deploy restent la première ligne de défense.
Scénario : intégrer la sécurité dans votre pipeline
Section intitulée « Scénario : intégrer la sécurité dans votre pipeline »Voici comment une équipe enchaîne les trois phases pour qu'aucune image vulnérable ou mal configurée n'atteigne la production. Chaque étape reprend un outil vu dans le parcours, avec la version épinglée utilisée dans les mesures, et chaque commande échoue quand le contrôle échoue.
-
Build : scanner l'image
La CI lance Trivy dans son image officielle épinglée par digest et échoue si une CVE de niveau
CRITICALest trouvée. L'appel direct à l'image évite l'intermédiaire d'une action tierce, comme le montre la leçon Trivy.Fenêtre de terminal docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \aquasec/trivy:0.74.0@sha256:62b1e65e8869bc4b4c6aa4fa2b21595256c7c2f6018a9d9ad61caf87187c1969 \image --scanners vuln --severity CRITICAL --exit-code 1 mon-app:1.0.0 -
Registre : signer l'image par son digest
L'image poussée est signée avec Cosign, en désignant son digest et non son tag : un tag peut être déplacé, un digest non. La leçon Cosign couvre la signature sans clé, qui évite de gérer
cosign.key.Fenêtre de terminal cosign sign --key cosign.key registry.example.com/mon-app@sha256:<digest> -
Admission : valider les manifestes
Au déploiement, un contrôleur d'admission vérifie chaque pod avant son enregistrement. Cette policy Kyverno de type ValidatingPolicy refuse tout conteneur sans
runAsNonRoot: true. Mesuré sur Kyverno 1.19.1 : un pod nginx sanssecurityContextest refusé avec le message de la policy.apiVersion: policies.kyverno.io/v1kind: ValidatingPolicymetadata:name: require-non-rootspec:validationActions: [Deny]matchConstraints:resourceRules:- apiGroups: [""]apiVersions: ["v1"]operations: ["CREATE", "UPDATE"]resources: ["pods"]validations:- expression: >-object.spec.containers.all(c,has(c.securityContext) &&has(c.securityContext.runAsNonRoot) &&c.securityContext.runAsNonRoot == true)message: "Les conteneurs doivent tourner en non-root (runAsNonRoot: true)." -
Runtime : surveiller en production
Falco tourne sur chaque nœud et alerte sur un shell interactif, la lecture d'un fichier sensible ou l'exécution d'un binaire absent de l'image. Les alertes partent vers l'outil d'astreinte de l'équipe.
Le résultat se mesure à chaque étape : l'image python:3.13 échoue au scan
avec 19 CVE critiques, l'image distroless passe ; un pod sans
runAsNonRoot ne s'enregistre pas ; un shell interactif dans un pod
déclenche Terminal shell in container.
Pièges courants à éviter
Section intitulée « Pièges courants à éviter »Même avec les bons outils, cinq erreurs reviennent et chacune neutralise un contrôle sans bruit. La plupart viennent d'un contrôle posé une fois puis jamais revérifié, ou relâché « temporairement » pour débloquer un déploiement.
- Scanner uniquement au build. Les CVE sont publiées en continu : une image propre aujourd'hui peut être vulnérable demain. Scannez aussi les images déjà déployées.
- Désactiver les Pod Security Standards « pour que ça marche ». Corrigez plutôt le manifeste, ou choisissez une image prévue pour tourner en non-root.
- Faire confiance au tag
latest. Un tag peut pointer vers n'importe quoi demain. Déployez par digest (image@sha256:…). - Ignorer les NetworkPolicy. Sans elles, un pod compromis joint tout le cluster : le trafic est-ouest, entre pods, est souvent plus dangereux que le trafic entrant.
- Mettre les secrets en variables d'environnement. Ils fuient dans les journaux, les rapports de plantage et
kubectl describe. Montez-les en fichier, ou mieux, servez-les depuis un gestionnaire externe, comme dans le module sur les secrets.
Outils recommandés
Section intitulée « Outils recommandés »Chaque phase a sa famille d'outils, et un outil de la phase Build ne remplace jamais un outil de la phase Runtime. Les trois tableaux se lisent par la colonne Quand l'utiliser : elle indique le contexte de déclenchement, pas une préférence de qualité.
Scanner de vulnérabilités
Section intitulée « Scanner de vulnérabilités »Un scanner de vulnérabilités compare les paquets présents dans une image aux bases de CVE publiques. Son résultat dépend de la fraîcheur de sa base : un scan lancé sur une image inchangée peut remonter dix vulnérabilités de plus qu'hier. Regardez la colonne Forces pour savoir si l'outil couvre aussi les dépôts et l'IaC.
| Outil | Forces | Quand l'utiliser |
|---|---|---|
| Trivy | Rapide, gratuit, multi-cibles (images, dépôts, IaC) | Premier choix pour la CI/CD |
| Grype | Léger, écosystème Anchore | Alternative à Trivy |
| Snyk | Base de CVE enrichie, remédiation guidée | Si vous avez un budget sécurité |
Conformité et audit
Section intitulée « Conformité et audit »Ces outils ne cherchent pas de CVE : ils comparent la configuration réelle du cluster à un référentiel (CIS Benchmark, recommandations NSA). Ils répondent à « ma configuration est-elle conforme », pas « mes images sont-elles vulnérables ».
| Outil | Forces | Quand l'utiliser |
|---|---|---|
| Kubescape | CIS Benchmarks, recommandations NSA, gratuit | Audit initial et continu |
| kube-bench | CIS Kubernetes Benchmark officiel | Audit de conformité formel |
| Polaris | Bonnes pratiques Kubernetes, simple | Premiers pas en sécurité Kubernetes |
Protection runtime
Section intitulée « Protection runtime »La protection runtime observe les appels système et le trafic des conteneurs déjà démarrés. Elle produit des alertes en continu et non un rapport ponctuel : prévoyez qui les reçoit et qui y répond avant de la déployer.
| Outil | Forces | Quand l'utiliser |
|---|---|---|
| Falco | Open source, capture des appels système par eBPF | Détection d'intrusion |
| NeuVector | Protection réseau et runtime, pare-feu L7 | Sécurité en entreprise |
| Sysdig Secure | Observabilité et sécurité intégrées | Si vous utilisez déjà Sysdig |
Guides détaillés
Section intitulée « Guides détaillés »Les pages ci-dessous reprennent ces outils en mode pratique. Si vous partez de zéro, commencez par Trivy, qui donne un résultat en une commande, puis Kubescape pour mesurer l'écart de votre cluster aux CIS Benchmarks. NeuVector suppose un cluster déjà en place.
- Kubescape : auditer un cluster selon les CIS Benchmarks et les recommandations NSA.
- NeuVector : protection runtime, pare-feu réseau L7 et conformité continue.
- Sécuriser Docker en production : capabilities, seccomp, rootless, mesurés sur Docker 29.1.3.
À retenir
Section intitulée « À retenir »- Un conteneur partage le noyau de l'hôte : une faille exploitée peut compromettre tout le nœud.
- Trois phases, trois familles d'outils : build (image minimale, scan, signature), admission (PSS, Kyverno), runtime (Falco).
- L'image minimale rapporte le plus : 4 122 CVE pour
python:3.13, 155 pour la même application en distroless. - Un namespace sans label accepte tout ; une clé de label PSS mal écrite est ignorée sans erreur.
- Les NetworkPolicy ne protègent que si le plugin réseau les applique.
- La détection runtime alerte après coup : elle ne remplace ni le scan ni l'admission.
- Ces contrôles sont exigés par les familles SOCLE Déploiement et admission (DPL) et Runtime et exploitation (RUN).
Contrôle de connaissances
Section intitulée « Contrôle de connaissances »Vérifiez que l'essentiel de cette leçon est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici : les trois phases, les mesures de la leçon et les pièges qui désactivent un contrôle.
Contrôle de connaissances
Validez vos connaissances avec ce quiz interactif
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
Vérification
(0/0)Score par domaine
Un domaine regroupe plusieurs guides et reçoit plusieurs questions : son score est plus fiable qu'un score par thème, qui porte souvent sur une seule question.
Bilan
Points forts
Aucun sujet réussi à 80 % sur au moins deux questions pour l’instant.
Points faibles
Aucun sujet sous le seuil de réussite.
À faire ensuite
Des indices pour retenter votre chance ?
Continuer le cours : Pod Security Standards
Nouveau quiz complet avec des questions aléatoires
Retravailler uniquement les questions ratées
Retour à la liste des certifications
Checklist sécurité conteneurs
Section intitulée « Checklist sécurité conteneurs »Cette checklist sert de revue avant une mise en production, pas de liste à cocher d'un bloc. Traitez-la dans l'ordre Build, Deploy, Runtime : chaque bloc suppose le précédent acquis. Une case vide doit être une exception documentée, avec un nom en face.
Ce bloc se joue dans le dépôt et dans la CI, avant qu'aucune image n'atteigne un registre. C'est le moment le moins coûteux : une correction ici est un commit, la même correction en production est un incident. Le point le plus souvent oublié reste la signature des images.
- Images de base minimales (distroless, alpine)
- Scanner intégré dans la CI (Trivy, Grype)
- Pipeline bloqué si CVE critique détectée
- Pas de secrets dans les images
- Dockerfile avec
USERnon-root - Images signées (Cosign) et déployées par digest
Ces contrôles se posent sur le namespace et sur le cluster, pas sur l'image. Ils protègent même contre une image que vous n'avez pas construite. Vérifiez chaque case sur un namespace réel : une clé de label Pod Security mal orthographiée est ignorée sans le moindre message d'erreur.
- Pod Security Standards appliqués (baseline ou restricted)
- NetworkPolicy définies (refus par défaut)
- RBAC avec moindre privilège
- ResourceQuota et LimitRange configurés
- Secrets montés en fichier, pas en variables d'environnement
Le bloc runtime est celui qui se dégrade le plus vite : un agent déployé une fois et jamais relu finit par alerter dans le vide. Les deux cases qui comptent sont la destination des alertes et la procédure de réponse : un détecteur sans destinataire ne change rien au délai de réaction.
- Outil de détection déployé (Falco, NeuVector)
- Alertes configurées pour les comportements anormaux
- Scan régulier des images en production
- Procédure de réponse aux incidents documentée