infra / docker
Sécurité des conteneurs (non-root, capabilities, scan de vulnérabilités)
Explication
Ce que vous allez apprendre
- Comprendre pourquoi l'isolation Docker n'est pas absolue et repose sur le noyau hôte partagé
- Créer un utilisateur non-root dans un Dockerfile avec
USERet vérifier son application - Réduire les capabilities Linux d'un conteneur au strict nécessaire (principe du moindre privilège)
- Rendre un système de fichiers en lecture seule pour limiter l'impact d'une compromission
- Scanner une image à la recherche de vulnérabilités connues avec
trivyavant sa mise en production
Dans quel contexte ?
Une faille de sécurité récente permet l'exécution de code arbitraire dans une bibliothèque JavaScript très utilisée. Sur une application qui tourne en root dans son conteneur, un attaquant exploitant cette faille hérite immédiatement des droits root à l'intérieur du conteneur, ce qui facilite une tentative d'évasion vers l'hôte. Sur la même application configurée avec un utilisateur dédié non privilégié (USER appuser) et des capabilities réduites, l'impact de la même faille reste bien plus contenu.
L'isolation de Docker n'est pas absolue
Il est tentant de croire qu'un conteneur est une bulle totalement étanche, mais ce n'est pas tout à fait vrai : les conteneurs partagent le même noyau Linux que la machine hôte. Une faille dans ce noyau, ou une mauvaise configuration, peut permettre à un attaquant de "s'échapper" du conteneur et d'atteindre la machine hôte elle-même. La sécurité des conteneurs consiste donc à réduire au maximum ce qu'un conteneur compromis pourrait faire, même dans le pire des cas.
| Mesure | Effet |
|---|---|
USER appuser | Le processus ne tourne pas en root |
--cap-drop=ALL --cap-add=... | Seules les capabilities nécessaires sont accordées |
--read-only | Le filesystem ne peut pas être modifié |
trivy image | Détecte les vulnérabilités connues avant déploiement |
Bonne pratique
Applique le principe du moindre privilège systématiquement : retire TOUTES les capabilities par défaut (--cap-drop=ALL) puis ne réautorise QUE celles réellement nécessaires à l'application (--cap-add=NET_BIND_SERVICE, par exemple). Un conteneur qui n'a besoin d'aucune capability spéciale ne devrait en avoir aucune.
Le réflexe n°1 : ne jamais tourner en root
Par défaut, un conteneur exécute son processus en tant qu'utilisateur root — exactement comme l'administrateur sur une machine classique. Si l'application est compromise (faille de sécurité exploitée), l'attaquant hérite alors des droits root À L'INTÉRIEUR du conteneur, ce qui facilite grandement une tentative d'évasion vers l'hôte. Créer un utilisateur dédié sans privilège dans le Dockerfile, avec l'instruction USER, est la mesure la plus simple et la plus efficace.
Les capabilities : donner uniquement ce qui est nécessaire
Sous Linux, les droits root ne sont pas un bloc unique : ils se décomposent en dizaines de permissions précises appelées "capabilities" (par exemple, le droit de se lier à un port réseau privilégié). Plutôt que de tout retirer ou tout garder, la bonne pratique consiste à retirer TOUTES les capacités par défaut, puis à ne réautoriser QUE celles réellement nécessaires à l'application — un principe qu'on appelle le "moindre privilège".
Le filesystem en lecture seule, une défense simple
Rendre le système de fichiers du conteneur non modifiable (sauf exceptions explicites via tmpfs) empêche un attaquant d'installer des outils malveillants ou de modifier des fichiers de l'application, même s'il parvient à exécuter du code.
Scanner : trouver les failles avant qu'un attaquant ne les trouve
Une image construite aujourd'hui peut contenir des dépendances avec des vulnérabilités déjà connues publiquement. Des outils comme trivy comparent le contenu exact d'une image à des bases de vulnérabilités, et peuvent bloquer automatiquement un pipeline si une faille critique est détectée — bien plus fiable qu'une vérification manuelle.
Commandes & code
Sécurité des conteneurs
# --- Ne jamais tourner en root dans le conteneur : créer un utilisateur dédié ---
FROM python:3.12-slim
RUN groupadd -r appuser && useradd -r -g appuser -d /app appuser
WORKDIR /app
COPY --chown=appuser:appuser . .
RUN pip install --no-cache-dir -r requirements.txt
USER appuser
# Toute commande RUN après "USER appuser" s'exécute avec ces droits, tout comme le CMD final
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0"]# Vérifier qu'un conteneur ne tourne PAS en root
docker exec mon-api whoami # doit répondre "appuser", pas "root"
docker inspect --format '{{.Config.User}}' mon-api
# Forcer l'utilisateur au run même si le Dockerfile ne le fait pas (défense en profondeur)
docker run -d --user 1000:1000 mon-api:1.0
# --- Capabilities Linux : réduire les privilèges au strict minimum ---
docker run -d --cap-drop=ALL --cap-add=NET_BIND_SERVICE mon-api:1.0
# --cap-drop=ALL retire TOUTES les capacités, --cap-add en réautorise une précisément (ex: bind port <1024)
# --- Filesystem en lecture seule : empêche toute écriture inattendue dans le conteneur ---
docker run -d --read-only --tmpfs /tmp mon-api:1.0
# --tmpfs /tmp fournit un espace inscriptible temporaire pour les besoins ponctuels (cache, uploads)
# --- Empêcher l'élévation de privilèges à l'intérieur du conteneur ---
docker run -d --security-opt=no-new-privileges mon-api:1.0
# --- Ne jamais utiliser --privileged sauf nécessité absolue (accès matériel complet de l'hôte) ---
docker run -d --privileged mon-outil-systeme # à éviter en production, quasi équivalent à root sur l'hôte
# --- Limiter les appels système autorisés avec un profil seccomp custom ---
docker run -d --security-opt seccomp=./profil-restreint.json mon-api:1.0
# --- Scan de vulnérabilités des images ---
docker scout cves mon-api:1.0 # intégré à Docker Desktop/CLI récent
trivy image mon-api:1.0 # scanner open-source très répandu
trivy image --severity HIGH,CRITICAL mon-api:1.0 # filtre sur les vulnérabilités les plus graves
grype mon-api:1.0 # alternative à trivy
# Intégrer un scan dans un pipeline CI, en échouant le build si vulnérabilités critiques
trivy image --exit-code 1 --severity CRITICAL mon-api:1.0
# Vérifier qu'aucun secret n'a fui dans les couches de l'image
trivy image --scanners secret mon-api:1.0Résumé
USER appuserdans le Dockerfile est la première ligne de défense : ne jamais laisser un conteneur tourner en root.--cap-drop=ALL+--cap-addciblé,--read-only,--security-opt=no-new-privilegesréduisent la surface d'attaque.- Scanner systématiquement les images (
trivy,docker scout) en CI, y compris pour des secrets qui auraient fuité dans une couche.
Exercices pratiques
Mission : contenir les dégâts d'une faille exploitée dans un conteneur
Objectif : Réduire drastiquement les privilèges d'un conteneur compromis pour limiter l'impact d'une faille déjà connue.
Contexte
Une faille critique vient d'être découverte dans une bibliothèque JavaScript utilisée par mon-api, qui tourne actuellement en root, avec toutes les capabilities par défaut et un filesystem inscriptible. Un correctif n'est pas encore disponible : il faut réduire l'impact potentiel en attendant.