La sécurité Docker en 2026 repose sur une défense en profondeur à six couches : l'hôte Docker (rootless, user namespaces, kernel hardening), les images (base minimale, scan CVE, signing), le Dockerfile (multi-stage, pinning, USER non-root), le runtime (drop capabilities, seccomp, AppArmor, read-only rootfs), le réseau (VPC isolation, user-defined networks, pas de bridge default), et les secrets (BuildKit secrets, Vault, Docker Secrets). Le CIS Docker Benchmark v1.7 (117 contrôles) sert de checklist officielle, vérifiable automatiquement via Docker Bench Security ou Trivy compliance. Bien appliquées, ces bases réduisent de 70 à 90 % la surface d'attaque d'un conteneur classique et limitent l'impact d'une éventuelle évasion. Cet article couvre les six couches avec commandes concrètes, les pièges fréquents, et la checklist à appliquer sur un nouveau projet.
Threat model Docker : que défendre contre quoi
Avant les contrôles, comprendre les menaces principales.
| Menace | Impact | Couche défensive |
|---|---|---|
| Image compromise (CVE, backdoor) | Code malveillant en prod | Scan, signing, base minimale |
| Secret exfiltré (ENV, layer, logs) | Fuite credentials cloud/API | BuildKit secrets, Vault |
| Évasion de conteneur (privesc kernel) | Accès hôte et pivot | Rootless, seccomp, AppArmor, user NS |
| Attaque réseau latérale | Pivot vers autres conteneurs | User-defined networks, policies |
| Supply chain (pull image piégée) | Image corrompue silencieusement | Digest SHA256, cosign verify |
| Docker socket exposé | Privesc via API Docker | Rootless, firewall, never mount /var/run/docker.sock |
| Container escape via /proc ou mount | Accès filesystem hôte | No --privileged, drop CAP_SYS_ADMIN |
Couche 1 - Sécuriser l'hôte Docker
L'hôte Docker est le périmètre le plus critique. Une compromission hôte = compromission de tous les conteneurs.
Rootless mode (recommandé en 2026)
# Installation rootless Docker (utilisateur sans root)
curl -fsSL https://get.docker.com/rootless | sh
# Variables d'environnement à ajouter au shell
export PATH=$HOME/bin:$PATH
export DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock
# Vérifier le mode
docker info | grep -i rootlessEn rootless, le daemon Docker et tous les conteneurs tournent sous l'utilisateur courant. Une évasion éventuelle n'atteint pas root sur l'hôte.
User namespaces (alternative si rootless impossible)
Si le rootless complet n'est pas faisable (contraintes d'entreprise, features avancées), activer User Namespace Remapping :
// /etc/docker/daemon.json
{
"userns-remap": "default",
"live-restore": true,
"no-new-privileges": true,
"icc": false,
"log-driver": "json-file",
"log-opts": { "max-size": "10m", "max-file": "3" }
}Avec userns-remap, l'UID 0 dans le conteneur est mappé vers un UID non-privilégié sur l'hôte.
Hardening OS et kernel
OS de l'hôte :
Mise à jour continue (unattended-upgrades Debian, dnf-automatic RHEL)
Désactiver services inutiles
Firewall restrictif (ufw, nftables, iptables)
SSH : pas de password, key-only, port non-standard si exposé
Kernel :
AppArmor (Ubuntu, Debian) ou SELinux (RHEL, Fedora) actif
Filesystem /var/lib/docker sur volume chiffré (LUKS)
Module kernel : désactiver ceux non utilisésCouche 2 - Images : minimalisme et vérification
Base minimale
ubuntu:24.04 : 77 MB, 90+ CVE résiduelles
python:3.12-slim : 45 MB, 20-40 CVE
python:3.12-alpine : 19 MB, 15-30 CVE
gcr.io/distroless/python3 : 25 MB, 0-5 CVE
cgr.dev/chainguard/python : 15 MB, 0-3 CVE (sponsored)
scratch + binaire Go statique : 5-15 MB, 0 CVE distroLes images distroless (Google) et Chainguard Images éliminent shell, gestionnaire de paquets et outils système que 99 % des applications n'utilisent pas mais que les attaquants exploitent pour pivoter. Standard 2026 pour les équipes AppSec matures.
Vérification de provenance
# Pinning par digest SHA256 immuable (pas juste par tag)
docker pull python:3.12.6-slim@sha256:1a89c1d4...
# Vérification signature cosign keyless
cosign verify \
--certificate-identity-regexp "https://github.com/org/.*" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
ghcr.io/org/app:sha-abc123Scan automatique en CI
Voir le guide dédié scan de conteneurs : pourquoi et comment pour Trivy, Grype, Snyk, Docker Scout.
Couche 3 - Dockerfile sécurisé
Template Dockerfile minimaliste hardened 2026
# Stage 1 - build (gros, outils inclus)
FROM golang:1.22.3-alpine@sha256:d3d5b... AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /bin/app ./cmd/app
# Stage 2 - runtime (minimal, no shell, non-root)
FROM gcr.io/distroless/static-debian12:nonroot
USER 65532:65532
WORKDIR /app
COPY --from=build --chown=65532:65532 /bin/app /app/app
EXPOSE 8080
ENTRYPOINT ["/app/app"]Règles Dockerfile essentielles
- Multi-stage builds systématiquement : exclure compilateurs et toolchain du runtime.
- USER non-root explicite (
USER 10001ouUSER nonroot). Ne jamais laisser en root implicite. - Pin par digest SHA256 sur les
FROM, pas juste par tag (latestou même3.12-slimsont mutables). - COPY plutôt que ADD sauf cas spécifique (ADD télécharge et extrait sans vérification).
.dockerignorestrict : exclure.git,.env,node_modules, clés SSH, tests.- Pas de secret en ARG/ENV : utiliser BuildKit secrets ou mount temporaire.
HEALTHCHECKdéfini pour l'observabilité.- Flatten les layers peu utiles :
RUN apt-get update && apt-get install -y ... && rm -rf /var/lib/apt/lists/*dans un seul RUN. EXPOSEdocumentaire uniquement : ne pas croire que c'est un contrôle de sécurité (c'est informatif).ENTRYPOINTplutôt queCMDpour contrôler strictement l'exécution.
BuildKit secrets - injection sûre au build
# syntax=docker/dockerfile:1.6
FROM alpine:3.19
RUN --mount=type=secret,id=github_token \
TOKEN=$(cat /run/secrets/github_token) && \
curl -H "Authorization: token $TOKEN" https://api.github.com/... -o /data.json# Build avec secret injecté (non persisté dans layer)
DOCKER_BUILDKIT=1 docker build \
--secret id=github_token,src=/path/to/token \
-t my-app .Le secret est accessible uniquement pendant l'exécution du RUN, jamais écrit dans une couche.
Couche 4 - Runtime : privilèges, capabilities, seccomp
Minimum privilege au lancement
docker run \
--read-only \ # rootfs en lecture seule
--tmpfs /tmp:rw,size=64M,noexec \ # /tmp isolé
--user 10001:10001 \ # non-root
--cap-drop=ALL \ # drop toutes les capabilities
--cap-add=NET_BIND_SERVICE \ # réajouter ce qui est strictement nécessaire
--security-opt=no-new-privileges \ # pas d'escalade via setuid
--security-opt=seccomp=profile.json \ # profile seccomp custom
--security-opt=apparmor=app-profile \ # profile AppArmor
--pids-limit=100 \ # limite PID pour éviter fork bomb
--memory=512m --memory-swap=512m \ # limite RAM
--cpus=1.0 \ # limite CPU
--network=my-app-net \ # pas le bridge default
my-app:1.2.3@sha256:abc...Linux capabilities : 38 à comprendre
Capabilities dangereuses à DROP absolument :
CAP_SYS_ADMIN : « root-lite », très puissant
CAP_SYS_MODULE : chargement modules kernel
CAP_SYS_PTRACE : debug et injection processus
CAP_SYS_RAWIO : accès matériel brut
CAP_NET_ADMIN : configuration réseau
CAP_SYS_BOOT : reboot
CAP_DAC_READ_SEARCH : lecture contournant les permissions
CAP_SETUID / SETGID : changement d'UID/GID
Capabilities souvent nécessaires à ajouter :
CAP_NET_BIND_SERVICE : écouter sur port inférieur à 1024
CAP_CHOWN : chown dans le conteneur
CAP_SETGID / SETUID : si l'app change d'UID explicitementPratique : --cap-drop=ALL puis --cap-add=NET_BIND_SERVICE (ou autre capability précise) pour ce qui est strictement nécessaire. Par défaut, Docker drop déjà 22 capabilities, mais garde 14 qui peuvent être exploitées.
Seccomp - filtre syscalls
Docker applique un profil seccomp par défaut qui bloque environ 44 appels système dangereux (reboot, mount, keyctl, kexec_load, personality, etc.).
# Voir le profil seccomp par défaut
docker run --rm alpine cat /proc/1/status | grep Seccomp
# Profil custom : autoriser uniquement les syscalls nécessaires
docker run --security-opt=seccomp=/path/to/my-profile.json my-appUn profil seccomp custom bien taillé réduit la surface de 300+ syscalls à 50-100 selon l'application. Outils pour générer : falco avec syscall tracing, strace en mode record, Docker seccomp-profile generator.
AppArmor ou SELinux
AppArmor (Ubuntu, Debian) et SELinux (RHEL, Fedora) appliquent des politiques MAC (Mandatory Access Control) sur les ressources accessibles.
# AppArmor - appliquer un profile custom
docker run --security-opt=apparmor=my-app-profile my-app
# Voir le profile par défaut Docker
cat /etc/apparmor.d/docker-defaultUn profile AppArmor dédié à une application verrouille les chemins filesystem accessibles, les sockets, les modes d'ouverture, etc. Effort initial non trivial (2-4 heures par application), gain significatif en défense en profondeur.
Read-only root filesystem
docker run --read-only --tmpfs /tmp:rw,size=64M my-appEmpêche un attaquant qui compromet l'application d'écrire des binaires malveillants ou de modifier la configuration. Certaines apps demandent /tmp writable, à monter séparément en tmpfs.
Couche 5 - Réseau Docker
Ne pas utiliser le bridge default
Le réseau bridge par défaut permet à tous les conteneurs de communiquer entre eux. Créer des réseaux user-defined isolés :
# Créer un réseau dédié
docker network create --driver=bridge my-app-net
# Lancer les conteneurs dans ce réseau
docker run --network=my-app-net --name=api my-api:1.0
docker run --network=my-app-net --name=db my-db:1.0Les conteneurs sur des réseaux user-defined séparés ne se voient pas.
Pas de publication de port inutile
# Mauvais - publication sur toutes les interfaces
docker run -p 5432:5432 postgres # écoute 0.0.0.0:5432, exposé Internet
# Meilleur - binding localhost seulement
docker run -p 127.0.0.1:5432:5432 postgres
# Encore mieux - pas de publication, accès via réseau user-defined
docker run --network=my-app-net --name=db postgresProtéger Docker socket
Ne JAMAIS :
Monter /var/run/docker.sock dans un conteneur exposé (Traefik, Portainer
exposés Internet) - équivaut à root sur l'hôte pour l'attaquant.
Alternatives :
Podman socket rootless
Docker Socket Proxy (Tecnativa) pour exposer une API restreinte
Traefik avec socket read-only + mode file plutôt que dynamicCouche 6 - Gestion des secrets
Hiérarchie des approches par maturité
À éviter :
Secret dans le Dockerfile (ARG ou ENV) → fuit via docker history
Secret dans docker run -e SECRET=... → fuit via docker inspect + logs
Secret en clair dans un volume monté → fuit si volume exposé
Acceptable pour dev local :
Fichier .env chargé via docker run --env-file
Secret dans Docker Desktop Secrets
Recommandé pour production :
Docker Secrets (Swarm uniquement)
Kubernetes Secrets (avec encryption at rest etcd + Sealed Secrets)
HashiCorp Vault (sidecar injector ou Vault Agent)
Cloud-native : AWS Secrets Manager, GCP Secret Manager, Azure Key Vault
OIDC Workload Identity Federation pour CI/CD (sans secret du tout)Exemple Kubernetes Sealed Secret (chiffrement à la source)
# Générer un Sealed Secret chiffré avec la clé publique du cluster
echo -n "my-super-secret-value" | kubectl create secret generic my-secret \
--dry-run=client --from-file=db-password=/dev/stdin -o yaml | \
kubeseal --format yaml > sealed-secret.yaml
# Le fichier sealed-secret.yaml est commitable dans Git, chiffré.
# Le cluster a la clé privée pour le déchiffrer.CIS Docker Benchmark et vérification automatique
Le CIS Docker Benchmark v1.7 (2024) couvre 117 contrôles répartis en 7 sections :
1. Host Configuration (~20 contrôles)
2. Docker Daemon Configuration (~35 contrôles)
3. Docker Daemon Files (~15 contrôles)
4. Container Images and Build File (~12 contrôles)
5. Container Runtime (~30 contrôles)
6. Docker Security Operations (~2 contrôles)
7. Docker Swarm Configuration (~9 contrôles)Vérification automatique
# Docker Bench Security (script officiel, 117 checks)
docker run --rm --net host --pid host --userns host --cap-add audit_control \
-e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \
-v /etc:/etc:ro \
-v /usr/bin/containerd:/usr/bin/containerd:ro \
-v /usr/bin/runc:/usr/bin/runc:ro \
-v /usr/lib/systemd:/usr/lib/systemd:ro \
-v /var/lib:/var/lib:ro \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
--label docker_bench_security \
docker/docker-bench-security
# Alternative : Trivy compliance CIS
trivy image --compliance docker-cis my-app:1.0
# Alternative : Prowler multi-cloud
prowler -c cis_dockerObjectif pragmatique : atteindre 80-90 % de conformité. 100 % est souvent irréaliste car certains contrôles cassent des fonctionnalités légitimes.
Docker Desktop en 2026 : Enhanced Container Isolation
Docker Desktop (Mac, Windows, Linux) est utilisé massivement par les développeurs. Risques spécifiques : il tourne avec privilèges élevés sur le poste de travail, expose l'API Docker localement.
Depuis 2023, Docker Desktop Business propose Enhanced Container Isolation (ECI) basé sur Sysbox :
- Les conteneurs deviennent automatiquement rootless côté hôte.
- Protection contre les évasions de conteneur même pour des images non-root.
- Blocage des opérations privilégiées dangereuses (mount /, accès à Docker socket interne, etc.).
Pour un développeur en 2026, l'alternative complète : Podman Desktop (rootless natif, gratuit, compatible API Docker), Rancher Desktop, ou Colima sur Mac.
Checklist développeur avant production
Image et build :
[ ] Image de base minimale (distroless, Chainguard ou Alpine pinné)
[ ] FROM avec digest SHA256 immuable
[ ] Multi-stage build avec runtime stripped
[ ] USER non-root explicite dans Dockerfile
[ ] .dockerignore complet (.git, .env, node_modules, tests)
[ ] Pas de secret dans ARG ou ENV
[ ] HEALTHCHECK défini
Runtime :
[ ] --cap-drop=ALL + --cap-add explicites
[ ] --security-opt=no-new-privileges
[ ] --read-only + --tmpfs /tmp
[ ] --pids-limit, --memory, --cpus définis
[ ] Seccomp custom pour applications à fort volume d'utilisateurs
[ ] AppArmor ou SELinux profile dédié
Hôte :
[ ] Rootless Docker ou user namespaces activé
[ ] daemon.json hardened (no-new-privileges, live-restore, icc:false)
[ ] Docker Bench Security > 80 % pass
[ ] OS hardened (AppArmor/SELinux, firewall)
Secrets :
[ ] Aucun secret dans l'image
[ ] Docker Secrets, Vault ou Cloud secret service utilisé
[ ] BuildKit secrets pour les builds nécessitant credentials
Réseau :
[ ] Pas de bridge default, réseaux user-defined
[ ] Pas de docker.sock monté dans un conteneur exposé
[ ] Binding 127.0.0.1 pour services non publics
[ ] TLS sur l'API Docker si exposée (rare, déconseillé)
Observabilité :
[ ] Logs conteneurs remontés dans un SIEM
[ ] Détection runtime (Falco, Tetragon) si criticité élevéePoints clés à retenir
- Sécurité Docker = défense en profondeur à 6 couches : hôte, images, Dockerfile, runtime, réseau, secrets. Aucune couche ne suffit seule.
- 4 actions prioritaires débutant : rootless mode, image distroless/Chainguard, USER non-root, secrets hors ENV/ARG. Couvre 70 % du risque.
- Runtime hardening essentiel :
--cap-drop=ALL+--cap-addciblé +--read-only+--security-opt=no-new-privileges. Seccomp custom et AppArmor pour criticité élevée. - CIS Docker Benchmark v1.7 = 117 contrôles officiels. Vérifier via Docker Bench Security, Trivy compliance, Prowler. Viser 80-90 %.
- Secrets : jamais en ENV/ARG dans Dockerfile. BuildKit
--mount=type=secretau build, Vault/Secret Manager/Kubernetes Secrets en runtime. OIDC WIF pour CI/CD idéalement. - Docker Desktop Business propose Enhanced Container Isolation (Sysbox). Alternatives OSS : Podman Desktop, Rancher Desktop, Colima.
- Ne jamais monter
/var/run/docker.sockdans un conteneur exposé : c'est root sur l'hôte pour tout attaquant qui le compromet.
Pour compléter le durcissement par la détection des vulnérabilités dans les images et le scan continu du registry, voir scan de conteneurs : pourquoi et comment. Pour resituer Docker security dans une discipline DevSecOps complète avec pipelines CI/CD shift-left, lire différence entre DevOps et DevSecOps. Pour la progression carrière et l'apprentissage structuré, la roadmap DevSecOps 2026 détaille les 4 étapes.





