MDstable
NoteSnippetChecklistPlaybook

Hardening Docker & Conteneurs

Sécuriser les conteneurs : rootless, capabilities, seccomp, AppArmor, images, secrets

snippetintermediate 2025-05-14 7 min read
hardeningdockercontainerssecurityrootlesscapabilitiesseccompapparmorimage-scanning

Images sécurisées

bash
Variables
{{IMAGE}}
{{TAG}}
# Scanner les vulnérabilités d'une image
docker scout cves {{IMAGE}}{{TAG}}
trivy image {{IMAGE}}{{TAG}}
grype {{IMAGE}}{{TAG}}
# Scanner avec un seuil de sévérité (CI/CD — fail si CRITICAL)
trivy image --exit-code 1 --severity CRITICAL {{IMAGE}}{{TAG}}
# Inspecter le contenu d'une image
docker sbom {{IMAGE}}{{TAG}} # Software Bill of Materials
syft {{IMAGE}}{{TAG}} # SBOM alternatif
dive {{IMAGE}}{{TAG}} # Analyser les layers
# Mettre à jour les images
docker pull {{IMAGE}}{{TAG}} # Récupérer la dernière version
# Puis rebuilder les conteneurs qui en dépendent
dockerfile
# Dockerfile sécurisé — bonnes pratiques
FROM alpine:3.20 AS builder
# ... build steps ...
# Stage final minimal
FROM gcr.io/distroless/static-debian12
# Ou : FROM scratch (pour des binaires statiques Go/Rust)
# Copier uniquement les artefacts nécessaires
COPY --from=builder /app/binary /app/binary
# Utilisateur non-root dédié
RUN addgroup -g 10001 appgroup && adduser -u 10001 -G appgroup -s /bin/sh -D appuser
# Pour distroless : USER avec UID directement
USER 10001:10001
# Pas de secrets dans les ARG/ENV
# ARG API_KEY ← dangereux, visible dans docker history
# Utiliser --secret ou des volumes au runtime
EXPOSE 8080
ENTRYPOINT ["/app/binary"]
⚠ Attention —

Ne jamais mettre de credentials, tokens ou clés dans un Dockerfile — même supprimés dans un layer ultérieur, ils restent visibles via docker history --no-trunc IMAGE. Utiliser BuildKit secrets : RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret.

Exécution rootless

bash
Variables
{{IMAGE}}
{{CONTAINER}}
# Installer Docker rootless (daemon sans root)
dockerd-rootless-setuptool.sh install
# Vérifier que le daemon tourne en rootless
systemctl --user status docker
docker info | grep -i rootless
# Podman — rootless nativement (drop-in replacement Docker)
podman run --rm {{IMAGE}} id
# uid=1000(user) gid=1000(user) groups=1000(user) ← pas de root
# Forcer un utilisateur non-root à l'exécution
docker run --user 10001000 {{IMAGE}} id
# Vérifier l'utilisateur d'un conteneur en cours
docker inspect {{CONTAINER}} --format='{{.Config.User}}'

Capabilities Linux

bash
Variables
{{IMAGE}}
{{CONTAINER}}
# Principe : drop ALL, ajouter uniquement ce qui est nécessaire
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE {{IMAGE}}
# Capabilities couramment nécessaires (et seulement elles)
# NET_BIND_SERVICE : écouter sur les ports < 1024
# CHOWN : changer le propriétaire de fichiers
# SETUID, SETGID : changer l'UID/GID du processus
# DAC_OVERRIDE : bypass des permissions sur les fichiers (à éviter)
# Capabilities dangereuses — ne JAMAIS accorder en production
# CAP_SYS_ADMIN : quasi-root, permet mount, namespace, etc.
# CAP_NET_ADMIN : modifier la config réseau de l'hôte
# CAP_SYS_PTRACE : attacher à des processus (débogage)
# CAP_DAC_OVERRIDE: bypass complet des ACLs fichiers
# CAP_SYS_MODULE : charger des modules kernel
# Lister les capabilities d'un conteneur en cours
docker inspect {{CONTAINER}} --format='{{.HostConfig.CapAdd}} HostConfigCapDrop
# Vérifier les capabilities depuis l'intérieur du conteneur
capsh --print
cat /proc/1/status | grep Cap
🔴 Danger —

docker run --privileged donne au conteneur un accès complet à l'hôte : tous les devices, toutes les capabilities, bypass des namespaces. Un conteneur privileged compromis = hôte compromis. Ne jamais utiliser en production, même temporairement.

Seccomp

bash
Variables
{{CONTAINER}}
{{IMAGE}}
# Le profil seccomp par défaut de Docker bloque ~44 syscalls dangereux
# Vérifier le profil actif d'un conteneur
docker inspect --format='{{.HostConfig.SecurityOpt}}' {{CONTAINER}}
# Appliquer un profil seccomp restrictif custom
docker run --security-opt seccomp/path/to/profile.json {{IMAGE}}
# Désactiver seccomp (à éviter — uniquement pour debug)
docker run --security-opt seccompunconfined {{IMAGE}}
json
{
"defaultAction": "SCMP_ACT_ERRNO",
"architectures": ["SCMP_ARCH_X86_64"],
"syscalls": [
{
"names": ["read", "write", "close", "fstat", "mmap", "mprotect",
"munmap", "brk", "rt_sigaction", "rt_sigprocmask",
"exit", "exit_group", "futex", "nanosleep",
"clock_gettime", "gettimeofday", "getpid", "getuid",
"getgid", "geteuid", "getegid", "socket", "connect",
"sendto", "recvfrom", "sendmsg", "recvmsg", "bind",
"listen", "accept", "accept4", "epoll_create",
"epoll_create1", "epoll_ctl", "epoll_wait",
"epoll_pwait", "openat", "open", "stat", "lstat",
"access", "ioctl", "fcntl", "dup", "dup2", "pipe"],
"action": "SCMP_ACT_ALLOW"
}
]
}

AppArmor / SELinux

bash
Variables
{{IMAGE}}
{{CONTAINER}}
# AppArmor — profil par défaut Docker
docker run --security-opt apparmordocker-default {{IMAGE}}
# Appliquer un profil AppArmor custom
# 1. Créer le profil
cat > /etc/apparmor.d/docker-custom << 'EOF'
#include <tunables/global>
profile docker-custom flagsattach_disconnectedmediate_deleted
#include <abstractions/base>
network
capability
file
umount
deny PROC w
deny /sys/f wklx
deny /sys/fs wklx
deny /sys/fs/c wklx
deny /sys/fs/cg wklx
deny /sys/fs/cgr wklx
deny /sys/firmware/ rwklx
deny /sys/kernel/security/ rwklx
EOF
# 2. Charger le profil
apparmor_parser -r -W /etc/apparmor.d/docker-custom
# 3. Appliquer
docker run --security-opt apparmordocker-custom {{IMAGE}}
# SELinux — label de type conteneur
docker run --security-opt labeltypecontainer_t {{IMAGE}}
# Vérifier les labels SELinux
docker inspect {{CONTAINER}} --format='{{.HostConfig.SecurityOpt}}'

Réseau

bash
Variables
{{IMAGE}}
{{CONTAINER}}
# Pas de réseau si inutile
docker run --network=none {{IMAGE}}
# Network Docker custom (isolation entre conteneurs)
docker network create --driver bridge --opt comdockernetworkbridgeenable_iccfalse app-network
docker run --network=app-network {{IMAGE}}
# Exposer uniquement sur localhost (jamais 0.0.0.0 en prod)
docker run -p 12700180808080 {{IMAGE}}
# Inspecter le réseau d'un conteneur
docker inspect {{CONTAINER}} --format='{{json NetworkSettingsNetworks | jq
# Désactiver ICC dans daemon.json (Inter-Container Communication)
# Voir section daemon.json ci-dessous
⚠ Attention —

--network=host supprime l'isolation réseau du conteneur — il partage la stack réseau de l'hôte. Toute écoute sur un port dans le conteneur est directement exposée sur l'hôte. À éviter absolument en production.

Secrets management

bash
Variables
{{SECRET_VALUE}}
{{IMAGE}}
# Docker Secrets (Swarm mode)
echo "{{SECRET_VALUE}}" | docker secret create my-secret
docker service create --secret my-secret {{IMAGE}}
# Disponible dans le conteneur : /run/secrets/my-secret
# BuildKit secrets (build time — ne pas persister dans les layers)
docker build --secret idmysecretsrc/secret.txt -t {{IMAGE}}
# Dans le Dockerfile :
# RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret
# Volume read-only pour les secrets
docker run -v /host/secrets/myapp/run/secretsro {{IMAGE}}
# HashiCorp Vault Agent sidecar
# Vault Agent s'authentifie auprès de Vault et écrit les secrets dans un volume partagé
🔴 Danger —

Ne jamais passer de secrets via variables d'environnement (-e SECRET=value) — docker inspect les expose en clair à quiconque peut exécuter cette commande sur l'hôte. Les variables d'environnement sont aussi visibles dans /proc/PID/environ depuis l'intérieur du conteneur.

Filesystem et volumes

bash
Variables
{{IMAGE}}
{{CONTAINER}}
# Root filesystem en lecture seule + tmpfs pour les répertoires qui nécessitent des écritures
docker run
--read-only
--tmpfs /tmprwnoexecnosuidsize
--tmpfs /var/runrwnoexecnosuidsize
{{IMAGE}}
# Empêcher l'escalade de privilèges (sudo, setuid)
docker run --no-new-privileges {{IMAGE}}
# Monter les volumes en lecture seule si possible
docker run -v /host/data/app/dataro {{IMAGE}}
# Vérifier les options de sécurité appliquées
docker inspect {{CONTAINER}} --format='
ReadOnlyRootfs HostConfigReadonlyRootfs
NoNewPrivileges HostConfigSecurityOpt
Privileged HostConfigPrivileged

daemon.json hardening

json
{
"icc": false,
"no-new-privileges": true,
"userns-remap": "default",
"live-restore": true,
"userland-proxy": false,
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
},
"storage-driver": "overlay2",
"seccomp-profile": "/etc/docker/seccomp-profile.json"
}
bash
# Appliquer la configuration
# Éditer /etc/docker/daemon.json puis :
systemctl restart docker
# Vérifier la configuration active
docker info | grep -A5 "Security Options"
docker info | grep "Logging Driver"

Checklist sécurité Docker

Checklist0/12
💡 Tip —

docker-bench-security (CIS Docker Benchmark) est un script qui audite automatiquement la configuration Docker de l'hôte : daemon, images, conteneurs en cours, sécurité du runtime. Exécuter avec docker run --rm -v /var/run/docker.sock:/var/run/docker.sock docker/docker-bench-security et corriger tous les [WARN].

OPS·BRAIN v1.075 notes · Securitylocal