Cosa fa#
File speciale sul filesystem che funge da canale di comunicazione diretto tra il Docker CLI e il demone dockerd. E' un Unix domain socket — non usa la rete, comunica direttamente nel kernel. Chiunque abbia accesso in scrittura a questo file ha controllo completo su Docker e, di conseguenza, sull'host intero.
TL;DR#
docker CLI ──► /var/run/docker.sock ──► dockerd
^
Unix domain socket
non e' rete — e' filesystem
solo sulla stessa macchina
passa nel kernel
Se monti questo socket dentro un container:
container ──► /var/run/docker.sock ──► dockerd
il container ha controllo totale su Docker
privilege escalation garantitaSocket di rete vs Unix domain socket#
SOCKET DI RETE (TCP/IP)
────────────────────────────────────────────────
processo A
│
▼
stack di rete (TCP/IP)
│
▼ (può attraversare la rete)
stack di rete
│
▼
processo B
identificato da: IP:porta
puo' essere su macchine diverse
traffico puo' uscire dalla macchina
UNIX DOMAIN SOCKET
────────────────────────────────────────────────
processo A
│
▼
kernel (IPC — Inter Process Communication)
│
▼
processo B
identificato da: path su filesystem (/var/run/docker.sock)
solo sulla stessa macchina
non esce mai dalla macchina
molto piu' veloce — nessun overhead di reteCome funziona normalmente#
Tu (terminale)
│
│ digiti: docker ps
│
▼
Docker CLI (/usr/bin/docker)
│
│ scrive sul socket: "GET /containers/json"
│ (protocollo HTTP su socket Unix)
│
▼
/var/run/docker.sock
│
▼
dockerd (il demone)
│
│ risponde con la lista dei container
│
▼
Docker CLI
│
▼
output nel terminalesequenceDiagram
participant U as Tu (docker CLI)
participant S as /var/run/docker.sock
participant D as dockerd
U->>S: docker ps (HTTP su Unix socket)
S->>D: richiesta
D->>S: lista container JSON
S->>U: output formattato
Il problema — montare il socket in un container#
# docker-compose.yml — PERICOLOSO
services:
app:
image: myapp
volumes:
- /var/run/docker.sock:/var/run/docker.sock
# ^ ^
# socket sull'host montato dentro il containerCosa succede:
HOST
├── dockerd (controlla tutto Docker)
│ ^
│ │ comunica tramite
│ │
├── /var/run/docker.sock ← montato dentro il container
│ ^
│ │ il container ci scrive sopra
│ │
└── CONTAINER
│
└── processo dentro il container
puo' fare qualsiasi operazione Docker
come se fosse il CLI sull'hostL'attacco — privilege escalation completa#
# Scenario: attaccante ha compromesso un container
# Il container ha docker.sock montato
# Dentro il container, l'attaccante esegue:
docker run -it \
-v /:/host \ # monta la ROOT dell'host dentro il nuovo container
alpine \ # immagine minimale
chroot /host # cambia la root del processo a /host
# ora "vede" il filesystem reale dell'host
# Risultato: shell con accesso root all'host intero
# Il container era "isolato" — ma docker.sock ha rotto l'isolamentoPRIMA (isolamento normale):
┌─────────────────────────────┐
│ HOST │
│ ├── filesystem host │
│ │ │
│ └── CONTAINER │
│ └── filesystem iso- │
│ lato dal host │
└─────────────────────────────┘
DOPO (docker.sock montato + attacco):
┌─────────────────────────────┐
│ HOST │
│ ├── filesystem host ◄─────┼── container vede tutto
│ │ │ e puo' modificare tutto
│ └── CONTAINER │
│ └── accesso tramite │
│ docker.sock │
└─────────────────────────────┘chroot — cosa fa#
chroot (change root) cambia la directory root per un processo:
chroot /host bash
# d'ora in poi per questo processo:
# / e' /host
# /etc e' /host/etc
# /bin e' /host/bin
# il processo non vede niente fuori da /host
# Usato legittimamente per:
# - recovery di sistema (come nel rescue Hetzner)
# - ambienti di build isolati
# - container primitivi (prima di Docker)Non confondere con chown (change owner — cambia il proprietario di un file).
Quando e' accettabile montare docker.sock#
In alcuni casi legittimi il socket deve essere montato:
# Caso legittimo — tool di monitoring che deve leggere i container
services:
portainer:
image: portainer/portainer-ce
volumes:
- /var/run/docker.sock:/var/run/docker.sock # necessario per funzionare
traefik:
image: traefik
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro # :ro = read-only
# ^
# mitigazione parziale:ro (read-only) riduce il rischio — il container puo' leggere lo stato dei container ma non crearne di nuovi. Non elimina il rischio completamente.
Come rilevare container con docker.sock montato#
# Controlla tutti i container in esecuzione
docker inspect $(docker ps -q) | grep -i "docker.sock"
# Alternativa piu' leggibile
docker ps -q | xargs docker inspect --format \
'{{.Name}}: {{range .Mounts}}{{if eq .Source "/var/run/docker.sock"}}SOCKET MONTATO{{end}}{{end}}'
# In un sistema sospetto — primo controllo
find /proc/*/fd -lname /var/run/docker.sock 2>/dev/nullScenario Reale — Blue Team#
Un analista SOC riceve un alert: processo insolito su un server di produzione con molti container. Controlla:
# 1. Quali container hanno docker.sock montato?
docker inspect $(docker ps -q) | grep docker.sock
# 2. Quel container ha creato altri container di recente?
docker events --since 1h | grep "container create"
# 3. Esistono container creati di recente con volumi sospetti?
docker ps -a --format "{{.CreatedAt}}\t{{.Names}}\t{{.Mounts}}" | sort -r | head -20Se trova un container creato dall'interno di un altro container — specialmente con -v /:/host — e' compromissione attiva.
Non montare mai /var/run/docker.sock in produzione a meno che non sia strettamente necessario. E' l'equivalente di dare a un processo le chiavi master di tutto il sistema.
Alternativa sicura: Docker API con TLS e certificati client, oppure socket proxy come tecnativa/docker-socket-proxy che espone solo le API necessarie con permessi granulari.
Dove l'ho incontrato#
- docker-security — superficie di attacco Docker
- docker-compose — volumi nei file compose
Collegato a#
- system — categoria
- iam — categoria — privilege escalation
- docker-security — contesto sicurezza Docker
- docker-overview — architettura generale Docker
- socket-concept — concetto di socket Unix


