From 210148d1d8ed621027fd5478644f34f18920bf14 Mon Sep 17 00:00:00 2001 From: Benjy Date: Sat, 18 Jul 2026 10:01:28 +0200 Subject: [PATCH 1/2] document TLS model and Scout report --- README.md | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f120180..e4e4872 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # docker-socket-proxy +[![Docker Scout report](https://img.shields.io/badge/Docker%20Scout-view%20report-2496ED?logo=docker&logoColor=white)](https://scout.docker.com/reports/org/cerede2000/images/host/hub.docker.com/repo/cerede2000%2Fdocker-socket-proxy) + Proxy HTTP minimaliste et sécurisé devant le socket Docker. Les clients sont associés à un profil par leur adresse IP, découverte depuis leurs labels Docker ; tout accès qui n'est pas explicitement accordé est refusé. Le projet permet de limiter les familles d'API Docker, puis de restreindre les opérations à certains conteneurs. Il évite ainsi de monter directement `/var/run/docker.sock` dans une application. @@ -11,10 +13,12 @@ cerede2000/docker-socket-proxy:latest ghcr.io/cerede2000/docker-socket-proxy:latest ``` -La première référence est publiée sur [Docker Hub](https://hub.docker.com/r/cerede2000/docker-socket-proxy) ; la seconde sur GitHub Container Registry. Les deux images sont multi-architecture (`linux/amd64` et `linux/arm64`), construites avec Go et exécutées sans privilèges dans Distroless Debian 13. +La première référence est publiée sur [Docker Hub](https://hub.docker.com/r/cerede2000/docker-socket-proxy) ; la seconde sur GitHub Container Registry. Les deux images sont multi-architecture (`linux/amd64` et `linux/arm64`), construites avec Go et exécutées sans privilèges dans `distroless/static-debian13:nonroot`. `latest` suit `main`. Chaque release Git `vX.Y.Z` publie également les tags Docker immuables `X.Y.Z` et `X.Y` sur les deux registres. +L'image publiée est analysée en continu par [Docker Scout](https://scout.docker.com/reports/org/cerede2000/images/host/hub.docker.com/repo/cerede2000%2Fdocker-socket-proxy). Le rapport est lié ici plutôt que figé dans le README : son résultat suit les mises à jour des vulnérabilités et de l'image. + ## Ce qui le différencie Les socket proxies classiques limitent principalement les familles d'endpoints Docker. Celui-ci ajoute deux niveaux de contrôle complémentaires : @@ -39,7 +43,7 @@ Créez un fichier `profiles.yml`, puis lancez le proxy. Le montage du socket est ```yaml services: docker-socket-proxy: - image: ghcr.io/cerede2000/docker-socket-proxy:latest + image: cerede2000/docker-socket-proxy:latest container_name: docker-socket-proxy user: "1000:998" # adapter à l'UID:GID pouvant lire le socket sur l'hôte read_only: true @@ -83,7 +87,7 @@ Cet exemple sépare les rôles : Traefik peut lire les informations nécessaires ```yaml services: docker-socket-proxy: - image: ghcr.io/cerede2000/docker-socket-proxy:latest + image: cerede2000/docker-socket-proxy:latest container_name: docker-socket-proxy user: "1000:998" # à adapter à l'hôte read_only: true @@ -300,6 +304,23 @@ Lorsqu'une portée est active (`allowlist`, `blacklist` ou une règle nominative Le proxy écrit dans ses journaux la découverte des rôles et les refus. Un client sans rôle, avec un rôle inconnu, ou ne partageant aucun réseau avec le proxy reçoit `403 Forbidden`. +## Réseau et TLS + +Le proxy est conçu pour une communication **locale au moteur Docker** : + +```text +client Docker -- HTTP privé --> docker-socket-proxy -- socket Unix --> dockerd +``` + +- La liaison entre le proxy et Docker utilise `DOCKER_SOCKET_PATH` (par défaut `/var/run/docker.sock`). Elle ne traverse pas le réseau et n'utilise donc pas TLS, certificat ou autorité de certification (CA). +- Le proxy n'ouvre pas de connexion HTTPS sortante et ne prend pas en charge un moteur Docker distant configuré par `DOCKER_HOST=tcp://…`. +- Le port `2375` est volontairement en HTTP clair : il doit rester accessible **uniquement** depuis un réseau Docker interne, partagé avec les clients autorisés. N'ajoutez pas de `ports:` et ne l'exposez jamais par Traefik, un load balancer ou Internet. +- `internal: true` réduit l'exposition du réseau, mais tout conteneur qui y est attaché reste un client potentiel : n'y raccordez que le proxy et les services qui ont réellement besoin de l'API Docker. + +Cette approche est la même que celle des proxies de référence Tecnativa et LinuxServer : le contrôle d'accès réseau et le filtrage d'API remplacent une terminaison TLS sur un port qui ne doit pas être publié. Si un besoin inter-hôtes apparaît, déployez un proxy local par hôte plutôt que d'étendre ce port : l'association client/profil de ce projet repose sur les réseaux Docker locaux. + +La runtime `distroless/static-debian13:nonroot` est adaptée à ce modèle : le binaire Go est compilé avec `CGO_ENABLED=0`, sans dépendance à `glibc`, OpenSSL ni magasin de CA. Ajouter des CA ne renforcerait pas cette configuration ; elles ne deviendraient nécessaires qu'avec une future fonctionnalité HTTPS sortante ou mTLS. + ## Développement ```bash From 4ee7a928f7e8a88edce544366847f0a5eceea38b Mon Sep 17 00:00:00 2001 From: Benjy Date: Sat, 18 Jul 2026 10:14:04 +0200 Subject: [PATCH 2/2] add English README and French translation --- README.fr.md | 331 +++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 168 +++++++++++++------------- 2 files changed, 414 insertions(+), 85 deletions(-) create mode 100644 README.fr.md diff --git a/README.fr.md b/README.fr.md new file mode 100644 index 0000000..830d24a --- /dev/null +++ b/README.fr.md @@ -0,0 +1,331 @@ +# docker-socket-proxy + +[English](README.md) | **Français** + +[![Docker Scout report](https://img.shields.io/badge/Docker%20Scout-view%20report-2496ED?logo=docker&logoColor=white)](https://scout.docker.com/reports/org/cerede2000/images/host/hub.docker.com/repo/cerede2000%2Fdocker-socket-proxy) + +Proxy HTTP minimaliste et sécurisé devant le socket Docker. Les clients sont associés à un profil par leur adresse IP, découverte depuis leurs labels Docker ; tout accès qui n'est pas explicitement accordé est refusé. + +Le projet permet de limiter les familles d'API Docker, puis de restreindre les opérations à certains conteneurs. Il évite ainsi de monter directement `/var/run/docker.sock` dans une application. + +## Images de production + +```text +cerede2000/docker-socket-proxy:latest +ghcr.io/cerede2000/docker-socket-proxy:latest +``` + +La première référence est publiée sur [Docker Hub](https://hub.docker.com/r/cerede2000/docker-socket-proxy) ; la seconde sur GitHub Container Registry. Les deux images sont multi-architecture (`linux/amd64` et `linux/arm64`), construites avec Go et exécutées sans privilèges dans `distroless/static-debian13:nonroot`. + +`latest` suit `main`. Chaque release Git `vX.Y.Z` publie également les tags Docker immuables `X.Y.Z` et `X.Y` sur les deux registres. + +L'image publiée est analysée en continu par [Docker Scout](https://scout.docker.com/reports/org/cerede2000/images/host/hub.docker.com/repo/cerede2000%2Fdocker-socket-proxy). Le rapport est lié ici plutôt que figé dans le README : son résultat suit les mises à jour des vulnérabilités et de l'image. + +## Ce qui le différencie + +Les socket proxies classiques limitent principalement les familles d'endpoints Docker. Celui-ci ajoute deux niveaux de contrôle complémentaires : + +1. Le droit est attribué au **client** par profil, automatiquement via son label Docker (`socketproxy.role`). +2. Le droit est ensuite limité à la **cible** : tous les conteneurs, allowlist, blacklist, refus explicite, ou accès strictement en lecture seule par conteneur. + +Un outil peut donc disposer d'un accès Docker étendu lorsque c'est nécessaire (Portainer, un opérateur), tandis que Traefik Manager peut uniquement consulter et redémarrer `traefik`. Le contrôle nom/ID est maintenu en cache et s'applique aux listes, événements et appels directs. + +## Principes de sécurité + +- Aucun droit n'est accordé implicitement. +- Seuls les conteneurs portant `socketproxy.role` (ou l'alias `socketproxy.service`) et correspondant à un profil sont autorisés. +- Le proxy ne retient que les IP partagées avec ses propres réseaux Docker. +- Les listes, les événements et les opérations ciblant un conteneur respectent la même portée. +- Le cache interne nom / ID de conteneur évite une requête Docker supplémentaire pour les vérifications usuelles. + +## Démarrage rapide + +Créez un fichier `profiles.yml`, puis lancez le proxy. Le montage du socket est en lecture seule : les requêtes Docker restent possibles via l'API Unix, mais le fichier socket ne peut pas être remplacé depuis le conteneur. + +```yaml +services: + docker-socket-proxy: + image: cerede2000/docker-socket-proxy:latest + container_name: docker-socket-proxy + user: "1000:998" # adapter à l'UID:GID pouvant lire le socket sur l'hôte + read_only: true + cap_drop: [ALL] + security_opt: + - no-new-privileges:true + tmpfs: + - /tmp + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - ./profiles.yml:/config/profiles.yml:ro + networks: + - socketproxy + restart: unless-stopped + +networks: + socketproxy: + internal: true +``` + +Le compte configuré avec `user` doit avoir accès au socket Docker de l'hôte. Vérifiez son UID et son GID avec `stat -c '%u:%g' /var/run/docker.sock` sous Linux, puis adaptez la valeur. Sur certaines installations, il est préférable de construire une image dérivée qui crée un groupe portant le GID réel du socket. + +## Associer un client à un profil + +Ajoutez l'un des deux labels suivants au conteneur client : + +```yaml +labels: + socketproxy.role: mon-profil + # ou : socketproxy.service: mon-profil +``` + +Le profil est rechargé automatiquement lorsque `profiles.yml` est modifié. La découverte des IP intervient au démarrage, lors des événements Docker pertinents, et périodiquement. + +## Exemple complet : Traefik et Traefik Manager + +Cet exemple sépare les rôles : Traefik peut lire les informations nécessaires au provider Docker ; Traefik Manager peut seulement consulter et redémarrer le conteneur `traefik`. Il ne peut ni agir sur les autres conteneurs ni lancer d'exec. + +`compose.yml` : + +```yaml +services: + docker-socket-proxy: + image: cerede2000/docker-socket-proxy:latest + container_name: docker-socket-proxy + user: "1000:998" # à adapter à l'hôte + read_only: true + cap_drop: [ALL] + security_opt: + - no-new-privileges:true + tmpfs: + - /tmp + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - ./profiles.yml:/config/profiles.yml:ro + networks: [socketproxy] + restart: unless-stopped + + traefik: + image: traefik:v3 + container_name: traefik + command: + - --providers.docker=true + - --providers.docker.endpoint=tcp://docker-socket-proxy:2375 + - --providers.docker.exposedbydefault=false + labels: + socketproxy.role: traefik + networks: [socketproxy, frontend] + restart: unless-stopped + + traefik-manager: + image: ghcr.io/chr0nzz/traefik-manager:latest + container_name: traefik-manager + environment: + DOCKER_HOST: tcp://docker-socket-proxy:2375 + RESTART_METHOD: proxy + TRAEFIK_CONTAINER: traefik + labels: + socketproxy.role: traefik-manager + networks: [socketproxy, frontend] + restart: unless-stopped + +networks: + socketproxy: + internal: true + frontend: + external: true +``` + +`profiles.yml` : + +```yaml +traefik: + ping: true + version: true + containers: true + networks: true + events: true + session: true + +traefik-manager: + ping: true + version: true + containers: true + post: true + allow_restart: true + container_scope: allowlist + allowed_containers: + - traefik +``` + +`container_name: traefik` et `allowed_containers: [traefik]` doivent correspondre exactement. Le profil `traefik-manager` ne permet pas `start` ou `stop` : seul `POST /containers/traefik/restart` est admis. + +## Configuration du proxy + +| Variable | Défaut | Description | +| --- | --- | --- | +| `DOCKER_SOCKET_PATH` | `/var/run/docker.sock` | Chemin du socket Docker à joindre | +| `PROXY_PORT` | `2375` | Port d'écoute et port du healthcheck intégré | +| `PROXY_LISTEN` | — | Adresse d'écoute complète ; prioritaire sur `PROXY_PORT` | +| `SOCKETPROXY_PROFILE_FILE` | `/config/profiles.yml` | Fichier YAML des profils | +| `DISCOVER_INTERVAL` | `30s` | Période de redécouverte des conteneurs ; durée Go (`15s`) ou nombre de secondes (`15`) | +| `EVENT_DEBOUNCE_DELAY` | `100ms` | Délai de regroupement des événements Docker ; durée Go ou nombre de millisecondes | + +Les arguments suivants sont disponibles et prioritaires sur les variables correspondantes : `--listen`, `--socket`, `--profiles`, `--discover-interval` et `--debounce-delay`. + +Le healthcheck appelle `http://127.0.0.1:$PROXY_PORT/version`. Si `--listen` ou `PROXY_LISTEN` utilise un autre port, renseignez `PROXY_PORT` avec ce même port. + +### Options de ligne de commande par profil + +Les profils peuvent aussi être définis dans la commande du conteneur. Le format est `--.