Docker Network Security
La sécurité réseau Docker repose sur un principe simple : chaque container n'accède qu'à ce dont il a besoin. Un container de base de données n'a aucune raison d'être accessible depuis le frontend, et aucune raison d'avoir accès à internet.
Segmentation réseau : le principe de moindre privilège
Le pattern classique est la séparation en tiers réseau :
# Mauvais : tout sur un réseau = tout peut parler à tout
docker network create everything
docker run --network everything frontend
docker run --network everything api
docker run --network everything database # DB accessible depuis le frontend !
# Bon : un réseau par tier
docker network create frontend-net # proxy + frontend
docker network create backend-net # frontend + api (pont entre tiers)
docker network create db-net # api + database (jamais accessible depuis frontend)
docker run --network frontend-net proxy
docker run --network frontend-net --network backend-net api-gateway
docker run --network backend-net --network db-net api
docker run --network db-net database # isolée du reste
Le container database ne peut être joint que depuis api. Le frontend ne peut jamais l'atteindre, même accidentellement.
Réseaux internes : couper l'accès internet
# Un réseau "internal" = pas de route vers l'extérieur
docker network create \
--internal \
--driver bridge \
isolated-db-net
docker run --network isolated-db-net postgres
# Ce container ne peut pas initier de connexions vers internet
# Utile pour les bases de données qui n'en ont aucun besoin
Comment ça marche : Docker supprime la règle de routage par défaut du namespace. Le container a une interface mais pas de route vers l'hôte ou internet.
# Vérifier depuis le container
docker exec my-db-container ip route
# 172.28.0.0/16 dev eth0 ← seulement la route locale, pas de "default via ..."
Chiffrement des communications
Overlay chiffré (Swarm)
docker network create \
--driver overlay \
--opt encrypted=true \
secure-overlay
Sans chiffrement, le trafic VXLAN circule en clair sur le réseau physique — lisible par n'importe qui sur ce réseau. Le chiffrement AES-GCM 256-bit protège le data plane.
Note : le management plane (Raft entre managers) est toujours TLS, même sans --opt encrypted.
TLS entre containers (mTLS)
Pour une sécurité maximale entre services, utiliser mTLS via Docker Secrets :
# Stocker les certificats comme Secrets
docker secret create ca.crt ca.crt
docker secret create server.crt server.crt
docker secret create server.key server.key
# Monter dans le service (accessible à /run/secrets/)
docker service create \
--name api \
--secret ca.crt \
--secret server.crt \
--secret server.key \
my-api-image
# Les secrets sont montés à /run/secrets/ca.crt, etc.
iptables et Docker
Docker gère iptables automatiquement mais te laisse un endroit pour tes règles : la chaîne DOCKER-USER.
# Voir toutes les chaînes Docker
sudo iptables -L -n | grep -E "DOCKER|Chain"
# Chain DOCKER-USER ← tes règles ici
# Chain DOCKER ← règles de port publishing (ne pas modifier)
# Chain DOCKER-ISOLATION-STAGE-1 ← isolation inter-réseaux
# Chain DOCKER-ISOLATION-STAGE-2 ← suite de l'isolation
# Bloquer tout le trafic entre deux réseaux Docker
sudo iptables -I DOCKER-USER -j DROP
# Autoriser seulement un subnet spécifique
sudo iptables -I DOCKER-USER -s 10.10.0.0/24 -d 10.10.1.0/24 -j ACCEPT
sudo iptables -I DOCKER-USER -j DROP # drop le reste
# Vérifier les règles NAT de Docker
sudo iptables -t nat -L DOCKER -n -v
sudo iptables -L DOCKER-ISOLATION-STAGE-1 -n -v
Utilise toujours
DOCKER-USERpour tes règles custom — c'est la seule chaîne que Docker ne touche jamais au redémarrage.
Sécuriser le socket Docker
Le socket /var/run/docker.sock donne un accès root sur la machine. Un container qui le monte peut faire docker run --privileged et sortir de l'isolation.
# NE PAS faire ça en production
docker run -v /var/run/docker.sock:/var/run/docker.sock some-image
# Pour l'API distante, utiliser TLS
dockerd \
--tlsverify \
--tlscacert=ca.pem \
--tlscert=server-cert.pem \
--tlskey=server-key.pem \
--host tcp://0.0.0.0:2376
# Client avec TLS
docker --tlsverify \
--tlscacert=ca.pem \
--tlscert=client-cert.pem \
--tlskey=client-key.pem \
-H tcp://remote-host:2376 ps
Capabilities réseau
Par défaut, les containers n'ont pas NET_ADMIN ni NET_RAW. Les ajouter élargit la surface d'attaque.
# NET_ADMIN permet de modifier les interfaces, routes, iptables
docker run --cap-add NET_ADMIN my-image # risqué
# NET_RAW permet les raw sockets (ping, packet capture)
docker run --cap-add NET_RAW my-image # éviter sauf si nécessaire
# Retirer toutes les capabilities et n'ajouter que le nécessaire
docker run --cap-drop ALL --cap-add NET_BIND_SERVICE my-app
Secrets vs Variables d'environnement
| Variables d'environnement | Docker Secrets | |
|---|---|---|
Visible dans docker inspect | Oui | Non |
Visible via /proc/1/environ | Oui | Non |
| Stocké dans l'image | Si hardcodé dans Dockerfile | Non |
| Disponibilité | Tous containers | Swarm uniquement |
| Rotation sans rebuild | Redémarrage requis | Service update |
# Mauvais : password dans une variable d'env
docker run -e DB_PASSWORD=secret123 myapp
docker inspect myapp # → password visible !
# Bon : Docker Secret
echo "secret123" | docker secret create db_password -
docker service create \
--name myapp \
--secret db_password \
myapp
# App lit /run/secrets/db_password
Profils Seccomp et AppArmor
# Profil Seccomp : limite les syscalls autorisés
docker run --security-opt seccomp=custom-profile.json nginx
# AppArmor : contrôle MAC (Mandatory Access Control)
docker run --security-opt apparmor=docker-default nginx
# Désactiver Seccomp (dangereux, jamais en production)
docker run --security-opt seccomp=unconfined nginx
Le profil Seccomp par défaut de Docker bloque ~44 syscalls dangereux.
Résumé
- Segmenter par tiers réseau : frontend, backend, db — pas de communication directe entre non-voisins
--internalcoupe l'accès internet pour les containers qui n'en ont pas besoin--opt encryptedchiffre le data plane overlay (AES-GCM 256-bit)- Toujours utiliser la chaîne
DOCKER-USERpour les règles iptables custom - Ne jamais monter le socket Docker en production ; utiliser TLS pour l'API distante
- Préférer Docker Secrets aux variables d'environnement pour les credentials
- Limiter les capabilities avec
--cap-drop ALL+--cap-addciblé