E-NO
DevOps 8 min de lecture

Durcissement de la sécurité des conteneurs Docker pour des valeurs par défaut d’exécution plus sûres : guide de mise en œuvre pratique

calendar_today Publié : 2026-07-08
update Dernière mise à jour : 2026-07-08
analytics Efficacité SEO : 97%
Illustration du guide technique pour « Durcissement de la sécurité des conteneurs Docker pour des valeurs par défaut d’exécution plus sûres : guide de mise en œuvre pratique ».

Les conteneurs par défaut s’exécutent souvent en root, peuvent écrire partout et conservent plus de capacités noyau que n’en ont besoin la plupart des applications. Cela augmente le risque en cas de compromission. Ce guide montre comment définir des valeurs par défaut Docker plus sûres avec des extraits pratiques applicables en quelques minutes : utilisateurs non-root, capacités Linux minimales, systèmes de fichiers en lecture seule avec tmpfs pour les chemins écrivables, et conservation des profils seccomp et AppArmor par défaut de Docker. Vous obtiendrez des exemples Dockerfile, docker run et Compose, ainsi qu’un plan de pilote local réduit et les pièges courants.

Vue d’ensemble du workflow

  1. Construire des images qui s’exécutent par défaut avec un utilisateur non-root. Corriger les propriétés des fichiers à la construction.
  2. Privilégier les ports élevés dans le conteneur et mapper depuis l’hôte.
  3. Supprimer toutes les capacités Linux, puis n’ajouter que le strict nécessaire.
  4. Rendre le système de fichiers du conteneur en lecture seule et fournir tmpfs pour les besoins d’écriture à l’exécution comme /tmp.
  5. Conserver le profil seccomp par défaut de Docker et le profil AppArmor docker-default quand disponibles.
  6. Ajouter des limites d’exécution comme no-new-privileges, pids-limit et plafonds de ressources.
  7. Tester localement avec un pilote réduit et mesurable avant d’élargir.

Dockerfile : non-root et propriétés

Créer un utilisateur et un groupe dédiés, corriger les propriétés pendant la construction et exécuter en tant que cet utilisateur. Exemple pour un service web générique à l’écoute sur 8080 :

# syntax=docker/dockerfile:1
FROM debian:12-slim

# Installer seulement le nécessaire (ex. dépendances runtime)
RUN apt-get update \
 && apt-get install -y --no-install-recommends ca-certificates \
 && rm -rf /var/lib/apt/lists/*

# Créer l’utilisateur et les répertoires de l’application
RUN groupadd --system app && useradd --system --create-home --gid app app
WORKDIR /app

# Copier les fichiers de l’application et définir la propriété
COPY --chown=app:app . /app

# Exposer un port élevé pour éviter des capacités supplémentaires
EXPOSE 8080

# Abandonner root au moment de la construction
USER app:app

# Utiliser un point d’entrée simple
CMD [

Notes :

  • Utiliser --chown lors du COPY pour que les fichiers soient écrivables par l’utilisateur de l’application si besoin.
  • Privilégier les ports élevés comme 8080 à l’intérieur du conteneur. Mapper le port hôte 80 sur 8080 au lancement, évitant NET_BIND_SERVICE.
  • Les constructions multi-étapes gardent les images plus petites et réduisent la surface d’attaque.

Capacités et réseau

Les conteneurs héritent d’un ensemble par défaut de capacités Linux. Les supprimer par défaut et n’ajouter que ce dont l’application a vraiment besoin.

Modèle plus sûr quand le réseau privilégié n’est pas requis :

docker run --rm \
  --cap-drop ALL \
  --security-opt no-new-privileges=true \
  -p 80:8080 myapp:latest

Si vous devez lier un port bas (par ex. 80) à l’intérieur du conteneur, soit :

  • Privilégier le mapping hôte : -p 80:8080 et garder CAP_NET_BIND_SERVICE absent.
  • Ou ajouter seulement la capacité spécifique :
docker run --rm \
  --cap-drop ALL --cap-add NET_BIND_SERVICE \
  --security-opt no-new-privileges=true \
  -p 80:80 myapp:latest

Éviter les conteneurs privilégiés et éviter d’ajouter des capacités larges comme SYS_ADMIN sauf raison claire, minimale et auditée.

Systèmes de fichiers en lecture seule et tmpfs

Un système de fichiers racine en lecture seule bloque de nombreuses attaques par écriture et réduit les dommages accidentels. L’associer à tmpfs pour les chemins écrivables dont l’application a besoin à l’exécution.

Exemple d’exécution :

docker run --rm \
  --read-only \
  --tmpfs /tmp:rw,noexec,nosuid,nodev,size=64m \
  --tmpfs /var/run:rw,noexec,nosuid,nodev,size=16m \
  -p 80:8080 myapp:latest

Conseils :

  • Diriger les journaux de l’application vers stdout/stderr. Docker les capture.
  • Si l’application doit persister des données, monter un volume explicite ou un bind mount sur un seul chemin, en lecture-écriture si nécessaire et lecture seule ailleurs.
  • Utiliser noexec, nosuid, nodev sur tmpfs quand possible.

Seccomp et AppArmor par défaut

Docker applique un profil seccomp par défaut qui bloque de nombreux appels systèmes risqués. Conserver ce profil sauf besoin avéré de l’assouplir. Éviter unconfined.

  • Seccomp : Le profil par défaut s’applique automatiquement. Ne pas définir --security-opt seccomp=unconfined. Fournir un profil seccomp personnalisé seulement si un cas spécifique l’exige.
  • AppArmor : Sur les hôtes avec AppArmor activé, le profil docker-default s’applique. Le conserver, ou lier un profil plus strict avec --security-opt apparmor=docker-default (ou un nom de profil personnalisé) quand disponible.

Exemple Compose qui conserve les défauts et ajoute no-new-privileges :

services:
  web:
    image: myapp:latest
    ports:
      - "80:8080"
    security_opt:
      - "no-new-privileges=true"
      - "apparmor:docker-default"  # si AppArmor est activé sur l’hôte

Si vous fournissez un fichier de profil seccomp personnalisé, le référencer explicitement :

docker run --rm \
  --security-opt seccomp=./seccomp-profile.json \
  myapp:latest

Exemple minimal de profil seccomp personnalisé (autoriser seulement ce dont l’application a besoin) :

{
  "defaultAction": "SCMP_ACT_ERRNO",
  "architectures": ["SCMP_ARCH_X86_64", "SCMP_ARCH_X86", "SCMP_ARCH_X32"],
  "syscalls": [
    {
      "names": ["read", "write", "open", "close", "exit", "exit_group"],
      "action": "SCMP_ACT_ALLOW"
    }
  ]
}

Exemples d’exécution et Compose

Regrouper plusieurs valeurs par défaut plus sûres :

exemple docker run :

docker run --rm \
  --user 1000:1000 \
  --cap-drop ALL \
  --security-opt no-new-privileges=true \
  --read-only \
  --tmpfs /tmp:rw,noexec,nosuid,nodev,size=64m \
  --pids-limit 200 \
  --cpus 1 --memory 256m \
  -p 80:8080 myapp:latest

Extrait Compose équivalent :

services:
  web:
    image: myapp:latest
    user: "1000:1000"
    ports:
      - "80:8080"
    read_only: true
    tmpfs:
      -
    cap_drop:
      - ALL
    security_opt:
      - "no-new-privileges=true"
      - "apparmor:docker-default"
    deploy:
      resources:
        limits:
          cpus: "1"
          memory: 256M

Note : Sur les hôtes sans AppArmor, retirer la ligne AppArmor. Les limites de ressources sous deploy nécessitent un orchestrateur supporté°; pour Compose local, on peut aussi utiliser mem_limit et cpus avec la syntaxe Compose v2.

Plan de pilote local

Commencer réduit et mesurable pour inspecter les résultats localement avant d’étendre.

Objectif : Exécuter un service en non-root avec système de fichiers en lecture seule, tmpfs pour /tmp, capacités supprimées et no-new-privileges. Vérifier que l’application sert encore le trafic et échoue sûrement en tentant d’écrire sur les chemins interdits.

Plan :

  1. Choisir un service simple (site statique ou API basique).
  2. Mettre à jour son Dockerfile pour ajouter un utilisateur dédié et définir USER.
  3. Lancer avec --cap-drop ALL, --read-only, tmpfs pour /tmp et no-new-privileges.
  4. Mapper le port hôte 80 sur le conteneur 8080 pour éviter des capacités supplémentaires.
  5. Vérifier :
  • Non-root : docker exec -it <container_id> id et confirmer uid != 0.
  • Système de fichiers : docker exec <container_id> sh -c 'touch /etc/blocked' doit échouer avec l’erreur "Read-only file system/”.
  • tmp écrivable : docker exec <container_id> sh -c 'touch /tmp/ok' doit réussir.
  • Trafic : curl localhost:80 doit retourner un 200.
  1. Documenter les écritures manquantes et fournir un volume ou chemin tmpfs spécifique pour elles.

Critères de succès :

  • L’application sert le trafic.
  • Les écritures sur les chemins non-tmp échouent comme prévu.
  • Aucune capacité ajoutée au-delà du requis.

Ce pilote réduit est rapide à inspecter et pose une base pour les services suivants.

Dépannage et pièges en production

Problèmes courants et correctifs :

  • Permission denied au démarrage : Les fichiers copiés en root ne sont pas écrivables par l’utilisateur de l’application. Corriger avec COPY --chown=app:app et vérifier la propriété des répertoires.
  • L’application ne peut pas écrire les journaux : Envoyer les journaux vers stdout/stderr ou monter un répertoire écrivable dédié pour les logs (ex. : --tmpfs /var/log/app:rw,noexec,nosuid,nodev,size=32m).
  • Les gestionnaires de paquets échouent à l’exécution : Avec --read-only, pas d’accès en écriture pour apt ou apk. Tout installer à la construction et garder l’exécution légère. Si installation à l’exécution indispensable, utiliser une étape de construction à la place.
  • Échec de liaison au port 80 : Utiliser -p 80:8080 et garder le conteneur sur 8080. Si liaison à 80 à l’intérieur obligatoire, ajouter seulement NET_BIND_SERVICE.
  • Fichiers temporaires cassés : Fournir tmpfs pour /tmp et les répertoires de sockets runtime comme /var/run.
  • Seccomp ou AppArmor personnalisés requis : N’assouplir que le nécessaire. Partir des défauts et ajouter des autorisations minimales.
  • Comportement incohérent entre hôtes : AppArmor peut être absent sur certains hôtes. Garder la configuration conditionnelle et tester sur l’OS cible.

Frontières de l’analyse d’images :

  • L’analyse aide à trouver des CVE connus dans les images, mais ne garantit pas des réglages d’exécution sûres. Combiner l’analyse avec les étapes de durcissement ici.
  • Les mauvaises configurations d’exécution comme tourner en root ou laisser des capacités larges sont hors portée des scanners d’images.

Conseils opérationnels :

  • Épingler les digests d’images de base (ex. : debian@sha256:...) pour réduire les changements surprises.
  • Utiliser des constructions multi-étapes pour garder les images petites.
  • Ajouter des healthchecks et limites de ressources pour arrêter les processus emballés.

Conclusion

Des valeurs par défaut Docker plus sûres sont simples : exécuter en utilisateur non-root, supprimer les capacités par défaut, garder le système de fichiers en lecture seule avec tmpfs pour les écritures requises, et s’appuyer sur les profils seccomp et AppArmor par défaut de Docker sauf besoin prouvé de personnaliser. Appliquer no-new-privileges et ajouter des limites de ressources. Commencer par un pilote local réduit, vérifier le comportement, puis déployer sur plus de services. Ces étapes réduisent le risque tout en gardant les opérations simples et inspectables.

Recherches connexes

Score de qualité de l’article

Utilité pour le lecteur 97%
  • check_circle Guide prêt à lire
  • check_circle Exemples pratiques inclus
  • check_circle URL d’article optimisée pour le SEO