Cosa fa#
Raccoglie i rischi di sicurezza specifici dell'ambiente Docker e le contromisure corrispondenti. Docker aggiunge una superficie di attacco nuova rispetto a Linux tradizionale — capirla e' fondamentale per il blue team.
La superficie di attacco Docker#
graph TD
A[Attaccante] -->|1 immagine vulnerabile| IMG[Immagine con CVE]
A -->|2 container root| ROOT[Container gira come root]
A -->|3 docker.sock esposto| SOCK[Accesso docker.sock]
A -->|4 porta esposta| PORT[DB esposto su 0.0.0.0]
A -->|5 secrets in env| ENV[Password in docker inspect]
IMG -->|RCE nel container| ESC[Escape dal container]
ROOT --> ESC
SOCK -->|controlla Docker| HOST[Accesso all'host]
PORT -->|accesso diretto DB| DATA[Dati rubati]
ENV --> DATA
ESC --> HOST
1. docker.sock — la chiave master#
/var/run/docker.sock e' il file che il demone Docker usa per ricevere comandi. Chi lo controlla, controlla Docker. Chi controlla Docker, controlla l'host.
ls -la /var/run/docker.sock
# srw-rw---- 1 root docker /var/run/docker.sock
# ^^^^^^
# solo gruppo docker puo' accedere
# Un attaccante con accesso a docker.sock puo':
docker run -v /:/host --rm -it alpine chroot /host sh
# monta il filesystem dell'host dentro un container
# chroot /host = l'host e' la root
# risultato: shell root sull'hostContromisure:
# Non montare mai docker.sock in un container senza necessita'
# Nel docker-compose, questa e' una flag rossa:
volumes:
- /var/run/docker.sock:/var/run/docker.sock # pericoloso
# Verifica chi e' nel gruppo docker:
getent group docker
# ogni utente in questo gruppo ha accesso root effettivo all'host2. Container root — il problema piu' comune#
Per default, i processi dentro un container girano come root (UID 0). Se c'e' una vulnerabilita' nel processo, l'attaccante ha root nel container — e con alcune configurazioni, puo' uscire sull'host.
# Verifica se un container gira come root:
docker exec container_rails whoami
# se risponde "root" → problema
# Nel Dockerfile — imposta un utente non-root:
RUN groupadd -r appuser && useradd -r -g appuser appuser
USER appuser # da qui in poi, tutti i comandi girano come appuser
# Nel docker-compose — sovrascrive l'utente del container:
services:
rails:
user: "1000:1000" # UID:GID dell'utente non-root3. Porte esposte — 0.0.0.0 vs 127.0.0.1#
# PERICOLOSO — database esposto su tutte le interfacce:
ports:
- "5432:5432" # equivale a 0.0.0.0:5432:5432
# raggiungibile da internet se il firewall non blocca
# SICURO — solo da localhost:
ports:
- "127.0.0.1:5432:5432"
# solo SSH tunnel o processi locali possono accedere
# MEGLIO — non esporre la porta affatto:
# usa le reti Docker per la comunicazione tra container
# e SSH tunnel per accesso dal tuo Macgraph LR
Internet -->|bloccato| P1["0.0.0.0:5432\npericoloso"]
Internet -->|bloccato| P2["127.0.0.1:5432\nsicuro"]
Mac -->|SSH tunnel| P2
Mac -->|connessione diretta| P1
4. Secrets — non metterli nelle variabili d'ambiente#
Le variabili d'ambiente sono visibili a chiunque abbia accesso al container:
docker inspect chatwoot_rails | grep -A 50 '"Env"'
# mostra tutte le variabili d'ambiente — incluse le passwordLivelli di sicurezza crescente:
# PEGGIO — hardcoded nel docker-compose.yml (finisce su git)
environment:
POSTGRES_PASSWORD: miapassword123
# MEGLIO — file .env (non committare su git)
environment:
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
# .env contiene: POSTGRES_PASSWORD=miapassword123
# .gitignore contiene: .env
# MEGLIO ANCORA — Docker Secrets (solo Docker Swarm/Kubernetes)
secrets:
db_password:
file: ./secrets/db_password.txt5. Immagini vulnerabili#
Ogni immagine contiene software — software ha CVE. Un'immagine vecchia e' una superficie di attacco:
# Scansiona un'immagine per CVE (richiede trivy installato):
trivy image chatwoot/chatwoot:latest
# Usa immagini alpine quando possibile — meno software = meno CVE:
postgres:16 # immagine completa Debian
postgres:16-alpine # immagine minima Alpine — molto meno superficie
# Aggiorna regolarmente:
docker compose pull # scarica versioni aggiornate
docker compose up -d # riavvia con le nuove immaginiChecklist blue team — audit rapido Docker#
# 1. Quali container girano come root?
for c in $(docker ps -q); do
echo -n "$c: "
docker exec $c whoami 2>/dev/null
done
# 2. Qualcuno ha docker.sock montato?
docker inspect $(docker ps -q) | grep docker.sock
# 3. Porte esposte su 0.0.0.0?
docker ps --format "{{.Names}}: {{.Ports}}" | grep "0.0.0.0"
# 4. Container con capabilities privilegiate?
docker inspect $(docker ps -q) | grep -E '"Privileged": true'
# 5. Immagini non aggiornate?
docker images --format "{{.Repository}}:{{.Tag}}\t{{.CreatedSince}}"Un container --privileged ha accesso completo all'host — e' equivalente a girare come root sull'host senza container. Non usarlo mai in produzione.
La regola piu' importante: ogni container deve avere solo i permessi strettamente necessari per fare il suo lavoro. Principio del minimo privilegio — stesso concetto di Linux UGO, applicato ai container.
Collegato a#
- docker-network-defense — isolamento di rete come difesa
- docker-compose — dove si configurano permessi e porte
- dockerfile — USER, secrets nel build
- linux-permissions-ugo — stesso principio, contesto diverso
- suid-sgid-sticky — privilege escalation analoga
- system — categoria
- iam — categoria
- docker-sock


