Le socket Unix de Docker (/var/run/docker.sock) est l'un des secrets les mieux gardés — et les plus mal protégés — des infrastructures basées sur Docker. Monter ce fichier dans un conteneur est une pratique courante, quasi systématique avec des outils comme Traefik, Portainer ou Watchtower : ces outils ont besoin de communiquer avec le daemon Docker pour découvrir les conteneurs, lire leurs métadonnées ou les gérer. Mais cette commodité cache un risque critique que la majorité des administrateurs Docker sous-estiment ou ignorent complètement. Avoir accès au socket Docker, c'est avoir un accès root illimité à la machine hôte. Un conteneur compromis avec accès au socket peut créer de nouveaux conteneurs, monter le système de fichiers de l'hôte, modifier les configurations réseau, exfiltrer des secrets d'environnement de tous les autres conteneurs, et ultimement s'évader complètement vers l'hôte. Ce guide complet vous explique pourquoi le socket Docker est un risque critique, comment le démontrer concrètement, et surtout comment le mitiger avec Docker Socket Proxy de Tecnativa — une solution élégante qui permet à vos outils de continuer à fonctionner tout en limitant drastiquement leur accès au daemon Docker.

DEVSECOPS Docker Socket Proxy : Sécuriser l'Accès au Daemon Docker Le risque de sécurité du… 🔒 Démonstration du risque… 🔑 Qu'est-ce que Docker Socket… 📊 Déployer le Socket Proxy avec… Configurer Traefik pour… 🌐 Configurer Portainer avec le… ayinedjimi-consultants.fr

Le risque de sécurité du socket Docker (root équivalent, container escape)

Le socket Docker (/var/run/docker.sock) est l'interface de communication entre les clients Docker et le daemon dockerd. Il s'agit d'un socket Unix qui accepte des requêtes HTTP REST. Lorsque vous montez ce socket dans un conteneur avec -v /var/run/docker.sock:/var/run/docker.sock, vous donnez à ce conteneur la capacité de parler directement au daemon Docker avec les mêmes droits que le daemon lui-même — c'est-à-dire les droits root.

Le problème fondamental est que le daemon Docker tourne en root et que son API ne dispose d'aucun système de contrôle d'accès granulaire natif. Il n'existe pas de concept de "lire les métadonnées des conteneurs" sans pouvoir simultanément "créer un conteneur, monter l'hôte, et exécuter du code root". C'est tout ou rien.

Les vecteurs d'attaque sont multiples :

  • Vulnérabilité dans l'application : une faille RCE dans nginx, WordPress ou n'importe quelle application conteneurisée avec accès au socket permet à l'attaquant de pivoter vers l'hôte
  • Image malveillante : une image compromise dans votre registry contient du code malveillant qui exploite le socket au démarrage
  • Injection de commandes : une vulnérabilité d'injection de commandes dans Traefik ou Portainer (même théorique) avec accès au socket = compromission totale de l'hôte
  • Ingénierie sociale : un développeur ajoute un outil "pratique" qui monte le socket sans en mesurer les conséquences sécuritaires

Le CIS Docker Benchmark, référence de sécurité Docker publiée par le Center for Internet Security, classe l'exposition du socket Docker comme une vulnérabilité critique (niveau L1) devant être mitigée dans tout environnement de production.

Démonstration du risque (monte le socket → accède au host)

La démonstration suivante est à réaliser dans un environnement de lab isolé. Elle illustre concrètement pourquoi l'accès au socket Docker est équivalent à un accès root à l'hôte.

Depuis un conteneur ayant accès au socket Docker, l'attaque la plus simple consiste à créer un nouveau conteneur qui monte le système de fichiers de l'hôte :

# Dans un conteneur avec /var/run/docker.sock monté :
curl -s --unix-socket /var/run/docker.sock \
  http://localhost/v1.41/containers/create \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "Image": "alpine",
    "Cmd": ["chroot", "/host", "bash", "-c", "cat /etc/shadow"],
    "HostConfig": {
      "Binds": ["/:/host"],
      "Privileged": true
    }
  }'

Cette requête crée un conteneur qui monte la racine de l'hôte dans /host et exécute chroot pour accéder au système de l'hôte avec les droits root. Le fichier /etc/shadow contenant les hashes des mots de passe est ainsi accessible.

Une variante encore plus directe utilise le CLI Docker depuis un conteneur compromis :

# Si docker CLI est disponible dans le conteneur
docker run -v /:/host --rm -it alpine chroot /host /bin/bash
# Vous êtes maintenant root sur l'hôte

D'autres techniques d'exploitation incluent :

  • Lecture des variables d'environnement de tous les conteneurs (docker inspect retourne les secrets en clair)
  • Arrêt de conteneurs critiques (pare-feu, monitoring)
  • Modification de la configuration réseau des conteneurs
  • Exfiltration des volumes montés de tous les conteneurs

Cette démonstration est reproduite dans les CTF Docker et les audits de sécurité — c'est une technique bien documentée et triviale à exploiter pour n'importe quel attaquant ayant une exécution de code dans un conteneur avec le socket monté.

Qu'est-ce que Docker Socket Proxy (Tecnativa/docker-socket-proxy)

Docker Socket Proxy, développé par Tecnativa, est une solution open source qui agit comme un proxy filtrant entre les conteneurs clients (Traefik, Portainer, Watchtower) et le socket Docker réel. Plutôt que de donner un accès direct et illimité au socket, le proxy expose un socket avec seulement les endpoints de l'API Docker autorisés explicitement.

Le principe de fonctionnement est simple :

  1. Le socket Docker réel (/var/run/docker.sock) est monté uniquement dans le conteneur Socket Proxy
  2. Le Socket Proxy expose lui-même un socket filtré (ou un port TCP) accessible depuis les autres conteneurs
  3. Chaque outil (Traefik, Portainer, etc.) est reconfiguré pour utiliser le socket proxy au lieu du socket réel
  4. Le proxy bloque toutes les requêtes vers les endpoints non autorisés (création/suppression de conteneurs, volumes, etc.)

Cette architecture est disponible sur github.com/Tecnativa/docker-socket-proxy et l'image Docker est publiée sous tecnativa/docker-socket-proxy. Le projet est actif et maintenu, avec plusieurs milliers d'étoiles GitHub et une adoption large dans la communauté self-hosted.

Les variables d'environnement du proxy contrôlent les endpoints autorisés. Par défaut, tout est bloqué. Vous activez explicitement ce dont chaque outil a besoin, en respectant le principe du moindre privilège.

Déployer le Socket Proxy avec Docker Compose

La configuration de base du Socket Proxy dans un docker-compose.yml :

version: '3.8'
services:
  socket-proxy:
    image: tecnativa/docker-socket-proxy
    container_name: socket-proxy
    restart: unless-stopped
    security_opt:
      - no-new-privileges:true
    networks:
      - socket-proxy
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    environment:
      # Endpoints globaux
      CONTAINERS: 1       # Lister les conteneurs
      SERVICES: 0         # Services Docker Swarm (désactivé)
      TASKS: 0            # Tasks Swarm (désactivé)
      SECRETS: 0          # NE JAMAIS activer - expose les secrets Docker
      CONFIGS: 0
      # Informations
      INFO: 1             # /info endpoint
      EVENTS: 1           # /events endpoint (stream)
      PING: 1             # /ping health check
      VERSION: 1          # Version du daemon
      # Images
      IMAGES: 1           # Lister les images (Watchtower)
      VOLUMES: 0          # Volumes (désactivé)
      NETWORKS: 1         # Lister les réseaux (Traefik)
      # Actions dangereuses - toujours 0 en production
      ALLOW_START: 0
      ALLOW_STOP: 0
      ALLOW_RESTARTS: 0
      POST: 0             # Désactive toutes les méthodes POST

networks:
  socket-proxy:
    name: socket-proxy
    internal: true  # Pas d'accès internet depuis ce réseau

Note critique : le réseau socket-proxy doit être déclaré comme internal: true. Cela signifie que les conteneurs sur ce réseau n'ont pas accès à internet — ils peuvent seulement communiquer entre eux. Le socket proxy ne doit jamais être accessible depuis l'extérieur.

Démarrez le proxy :

docker compose up -d socket-proxy
docker logs socket-proxy --tail 20

Vérifiez que le proxy fonctionne :

curl -s http://socket-proxy:2375/v1.41/containers/json | python3 -m json.tool | head -20

Configurer Traefik pour utiliser le Socket Proxy (sans exposer le socket brut)

Traefik est l'outil le plus couramment configuré avec le socket Docker. Il a besoin d'accéder à l'API Docker pour découvrir les conteneurs et leurs labels de configuration. Sa configuration standard (-v /var/run/docker.sock:/var/run/docker.sock) est à remplacer par une connexion au Socket Proxy.

Dans la configuration statique de Traefik (traefik.yml), remplacez la configuration Docker par :

providers:
  docker:
    endpoint: "tcp://socket-proxy:2375"
    exposedByDefault: false
    network: traefik-public

Dans le docker-compose.yml de Traefik, supprimez le montage du socket et ajoutez le réseau socket-proxy :

services:
  traefik:
    image: traefik:v3.0
    # SUPPRIMER cette ligne :
    # - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - traefik-public
      - socket-proxy  # Ajout du réseau vers le proxy
    depends_on:
      - socket-proxy

Pour Traefik, les endpoints nécessaires dans le Socket Proxy sont :

CONTAINERS: 1   # Découverte des conteneurs
NETWORKS: 1     # Informations réseau
EVENTS: 1       # Événements en temps réel (création/suppression de conteneurs)
PING: 1         # Health check

Redémarrez Traefik et vérifiez dans les logs qu'il détecte bien les conteneurs via le proxy. Aucun changement de comportement n'est attendu — Traefik fonctionne de manière identique, mais sans accès au socket brut. Consultez la documentation de sécurité Docker pour les bonnes pratiques complémentaires.

Configurer Portainer avec le Socket Proxy

Portainer est l'interface web de gestion Docker la plus populaire. Par nature, il nécessite plus de droits que Traefik car il permet de gérer les conteneurs (créer, arrêter, supprimer). Cependant, vous pouvez tout de même le faire passer par le Socket Proxy avec une configuration appropriée.

Configuration Socket Proxy pour Portainer :

environment:
  # Portainer a besoin de plus de droits pour la gestion
  CONTAINERS: 1
  SERVICES: 1
  TASKS: 1
  NETWORKS: 1
  VOLUMES: 1
  IMAGES: 1
  INFO: 1
  EVENTS: 1
  PING: 1
  VERSION: 1
  # Actions de gestion
  ALLOW_START: 1    # Démarrer des conteneurs
  ALLOW_STOP: 1     # Arrêter des conteneurs
  ALLOW_RESTARTS: 1 # Redémarrer
  POST: 1           # Créer des conteneurs (si nécessaire)

Configuration Portainer avec le proxy TCP :

services:
  portainer:
    image: portainer/portainer-ce:latest
    command: --host tcp://socket-proxy:2375
    networks:
      - socket-proxy
      - traefik-public

Attention : avec POST: 1, Portainer peut créer de nouveaux conteneurs via le proxy, ce qui reste un risque si Portainer lui-même est compromis. Pour des environnements très sensibles, envisagez d'utiliser Portainer en mode lecture seule (--no-auth n'est pas recommandé, mais limiter les droits utilisateurs dans Portainer lui-même est possible).

Configurer Watchtower (auto-update containers) avec le Socket Proxy

Watchtower est un outil populaire pour la mise à jour automatique des conteneurs Docker. Il surveille les mises à jour d'images et redémarre les conteneurs avec la nouvelle version. Il nécessite un accès assez large à l'API Docker.

Configuration Socket Proxy minimale pour Watchtower :

environment:
  CONTAINERS: 1   # Lister les conteneurs à surveiller
  IMAGES: 1       # Pull les nouvelles images
  PING: 1
  VERSION: 1
  POST: 1         # Nécessaire pour recréer les conteneurs
  ALLOW_STOP: 1   # Pour arrêter les anciens conteneurs
  ALLOW_START: 1  # Pour démarrer les nouveaux

Configuration Watchtower :

services:
  watchtower:
    image: containrrr/watchtower
    environment:
      DOCKER_HOST: tcp://socket-proxy:2375
      WATCHTOWER_SCHEDULE: "0 0 3 * * *"
      WATCHTOWER_CLEANUP: "true"
      WATCHTOWER_NOTIFICATIONS: slack
      WATCHTOWER_NOTIFICATION_SLACK_HOOK_URL: $SLACK_WEBHOOK
    networks:
      - socket-proxy

Notez que si vous utilisez plusieurs outils nécessitant des droits différents, il est préférable de déployer plusieurs instances du Socket Proxy avec des configurations différentes — une pour Traefik (droits minimaux), une pour Watchtower (droits moyens), une pour Portainer (droits plus larges). Cette approche respecte davantage le principe du moindre privilège.

Variables d'environnement du Socket Proxy (CONTAINERS, IMAGES, NETWORKS, etc.)

La documentation complète des variables d'environnement disponibles dans Docker Socket Proxy est essentielle pour comprendre ce que vous autorisez et ce que vous bloquez.

Les variables correspondent aux sections de l'API Docker REST :

  • CONTAINERS (GET /containers/*) : lister et inspecter les conteneurs
  • IMAGES (GET /images/*, POST /images/create) : lister les images, pull
  • NETWORKS (GET /networks/*) : lister et inspecter les réseaux
  • VOLUMES (GET /volumes/*) : lister et inspecter les volumes
  • EVENTS (GET /events) : stream d'événements Docker
  • SERVICES, TASKS : pour Docker Swarm uniquement
  • SECRETS : secrets Docker Swarm — ne jamais activer
  • POST : autorise toutes les méthodes POST (création d'objets)
  • DELETE : autorise toutes les méthodes DELETE — à éviter
  • ALLOW_START, ALLOW_STOP, ALLOW_RESTARTS : actions spécifiques sur les conteneurs

Règle de base : commencez avec tout à 0, puis activez uniquement ce dont votre outil a besoin. Testez votre configuration en vérifiant dans les logs du Socket Proxy les requêtes bloquées (403 Forbidden) et activez progressivement les endpoints nécessaires.

Les logs du Socket Proxy listent toutes les requêtes avec leur résultat (autorisée ou bloquée), ce qui est précieux pour l'audit de sécurité et le débogage. Intégrez ces logs dans votre SIEM ou votre stack de monitoring pour détecter les tentatives d'accès à des endpoints non autorisés — cela peut indiquer une compromission d'un de vos conteneurs.

Docker rootless comme alternative complémentaire

Docker rootless est une alternative architecturale qui résout le problème à la source : le daemon Docker tourne en tant qu'utilisateur non-root, ce qui signifie que même en cas d'accès au socket, l'attaquant n'obtient que les droits de l'utilisateur Unix qui fait tourner Docker, pas root.

Installation de Docker rootless :

dockerd-rootless-setuptool.sh install
# Ou sur les systèmes récents :
sudo apt-get install docker-ce-rootless-extras
dockerd-rootless-setuptool.sh install --force

Avec Docker rootless, le socket se trouve dans /run/user/$(id -u)/docker.sock. Configurez les clients Docker pour l'utiliser :

export DOCKER_HOST=unix:///run/user/$(id -u)/docker.sock

Les limitations de Docker rootless : certains ports inférieurs à 1024 ne peuvent pas être exposés directement (solution : newuidmap), certains pilotes de stockage ne sont pas supportés, et les performances réseau peuvent être légèrement inférieures. Pour les environnements de production avec Docker rootless, combinez-le avec le Socket Proxy pour une défense en profondeur maximale.

Docker rootless et Socket Proxy sont complémentaires : Docker rootless réduit l'impact d'un accès au socket, Socket Proxy réduit la surface d'accès au socket. Ensemble, ils constituent une défense solide contre l'escalade de privilèges via le daemon Docker. Intégrez cette approche dans votre audit selon le CIS Docker Benchmark.

Intégrer dans un audit de sécurité Docker (CIS Docker Benchmark)

Le CIS Docker Benchmark est le référentiel de sécurité de facto pour les environnements Docker. Il couvre la configuration du daemon, les images, les réseaux, et les pratiques de déploiement. L'utilisation de Socket Proxy répond directement aux contrôles suivants :

  • CIS 2.14 : "Do not expose the Docker daemon socket to containers" — le Socket Proxy est la solution recommandée
  • CIS 4.1 : "Ensure that a user for the container has been created" — Docker rootless complète ce contrôle
  • CIS 5.4 : "Ensure that privileged containers are not used" — no-new-privileges:true sur le Socket Proxy

Pour auditer votre configuration Docker avec les recommandations CIS, utilisez Docker Bench for Security :

docker run -it --net host --pid host --userns host --cap-add audit_control \
  -e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \
  -v /etc:/etc:ro \
  -v /usr/bin/containerd:/usr/bin/containerd:ro \
  -v /usr/bin/runc:/usr/bin/runc:ro \
  -v /usr/lib/systemd:/usr/lib/systemd:ro \
  -v /var/lib:/var/lib:ro \
  -v /var/run/docker.sock:/var/run/docker.sock:ro \
  --label docker_bench_security \
  docker/docker-bench-security

Ce script audite automatiquement votre installation Docker contre les contrôles CIS et génère un rapport avec les points de conformité et les points d'amélioration. Après avoir implémenté le Socket Proxy, le contrôle sur l'exposition du socket doit passer de WARN à INFO (non applicable). Consultez notre article dédié à la sécurisation Docker en production pour une liste complète des mesures à implémenter.

En complément du Socket Proxy, les mesures de sécurité essentielles à combiner sont : la signature d'images Docker Content Trust (DCT), l'utilisation de registries privés avec scan de vulnérabilités (Trivy, Grype), les politiques de réseau (interdire la communication inter-conteneurs par défaut), et l'audit des Dockerfile pour éviter les images :latest et les images de base non maintenues.

Besoin d'un accompagnement expert ?

Nos consultants sécurisent et optimisent votre infrastructure.

Contacter nos experts →

Surveiller et alerter sur les accès suspects au Socket Proxy

Le Socket Proxy génère des logs pour chaque requête reçue, qu'elle soit autorisée ou bloquée. Ces logs sont une source précieuse d'information de sécurité. Une requête bloquée vers un endpoint dangereux (création de conteneur, accès aux secrets) depuis un conteneur applicatif doit déclencher une alerte immédiate — cela peut indiquer une compromission en cours.

Configurez le logging JSON du Socket Proxy pour une meilleure intégration avec les outils de centralisation de logs :

services:
  socket-proxy:
    image: tecnativa/docker-socket-proxy
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "3"

Script de surveillance basique pour détecter les requêtes suspectes bloquées :

#!/bin/bash
# Surveiller les tentatives d'accès bloquées
BLOCKED=$(docker logs socket-proxy 2>&1 | grep -c "403")
if [ $BLOCKED -gt 5 ]; then
  curl -X POST $SLACK_WEBHOOK \
    -H 'Content-type: application/json' \
    -d "{\"text\":\"Alerte Docker Socket Proxy: $BLOCKED requêtes bloquées détectées\"}"
fi

Pour une intégration plus avancée, envoyez les logs du Socket Proxy vers votre SIEM (Elastic Stack, Graylog) et créez des règles de corrélation pour détecter les patterns suspects : rafale de requêtes bloquées depuis un même conteneur, tentatives répétées d'accès à /secrets, ou accès aux endpoints de création depuis des conteneurs qui n'en ont pas besoin.

La surveillance du Socket Proxy s'intègre naturellement dans une stratégie de détection et réponse aux incidents (EDR/XDR) pour vos environnements conteneurisés. Les événements générés par le proxy peuvent être corrélés avec les alertes CrowdSec, les logs applicatifs et les alertes réseau pour construire une image complète d'une attaque potentielle. Pour approfondir la surveillance de sécurité des environnements Docker, consultez notre guide sur la détection d'incidents dans les environnements conteneurisés.

Questions fréquentes sur Docker Socket Proxy

Le Socket Proxy résout-il complètement le problème de sécurité du socket Docker ?

Le Socket Proxy réduit considérablement la surface d'attaque mais ne l'élimine pas complètement. Si un outil configuré avec le proxy est compromis, l'attaquant est limité aux endpoints que vous avez autorisés. Avec Traefik (CONTAINERS, NETWORKS, EVENTS), l'attaquant peut lister les conteneurs et leurs métadonnées mais ne peut pas en créer de nouveaux ni modifier la configuration. C'est une amélioration majeure par rapport à un accès complet au socket, mais une défense en profondeur (Docker rootless, isolation réseau, images signées) reste nécessaire.

Le Socket Proxy fonctionne-t-il avec Docker Swarm ?

Oui, et les variables SERVICES, TASKS et NODES sont disponibles pour les outils nécessitant un accès à l'API Swarm. Cependant, les secrets Docker Swarm (SECRETS: 1) ne doivent jamais être activés — cela exposerait l'ensemble des secrets de votre Swarm à tous les conteneurs ayant accès au proxy. Configurez un proxy dédié avec des droits Swarm uniquement pour les outils de gestion Swarm, séparé du proxy utilisé par Traefik.

Peut-on utiliser le Socket Proxy avec Kubernetes ?

En environnement Kubernetes, le daemon Docker est généralement remplacé par containerd ou CRI-O, et l'accès direct au socket Docker n'est plus la norme. Kubernetes gère ses propres abstractions de conteneurs via la CRI (Container Runtime Interface). Le Socket Proxy est donc plus pertinent dans des environnements Docker Compose ou Swarm. Pour Kubernetes, les équivalents sécurité sont les PodSecurityAdmission, les Network Policies, et l'utilisation de seccomp profiles.

Comment savoir quels endpoints mon outil nécessite ?

La méthode recommandée est d'activer le logging verbose du Socket Proxy (LOG_LEVEL=debug), de lancer votre outil normalement, et d'observer dans les logs quels endpoints sont appelés et lesquels sont bloqués. Commencez avec tous les endpoints désactivés et activez-les progressivement jusqu'à ce que votre outil fonctionne correctement. La documentation de chaque outil liste généralement les endpoints Docker API nécessaires.

Le Socket Proxy ajoute-t-il une latence significative ?

La latence ajoutée par le Socket Proxy est négligeable — inférieure à 1 ms dans la quasi-totalité des cas. Le proxy est un simple passthrough HTTP avec filtrage au niveau des routes. Pour Traefik qui consulte l'API Docker périodiquement (toutes les quelques secondes), cette latence est imperceptible. Pour Portainer qui génère des requêtes interactives, aucune différence notable n'est constatée en pratique.

À retenir

  • Le socket Docker monté directement dans un conteneur équivaut à donner les droits root à ce conteneur — c'est un risque critique documenté par le CIS Docker Benchmark
  • Docker Socket Proxy de Tecnativa filtre les requêtes API Docker et n'expose que les endpoints explicitement autorisés par variable d'environnement
  • Le réseau socket-proxy doit être déclaré internal:true pour empêcher tout accès depuis internet vers le proxy
  • Déployez une instance de proxy par outil avec des droits spécifiques à ses besoins réels, en respectant le principe du moindre privilège
  • Docker rootless et Socket Proxy sont complémentaires : combinez les deux pour une défense en profondeur maximale contre l'escalade de privilèges via Docker