Configuration avancée de Docker et Hardening
Objectifs pédagogiques
Section titled “Objectifs pédagogiques”- Comprendre la configuration du daemon Docker et ses options avancées
- Maîtriser les liens entre Docker et containerd
- Connaître les bonnes pratiques de hardening d’une installation Docker
- Savoir configurer les runtimes et les options de sécurité
Configuration avancée du daemon Docker
Section titled “Configuration avancée du daemon Docker”Architecture de configuration
Section titled “Architecture de configuration”Docker utilise plusieurs fichiers de configuration selon la plateforme :
- Linux :
/etc/docker/daemon.json(configuration principale) - Systemd :
/etc/systemd/system/docker.service.d/(overrides systemd) - Windows :
C:\ProgramData\docker\config\daemon.json
Structure du fichier daemon.json
Section titled “Structure du fichier daemon.json”Le fichier daemon.json permet de configurer de nombreux aspects du daemon Docker :
{ "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" }, "storage-driver": "overlay2", "storage-opts": [ "overlay2.override_kernel_check=true" ], "default-address-pools": [ { "base": "172.80.0.0/16", "size": 24 } ], "data-root": "/var/lib/docker", "exec-opts": ["native.cgroupdriver=systemd"], "live-restore": true, "userland-proxy": false, "no-new-privileges": true, "icc": false, "default-ulimits": { "nofile": { "Name": "nofile", "Hard": 64000, "Soft": 64000 } }}Options de configuration essentielles
Section titled “Options de configuration essentielles”1. Gestion des logs
Section titled “1. Gestion des logs”{ "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3", "compress": "true" }}Drivers de logs disponibles :
json-file: Par défaut, stocke les logs en JSONsyslog: Envoie vers syslogjournald: Utilise systemd-journaldgelf: Graylog Extended Log Formatfluentd: Envoie vers Fluentdawslogs: AWS CloudWatch Logssplunk: Splunk Enterprise
Pourquoi limiter les logs ? Sans limite, les logs peuvent remplir le disque et causer des pannes système !
2. Storage Driver et performance
Section titled “2. Storage Driver et performance”{ "storage-driver": "overlay2", "storage-opts": [ "overlay2.override_kernel_check=true" ], "data-root": "/mnt/docker-data"}Storage drivers disponibles :
overlay2: Recommandé - Le plus performant et moderneaufs: Ancien, obsolètedevicemapper: Pour les vieux kernelsbtrfs: Pour filesystem Btrfszfs: Pour filesystem ZFS
Bonnes pratiques :
- Utilisez toujours
overlay2sauf besoin spécifique - Montez
data-rootsur un volume dédié avec suffisamment d’espace - Surveillez l’utilisation disque avec
docker system df
3. Gestion réseau avancée
Section titled “3. Gestion réseau avancée”{ "bip": "192.168.1.1/24", "default-address-pools": [ { "base": "172.80.0.0/16", "size": 24 }, { "base": "172.90.0.0/16", "size": 24 } ], "fixed-cidr": "192.168.1.0/25", "userland-proxy": false, "icc": false, "ip-forward": true, "iptables": true, "ip-masq": true}Explications :
bip: Bridge IP pour le réseau bridge par défautdefault-address-pools: Pools d’adresses pour les réseaux customuserland-proxy: Désactiver améliore les performances (utilise iptables directement)icc(Inter-Container Communication) :falseisole les conteneurs entre eux par défautip-forward: Active le forwarding IP (nécessaire pour NAT)
4. Options de sécurité
Section titled “4. Options de sécurité”{ "no-new-privileges": true, "selinux-enabled": true, "userns-remap": "default", "seccomp-profile": "/etc/docker/seccomp-custom.json", "default-ulimits": { "nofile": { "Hard": 64000, "Soft": 64000 }, "nproc": { "Hard": 4096, "Soft": 2048 } }}Détails :
no-new-privileges: Empêche l’escalade de privilèges (setuid, etc.)userns-remap: Active les user namespaces (root dans le conteneur != root sur l’hôte)seccomp-profile: Profil de filtrage des appels systèmedefault-ulimits: Limites par défaut (fichiers ouverts, processus)
5. Options de performance
Section titled “5. Options de performance”{ "live-restore": true, "max-concurrent-downloads": 10, "max-concurrent-uploads": 10, "max-download-attempts": 5, "shutdown-timeout": 15}Explications :
live-restore: Crucial ! Les conteneurs continuent de tourner pendant le redémarrage du daemonmax-concurrent-downloads: Nombre de layers téléchargés en parallèleshutdown-timeout: Délai avant SIGKILL lors de l’arrêt (seconds)
Docker et containerd : comprendre la relation
Section titled “Docker et containerd : comprendre la relation”Architecture en couches
Section titled “Architecture en couches”┌─────────────────────────────────┐│ Docker CLI (docker) │└────────────┬────────────────────┘ │ ▼┌─────────────────────────────────┐│ Docker Engine (dockerd) ││ - API REST ││ - Images management ││ - Networking ││ - Volumes │└────────────┬────────────────────┘ │ (gRPC) ▼┌─────────────────────────────────┐│ containerd ││ - Container lifecycle ││ - Image pull/push ││ - Network namespaces │└────────────┬────────────────────┘ │ ▼┌─────────────────────────────────┐│ runc (ou autre runtime) ││ - OCI runtime ││ - Création des conteneurs ││ - Namespaces/cgroups │└─────────────────────────────────┘containerd : le runtime de haut niveau
Section titled “containerd : le runtime de haut niveau”containerd est un daemon qui gère le cycle de vie complet des conteneurs :
- Pull et push d’images
- Stockage des images
- Exécution et supervision des conteneurs
- Gestion des snapshots de filesystem
- Networking de bas niveau
Configuration de containerd
Section titled “Configuration de containerd”Fichier : /etc/containerd/config.toml
version = 2
[plugins] [plugins."io.containerd.grpc.v1.cri"] sandbox_image = "registry.k8s.io/pause:3.9"
[plugins."io.containerd.grpc.v1.cri".containerd] default_runtime_name = "runc"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc] runtime_type = "io.containerd.runc.v2"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options] SystemdCgroup = true
# Runtime alternatif : gVisor [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runsc] runtime_type = "io.containerd.runsc.v1"
[plugins."io.containerd.internal.v1.opt"] path = "/var/lib/containerd/opt"Interaction Docker ↔ containerd
Section titled “Interaction Docker ↔ containerd”Docker communique avec containerd via gRPC :
# Voir les conteneurs dans containerdsudo ctr -n moby containers list
# Voir les tâches (processus) activessudo ctr -n moby tasks list
# Namespace "moby" = namespace Docker dans containerdNote importante : Le namespace moby est utilisé par Docker dans containerd.
runc : le runtime OCI de bas niveau
Section titled “runc : le runtime OCI de bas niveau”runc est l’implémentation de référence de l’OCI (Open Container Initiative) :
- Crée et lance les conteneurs selon la spec OCI
- Configure les namespaces, cgroups, capabilities
- Exécute le processus init du conteneur
Tester runc directement
Section titled “Tester runc directement”# Créer un bundle OCImkdir -p /tmp/mycontainer/rootfscd /tmp/mycontainer
# Extraire un rootfsdocker export $(docker create busybox) | tar -C rootfs -xvf -
# Générer la config OCIrunc spec
# Lancer le conteneursudo runc run mycontainerRuntimes alternatifs
Section titled “Runtimes alternatifs”1. gVisor (runsc) - Sandboxing renforcé
Section titled “1. gVisor (runsc) - Sandboxing renforcé”gVisor ajoute une couche de sécurité en implémentant un kernel user-space :
{ "runtimes": { "runsc": { "path": "/usr/local/bin/runsc", "runtimeArgs": [ "--platform=ptrace" ] } }}Utilisation :
docker run --runtime=runsc -it alpine shAvantages :
- Isolation renforcée (kernel simulé)
- Réduit la surface d’attaque du kernel
Inconvénients :
- Performance réduite (~30-50% plus lent)
- Incompatibilité avec certains appels système
2. Kata Containers - VM légères
Section titled “2. Kata Containers - VM légères”Kata Containers lance chaque conteneur dans une micro-VM :
{ "runtimes": { "kata-runtime": { "path": "/usr/bin/kata-runtime" } }}Avantages :
- Isolation forte (VM réelle)
- Compatible avec Kubernetes
Inconvénients :
- Overhead de démarrage
- Consommation mémoire plus élevée
3. crun - Runtime en C
Section titled “3. crun - Runtime en C”Alternative à runc, écrit en C (runc est en Go) :
{ "default-runtime": "crun", "runtimes": { "crun": { "path": "/usr/bin/crun" } }}Avantages :
- Plus rapide et plus léger
- Moins de mémoire consommée
Hardening d’une installation Docker
Section titled “Hardening d’une installation Docker”1. Sécuriser le daemon Docker
Section titled “1. Sécuriser le daemon Docker”Désactiver l’accès TCP non sécurisé
Section titled “Désactiver l’accès TCP non sécurisé”❌ DANGEREUX :
# NE JAMAIS faire ceci sans TLS !dockerd -H tcp://0.0.0.0:2375✅ SÉCURISÉ : Utiliser TLS
# Générer les certificatsopenssl genrsa -aes256 -out ca-key.pem 4096openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem
# Certificat serveuropenssl genrsa -out server-key.pem 4096openssl req -subj "/CN=docker.example.com" -sha256 -new -key server-key.pem -out server.csrecho subjectAltName = DNS:docker.example.com,IP:10.0.0.1 >> extfile.cnfecho extendedKeyUsage = serverAuth >> extfile.cnfopenssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem \ -CAcreateserial -out server-cert.pem -extfile extfile.cnf
# Certificat clientopenssl genrsa -out key.pem 4096openssl req -subj '/CN=client' -new -key key.pem -out client.csrecho extendedKeyUsage = clientAuth >> extfile-client.cnfopenssl x509 -req -days 365 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem \ -CAcreateserial -out cert.pem -extfile extfile-client.cnfConfiguration dans /etc/docker/daemon.json :
{ "hosts": ["tcp://0.0.0.0:2376"], "tls": true, "tlsverify": true, "tlscacert": "/etc/docker/certs/ca.pem", "tlscert": "/etc/docker/certs/server-cert.pem", "tlskey": "/etc/docker/certs/server-key.pem"}Activer les user namespaces
Section titled “Activer les user namespaces”Le user namespace remapping transforme l’UID root (0) du conteneur en un UID non-privilégié sur l’hôte.
{ "userns-remap": "default"}Docker créera automatiquement un utilisateur dockremap :
# Vérifiercat /etc/subuidcat /etc/subgid
# Devrait afficherdockremap:100000:65536Impact : root dans le conteneur = UID 100000 sur l’hôte !
# Testerdocker run --rm -it alpine id# uid=0(root) ... dans le conteneur
# Sur l'hôteps aux | grep "sleep 1000"# Processus appartient à l'UID 100000 !2. Limiter les ressources (cgroups v2)
Section titled “2. Limiter les ressources (cgroups v2)”Configuration système
Section titled “Configuration système”Vérifier cgroups v2 :
mount | grep cgroup# doit afficher : cgroup2 on /sys/fs/cgroup type cgroup2Configuration dans /etc/docker/daemon.json :
{ "exec-opts": ["native.cgroupdriver=systemd"], "default-ulimits": { "nofile": { "Hard": 64000, "Soft": 32000 }, "nproc": { "Hard": 512, "Soft": 256 }, "memlock": { "Hard": -1, "Soft": -1 } }}Limites au niveau conteneur
Section titled “Limites au niveau conteneur”# CPU (1 core = 1024 shares)docker run -d --cpus="1.5" --cpu-shares=512 nginx
# Mémoiredocker run -d --memory="512m" --memory-swap="1g" --memory-reservation="256m" nginx
# IO disquedocker run -d --device-read-bps /dev/sda:10mb --device-write-bps /dev/sda:5mb nginx
# PIDsdocker run -d --pids-limit 200 nginx3. Sécuriser avec AppArmor et SELinux
Section titled “3. Sécuriser avec AppArmor et SELinux”AppArmor (Ubuntu/Debian)
Section titled “AppArmor (Ubuntu/Debian)”Profil par défaut : /etc/apparmor.d/docker
# Vérifier AppArmorsudo aa-status | grep docker
# Créer un profil customsudo nano /etc/apparmor.d/docker-custom
# Appliquersudo apparmor_parser -r -W /etc/apparmor.d/docker-customUtiliser un profil :
docker run --security-opt apparmor=docker-custom nginxSELinux (RHEL/CentOS/Fedora)
Section titled “SELinux (RHEL/CentOS/Fedora)”Activer SELinux pour Docker :
{ "selinux-enabled": true}Labels SELinux :
# Appliquer un label customdocker run -v /data:/data:z nginx# :z = relabel private# :Z = relabel shared
# Vérifierls -lZ /data4. Profil Seccomp personnalisé
Section titled “4. Profil Seccomp personnalisé”Seccomp filtre les appels système autorisés. Docker utilise un profil par défaut qui bloque ~44 appels système dangereux.
Profil custom restrictif
Section titled “Profil custom restrictif”Créer /etc/docker/seccomp-strict.json :
{ "defaultAction": "SCMP_ACT_ERRNO", "architectures": [ "SCMP_ARCH_X86_64", "SCMP_ARCH_X86", "SCMP_ARCH_AARCH64" ], "syscalls": [ { "names": [ "accept", "accept4", "access", "arch_prctl", "bind", "brk", "chdir", "clone", "close", "connect", "dup", "dup2", "dup3", "epoll_create", "epoll_create1", "epoll_ctl", "epoll_wait", "execve", "exit", "exit_group", "fchdir", "fchown", "fcntl", "fstat", "fsync", "futex", "getcwd", "getdents", "getdents64", "getpid", "getppid", "getuid", "ioctl", "listen", "lseek", "mmap", "mprotect", "munmap", "open", "openat", "pipe", "pipe2", "poll", "prctl", "read", "readlink", "recvfrom", "recvmsg", "rt_sigaction", "rt_sigprocmask", "rt_sigreturn", "sendmsg", "sendto", "set_robust_list", "set_tid_address", "setgid", "setgroups", "setuid", "socket", "stat", "statfs", "wait4", "write" ], "action": "SCMP_ACT_ALLOW" } ]}Utiliser :
docker run --security-opt seccomp=/etc/docker/seccomp-strict.json nginx5. Audit et monitoring
Section titled “5. Audit et monitoring”Docker Bench Security
Section titled “Docker Bench Security”Outil officiel de Docker pour auditer la sécurité :
# Installationgit clone https://github.com/docker/docker-bench-security.gitcd docker-bench-security
# Exécutionsudo ./docker-bench-security.shAnalyse :
- Configuration du daemon
- Configuration réseau
- Images
- Conteneurs en cours d’exécution
- Fichiers de configuration
Auditd pour tracer les événements Docker
Section titled “Auditd pour tracer les événements Docker”# Installer auditdsudo apt install auditd
# Règles pour Dockersudo nano /etc/audit/rules.d/docker.rulesContenu :
-w /usr/bin/dockerd -k docker-w /var/lib/docker -k docker-w /etc/docker -k docker-w /usr/lib/systemd/system/docker.service -k docker-w /usr/lib/systemd/system/docker.socket -k docker-w /etc/default/docker -k docker-w /etc/docker/daemon.json -k docker-w /usr/bin/containerd -k docker-w /usr/bin/runc -k dockerActiver :
sudo augenrules --loadsudo systemctl restart auditd
# Rechercher les événementssudo ausearch -k docker6. Bonnes pratiques générales
Section titled “6. Bonnes pratiques générales”Checklist de hardening
Section titled “Checklist de hardening”- Désactiver l’API TCP non-TLS ou activer TLS mutuel
- Activer user namespaces (
userns-remap) - Limiter les ressources (CPU, mémoire, PIDs) par défaut
- Utiliser un runtime sécurisé (gVisor, Kata) pour workloads sensibles
- Activer AppArmor/SELinux
- Profil Seccomp restrictif pour conteneurs sensibles
- Ne jamais monter
/var/run/docker.sockdans un conteneur (ou via docker-socket-proxy) - Scanner les images avec Trivy, Clair, Snyk
- Rootless mode pour utilisateurs non-privilégiés
- Logs centralisés (syslog, journald, ou driver distant)
- Rotation des logs configurée
- Monitoring (Prometheus + cAdvisor)
- Audit régulier avec Docker Bench Security
- Mettre à jour Docker et containerd régulièrement
- Utiliser des images minimales (Alpine, Distroless)
- Ne pas utiliser
:latesten production
Configuration production recommandée
Section titled “Configuration production recommandée”/etc/docker/daemon.json :
{ "log-driver": "journald", "log-opts": { "tag": "docker/{{.Name}}" }, "storage-driver": "overlay2", "data-root": "/mnt/docker-data", "exec-opts": ["native.cgroupdriver=systemd"], "live-restore": true, "userland-proxy": false, "no-new-privileges": true, "icc": false, "userns-remap": "default", "selinux-enabled": true, "default-ulimits": { "nofile": { "Hard": 64000, "Soft": 64000 }, "nproc": { "Hard": 4096, "Soft": 2048 } }, "default-address-pools": [ { "base": "172.80.0.0/16", "size": 24 } ], "max-concurrent-downloads": 10, "max-concurrent-uploads": 5, "shutdown-timeout": 15, "runtimes": { "runsc": { "path": "/usr/local/bin/runsc" } }}7. Ressources et outils
Section titled “7. Ressources et outils”Outils de sécurité
Section titled “Outils de sécurité”-
Trivy : Scanner de vulnérabilités pour images et filesystems
Terminal window trivy image nginx:latest -
Falco : Détection de comportements anormaux runtime
Terminal window docker run -it --rm \--privileged \-v /var/run/docker.sock:/host/var/run/docker.sock \-v /dev:/host/dev \-v /proc:/host/proc:ro \falcosecurity/falco -
Docker Scout : Analyse de sécurité intégrée
Terminal window docker scout cves nginx:latest
Documentation officielle
Section titled “Documentation officielle”- Docker Security
- Docker Daemon Configuration
- containerd Documentation
- OCI Runtime Specification
- CIS Docker Benchmark
Exercices pratiques
Section titled “Exercices pratiques”Exercice 1 : Configuration de base
Section titled “Exercice 1 : Configuration de base”- Créer un fichier
/etc/docker/daemon.jsonavec rotation de logs - Redémarrer Docker et vérifier la configuration
- Lancer un conteneur et vérifier que les logs sont limités
Exercice 2 : User namespaces
Section titled “Exercice 2 : User namespaces”- Activer
userns-remap - Lancer un conteneur avec un processus
sleep 1000 - Vérifier sur l’hôte que le processus appartient à un UID > 100000
Exercice 3 : Audit de sécurité
Section titled “Exercice 3 : Audit de sécurité”- Exécuter Docker Bench Security
- Identifier 5 recommandations
- Implémenter les corrections
Exercice 4 : Runtime gVisor
Section titled “Exercice 4 : Runtime gVisor”- Installer gVisor (runsc)
- Configurer Docker pour utiliser runsc
- Comparer les performances avec runc
Conclusion
Section titled “Conclusion”La configuration avancée et le hardening de Docker sont essentiels pour une utilisation en production sécurisée.
Points clés à retenir :
- Le daemon Docker a de nombreuses options pour la sécurité, les performances et la gestion
- containerd et runc sont des couches séparées que vous pouvez configurer indépendamment
- Les user namespaces, AppArmor/SELinux, et Seccomp sont vos meilleurs alliés
- Auditez régulièrement avec Docker Bench Security
- Restez à jour et scannez vos images
La sécurité est un processus continu, pas une configuration ponctuelle !