La conteneurisation avec Docker est devenue le standard de déploiement des applications modernes, mais elle introduit une surface d’attaque spécifique que beaucoup d’équipes sous-estiment. Un conteneur mal configuré peut donner accès au système hôte, permettre l’escalade de privilèges, exposer des secrets dans les couches d’image, ou servir de point d’entrée pour une propagation latérale dans un cluster Kubernetes. En 2026, plusieurs incidents majeurs de sécurité trouvaient leur origine dans des configurations Docker et Kubernetes insuffisamment durcies. Cette checklist couvre les points essentiels à vérifier pour chaque conteneur en production.
Utiliser des images de base minimales et vérifiées
Le choix de l’image de base est la première décision de sécurité dans la construction d’un conteneur. Les images ‘full-fat’ comme `ubuntu:latest` embarquent des centaines de paquets inutiles pour une application donnée, chacun représentant une surface d’attaque potentielle avec ses propres CVE. Préférez des images minimales : `alpine` (5 Mo), `distroless` (Google), ou `scratch` pour les binaires Go et Rust autonomes. Ces images ne contiennent que le strict nécessaire et sont bien plus faciles à scanner et auditer.
Les images officielles Docker Hub sont un point de départ raisonnable mais ne dispensent pas d’une vérification. En 2024 et 2025, plusieurs images populaires sur Docker Hub ont été empoisonnées par des typosquatters (images avec des noms proches d’images légitimes). Utilisez toujours le Docker Content Trust (DCT) pour vérifier les signatures cryptographiques des images, ou préférez des registres privés (AWS ECR, Azure Container Registry, Harbor auto-hébergé) où vous contrôlez le contenu publié.
Le pinning des images par digest SHA256 plutôt que par tag est une pratique de sécurité critique. Un tag comme `node:20-alpine` peut pointer vers une image différente si la maintenir repousse une mise à jour. En utilisant `node:20-alpine@sha256:abc123…`, vous garantissez que la même image est utilisée partout, que ce soit en développement, en CI ou en production. Le renouvellement régulier de ces pins (au moins mensuel) est nécessaire pour intégrer les mises à jour de sécurité amont.
Ne jamais exécuter les conteneurs en root
Par défaut, les processus dans un conteneur Docker s’exécutent en tant que root (UID 0). Si un attaquant compromet l’application (injection de commandes, déserialisation non sécurisée), il se retrouve root dans le conteneur. Et malgré l’isolation des namespaces, un conteneur root avec des capabilities dangereuses ou un volume monté depuis l’hôte peut facilement escalader vers l’hôte lui-même. Toujours définir un utilisateur non-privilégié dans le Dockerfile avec `USER`.
La directive `USER` dans le Dockerfile doit apparaître après l’installation des dépendances (qui peuvent nécessiter des droits root) et avant la commande `CMD`. Créez un utilisateur et un groupe dédiés à l’application : `RUN groupadd -r appgroup && useradd -r -g appgroup appuser`. Assurez-vous que les répertoires et fichiers nécessaires à l’application appartiennent à cet utilisateur. Les volumes montés depuis l’hôte peuvent avoir des problèmes de permissions qui doivent être anticipés.
Le Pod Security Admission (PSA) de Kubernetes fournit un mécanisme de politique pour imposer ces contraintes à l’échelle du cluster. En activant le niveau `restricted` sur un namespace, toutes les workloads dans ce namespace sont forcées de s’exécuter sans root, sans capabilities supplémentaires, et avec un système de fichiers racine en lecture seule. Le PSA remplace les PodSecurityPolicies dépréciées depuis Kubernetes 1.25 et offre trois niveaux : `privileged`, `baseline` et `restricted`.
Scanner les images avec Trivy avant chaque déploiement
Trivy est devenu le standard de facto pour le scan de vulnérabilités dans les images de conteneur. Il détecte les CVE dans les paquets OS (Alpine, Debian, Ubuntu, RHEL), les dépendances applicatives (npm, pip, Maven, Cargo), les fichiers de configuration problématiques, et les secrets hardcodés (clés API, tokens, certificats privés). Son intégration dans les pipelines CI/CD est simple grâce à des actions GitHub, plugins Jenkins et images Docker prêtes à l’emploi.
La configuration de Trivy pour bloquer les déploiements selon la sévérité des vulnérabilités est la pratique recommandée : `trivy image –exit-code 1 –severity CRITICAL,HIGH monapp:latest`. Un exit code non nul fait échouer le job CI et empêche le push de l’image vers le registre de production. Il est courant d’appliquer un seuil strict (CRITICAL uniquement) pour les images de production et un seuil plus permissif (HIGH) pour les environnements de staging, pour éviter les blocages constants dans les équipes qui n’ont pas encore intégré une culture de remédiation rapide.
Les faux positifs sont inévitables avec tout scanner de vulnérabilités. Trivy permet de définir des fichiers `.trivyignore` qui listent les CVE à ignorer pour un projet donné, avec un commentaire justificatif. Cette liste doit être révisée régulièrement : une CVE ignorée en janvier parce que non exploitable peut devenir critique en mars avec la publication d’un exploit. Intégrer la revue des `.trivyignore` dans le processus mensuel de patch management garantit que les exceptions ne deviennent pas des angles morts permanents.
Restreindre les capabilities Linux et le syscall namespace
Les conteneurs Docker héritent par défaut d’un sous-ensemble des capabilities Linux du noyau, dont certaines sont dangereuses pour la sécurité. `CAP_NET_ADMIN` permet de modifier les tables de routage, `CAP_SYS_ADMIN` donne des pouvoirs quasi-root sur le système, et `CAP_CHOWN` permet de changer le propriétaire de fichiers arbitraires. La directive `–cap-drop ALL –cap-add` dans Docker (ou `securityContext.capabilities` dans Kubernetes) permet de partir de zéro et de n’ajouter que les capabilities réellement nécessaires.
Seccomp (Secure Computing Mode) est un mécanisme noyau Linux qui filtre les appels système qu’un processus peut effectuer. Docker applique un profil seccomp par défaut qui bloque une quarantaine d’appels système dangereux. Pour un niveau de sécurité supérieur, définissez un profil seccomp personnalisé qui n’autorise que les syscalls réellement utilisés par votre application — identifiables avec `strace` ou des outils comme `Falco`. Ce profil réduit drastiquement l’impact potentiel d’une compromission.
AppArmor (sur Ubuntu/Debian) et SELinux (sur RHEL/Fedora) offrent une couche de contrôle d’accès mandatoire (MAC) qui complète seccomp et les capabilities. Docker génère automatiquement un profil AppArmor (`docker-default`) pour chaque conteneur, mais un profil spécifique à l’application offre une isolation plus fine. Kubernetes supporte les profils AppArmor via des annotations de Pod et les profils seccomp via `securityContext.seccompProfile`. Ces mécanismes sont souvent omis dans les déploiements par manque de temps mais leur valeur de sécurité est significative.
Gestion des secrets : ne jamais mettre de credentials dans les images
L’une des erreurs les plus fréquentes et les plus dangereuses est d’inclure des secrets (mots de passe, clés API, certificats, tokens) dans les images Docker, que ce soit dans le Dockerfile (via `ENV` ou `ARG`), dans les fichiers de configuration copiés dans l’image, ou dans les layers intermédiaires. Les layers Docker sont permanents : même si vous supprimez un fichier dans un RUN ultérieur, il reste accessible dans les couches précédentes via `docker history` ou `docker save`.
La solution standard est d’injecter les secrets au runtime via des variables d’environnement (simple mais visible dans `docker inspect`), des volumes montés (fichiers secrets), ou des services de gestion de secrets comme HashiCorp Vault, AWS Secrets Manager ou Azure Key Vault. Kubernetes Secrets, bien que base64-encodés et non chiffrés par défaut en etcd, peuvent être chiffrés at rest via la configuration `EncryptionConfiguration` et intégrés avec des opérateurs comme External Secrets Operator qui synchronise depuis Vault ou les services cloud.
Le bloc de code ci-dessous montre un Dockerfile multi-stage sécurisé qui utilise `–mount=type=secret` (buildkit) pour accéder à un fichier secret pendant le build sans l’inclure dans les layers finales. Cette technique, disponible depuis Docker BuildKit 0.6, permet d’utiliser des credentials d’authentification pour des registres privés ou des repositories npm privés pendant le build, sans les exposer dans l’image résultante. Vérifiable avec `docker history –no-trunc image:tag`.
# Dockerfile multi-stage securise avec BuildKit secrets
# syntax=docker/dockerfile:1
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
# Secret monte uniquement pendant ce RUN, pas dans les layers finales
RUN --mount=type=secret,id=npmrc,target=/root/.npmrc
npm ci --only=production
COPY . .
RUN npm run build
# Image finale minimale
FROM node:20-alpine
RUN groupadd -r appgroup && useradd -r -g appgroup appuser
WORKDIR /app
COPY --from=builder --chown=appuser:appgroup /app/dist ./dist
COPY --from=builder --chown=appuser:appgroup /app/node_modules ./node_modules
USER appuser
EXPOSE 3000
CMD ["node", "dist/server.js"]
# Build: docker build --secret id=npmrc,src=$HOME/.npmrc -t monapp:latest .
Réseau et isolation : least privilege pour les communications
Par défaut, tous les conteneurs sur le même réseau Docker bridge peuvent se communiquer librement. Cette flat network architecture est pratique en développement mais dangereuse en production : un conteneur compromis peut scanner et attaquer tous ses voisins. La solution est de créer des réseaux Docker dédiés par groupe fonctionnel et de n’exposer que les ports strictement nécessaires. Un conteneur web n’a pas besoin de parler directement à un conteneur de backup ; un réseau séparé impose cette contrainte.
Dans Kubernetes, les NetworkPolicies permettent de définir des règles de pare-feu L3/L4 entre les pods. Sans NetworkPolicy, tous les pods d’un cluster peuvent communiquer entre eux par défaut. Une politique de type ‘default deny all’ suivie d’autorisations explicites est la pratique recommandée : `spec.podSelector: {}` avec `policyTypes: [Ingress, Egress]` vides bloque tout le trafic, puis des règles spécifiques autorisent exactement ce qui est nécessaire. Les CNI plugins comme Calico, Cilium ou Weave supportent les NetworkPolicies.
L’inspection du trafic réseau sortant est souvent négligée. Un conteneur compromis peut exfiltrer des données ou communiquer avec un serveur C2 via des ports standard (80, 443) pour contourner les pare-feux. Cilium, avec eBPF, permet d’inspecter le trafic L7 et de créer des règles de politique réseau au niveau DNS (autoriser seulement les connexions vers `api.example.com`) ou HTTP (autoriser uniquement les méthodes GET vers certains endpoints). Cette visibilité granulaire est essentielle pour les environnements à haute sensibilité.
Runtime security : détecter les comportements anormaux avec Falco
Falco est un outil CNCF de security monitoring en temps réel pour les conteneurs et Kubernetes. Il s’appuie sur des sondes eBPF (ou kernel module) pour capturer les appels système de tous les processus sur l’hôte et les compare à des règles prédéfinies qui décrivent des comportements suspects. Des dizaines de règles par défaut couvrent des patterns courants d’attaque : lecture de fichiers sensibles (`/etc/shadow`, `/root/.ssh/`), spawn de shell dans un conteneur (`sh`, `bash`, `busybox`), modification de fichiers système, connexions réseau inattendues.
L’intégration de Falco dans un pipeline SIEM (Security Information and Event Management) transforme ces alertes en événements corrélables avec d’autres sources de logs. Falco peut envoyer ses alertes vers Elasticsearch/Kibana, Splunk, Datadog, ou un webhook personnalisé. Une règle Falco qui détecte un `curl` ou un `wget` depuis un conteneur Node.js de production est un signal fort de compromission — des outils réseau n’ont aucune raison d’être utilisés dans une image de production bien construite.
Les règles Falco personnalisées permettent d’adapter la détection aux spécificités de chaque application. Si votre conteneur PHP ne devrait jamais exécuter `python3`, créez une règle qui alerte sur tout processus non-PHP dans ce conteneur. Si votre API ne devrait jamais écrire dans `/tmp`, une règle sur les ouvertures de fichiers en écriture dans ce répertoire devient un indicateur de compromission. Cette approche de profiling comportemental est complémentaire aux scanners de CVE et capture les attaques zero-day que les signatures ne connaissent pas encore.
Audit régulier avec Docker Bench et CIS Benchmarks
Docker Bench for Security est un outil open source qui automatise la vérification des recommandations CIS Docker Benchmark. Il exécute une centaine de tests couvrant la configuration du démon Docker, les pratiques de build d’images, la sécurité des conteneurs en cours d’exécution, les politiques Swarm, et les configurations réseau. Un score global et des recommandations détaillées par test permettent d’identifier rapidement les lacunes et de prioriser les corrections.
Les CIS Benchmarks sont des référentiels de durcissement reconnus par les organismes de sécurité mondiaux (ANSSI en France référence le CIS pour les configurations cloud). Un audit mensuel avec Docker Bench et une revue trimestrielle des résultats avec l’équipe permettent de mesurer la progression et de s’assurer que les nouvelles configurations respectent les standards. Ces audits doivent être documentés et archivés pour les exigences de conformité ISO 27001, SOC 2 ou NIS2.
La dette de sécurité dans les environnements conteneurisés s’accumule rapidement : des images non mises à jour, des configurations qui ont dérivé, des secrets qui ont changé mais pas été rotatés. Intégrer les audits de sécurité dans le cycle DevOps plutôt que de les traiter comme des événements ponctuels est la clé d’une posture de sécurité durable. Automatiser Docker Bench dans un job CI hebdomadaire qui génère un rapport Slack ou email transforme l’audit en processus continu plutôt qu’en exercice annuel.
Commentaires (0)
Laisser un commentaire
Les commentaires sont modérés. Questions WordPress, cybersécurité ou dev web bienvenues.