Aller au contenu
English
Compte : non connecté, synchronisation possible Mon apprentissage
Selectionner le thème
English

Sécurité des conteneurs : images, clusters, runtime

Par mis à jour le

Améliorer cette page

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.

  • 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

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.

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'attaqueRisqueExemple concret
Images vulnérablesCVE dans les dépendances, image de base obsolèteLog4Shell présent dans une image Java non mise à jour
Configurations permissivesConteneurs root, capabilities excessivesUn conteneur avec CAP_SYS_ADMIN peut modifier le système hôte
Secrets exposésVariables d'environnement en clair, ConfigMaps accessiblesMot de passe de base visible via kubectl describe pod
Réseau ouvertAbsence de NetworkPolicy, trafic est-ouest libreUn pod compromis peut scanner tout le cluster
Supply chainImages tierces non vérifiées, registres publicsImage 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.

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.

ProfilResponsabilité principaleActions concrètes
DéveloppeurConstruire des images sécuriséesImages de base minimales, aucun secret embarqué, USER non-root
Ops / SREDurcir l'infrastructurePod Security Standards, NetworkPolicy, mises à jour
DevSecOpsAutomatiser les contrôlesScan 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 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à.

Chaîne de défense en profondeur : l'image minimale et scannée est poussée signée dans le registre, passe l'admission Pod Security Admission et Kyverno qui refuse les pods non conformes ou non signés, puis tourne sous la détection Falco qui émet des alertes sur un comportement anormal.Buildimage minimale + scanRegistreimage signée, par digestAdmissionPSA + KyvernoRuntimedétection FalcoRefuspod non conforme ou non signéAlertecomportement anormal
Source · SVG
Échap pour fermer, + et − pour zoomer, défilement pour se déplacer. 100 %

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 USER non-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ète
FROM python:3.13-slim@sha256:7c61056e61ac89e852de05f3dc6fa51a6dd2181797bceed46aa725dd7cb2cd3b AS builder
WORKDIR /app
COPY 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 pip
FROM gcr.io/distroless/python3-debian13:nonroot@sha256:774595d652a294b54c9bd575b2d9fdd1a4b47547dc17b8bfa4c0e953c64855b3
WORKDIR /app
ENV PYTHONPATH=/app/deps
COPY --from=builder /app/deps /app/deps
COPY app.py .
USER 65532
CMD ["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.

Fenêtre de terminal
$ docker run --rm mon-app:1.0.0
requests 2.32.5 yaml 6.0.3 uid 65532

Le gain, mesuré avec Trivy 0.74.0 sur la même application (requests et PyYAML) :

ImageTaille sur disqueCVE détectéesdont CRITICAL
python:3.131,62 Go4 12219
application en distroless/python3-debian1399,5 Mo1550

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à.

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: v1
kind: Namespace
metadata:
name: production
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/warn: restricted

Une 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, restricted

Les 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/v1
kind: NetworkPolicy
metadata:
name: deny-all-ingress
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress

Deux 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.

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.

SignalCe qu'il détecteOutil
Appels système anormauxShell dans un conteneur, lecture de fichiers sensiblesFalco
Connexions réseauTrafic vers des IP suspectes, scan de portsNeuVector, Cilium
Processus inattendusMineur de cryptomonnaie, reverse shellFalco, Sysdig
Dérive de configurationFichiers modifiés par rapport à l'image d'origineNeuVector

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.

  1. Build : scanner l'image

    La CI lance Trivy dans son image officielle épinglée par digest et échoue si une CVE de niveau CRITICAL est 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
  2. 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>
  3. 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 sans securityContext est refusé avec le message de la policy.

    apiVersion: policies.kyverno.io/v1
    kind: ValidatingPolicy
    metadata:
    name: require-non-root
    spec:
    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)."
  4. 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.

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.

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é.

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.

OutilForcesQuand l'utiliser
TrivyRapide, gratuit, multi-cibles (images, dépôts, IaC)Premier choix pour la CI/CD
GrypeLéger, écosystème AnchoreAlternative à Trivy
SnykBase de CVE enrichie, remédiation guidéeSi vous avez un budget sécurité

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 ».

OutilForcesQuand l'utiliser
KubescapeCIS Benchmarks, recommandations NSA, gratuitAudit initial et continu
kube-benchCIS Kubernetes Benchmark officielAudit de conformité formel
PolarisBonnes pratiques Kubernetes, simplePremiers pas en sécurité Kubernetes

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.

OutilForcesQuand l'utiliser
FalcoOpen source, capture des appels système par eBPFDétection d'intrusion
NeuVectorProtection réseau et runtime, pare-feu L7Sécurité en entreprise
Sysdig SecureObservabilité et sécurité intégréesSi vous utilisez déjà Sysdig

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.
  • 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).

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

6 questions
6 min.
70% 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

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 USER non-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

Ce projet vous est utile ?

Les guides, formations, quiz et labs restent gratuits. Si vous les utilisez régulièrement, vous pouvez contribuer à leur maintenance et à leur développement.

Devenir membre de soutien (nouvel onglet) Soutenir une fois (nouvel onglet) En savoir plus

Suivez aussi le projet sur LinkedIn