The log shipper that owned the host
The observability lab mounts /var/run/docker.sock into Alloy so it can read container logs. That socket is full control of the Docker host.
- Weakness
- CWE-269 · Improper privilege management · CIS Docker 5.31
- Target
- the
alloyservice inobs-lab/docker-compose.yml(Observability course)
Run these techniques only against the lab you own. Using them on systems you don't have permission to test is illegal.
The threat
01The socket is the Docker API — all of it
The Docker daemon runs as root and trusts anything that can talk to its socket. The API can start containers with any configuration, including privileged containers or ones that mount the host's filesystem. Access to the socket is therefore equivalent to root on the host, even with
:roon the mount, because read-only applies to the socket FILE, not to the API calls made through it.A compromised log shipper, metrics agent, or CI helper that holds the socket is a compromised host.
What's at risk
- Root-equivalent control of the host and every container on it.
Detect
01Find every container with the socket mounted
terminal$ docker ps -q | xargs docker inspect --format '{{.Name}} {{range .Mounts}}{{.Source}} {{end}}' | grep docker.sock── output ──/obs-lab-alloy-1 /var/run/docker.sock /home/priya/obs-lab/alloy/config.alloy02Catch it in review with a scanner
Policy scanners flag socket mounts in compose files and Kubernetes manifests (as
hostPathvolumes). Checkov's CKV_K8S_27 ('Do not expose the docker daemon socket to containers') is one example; trivy config has equivalent checks for Kubernetes.
Defend
01Put an allowlisting proxy in front of the socket
A socket proxy exposes only the API endpoints the tool needs. Alloy's Docker discovery and log source need to LIST containers and READ their logs, not create, exec, or change anything. Only the proxy container touches the real socket, and it forbids everything else.
Vulnerable
obs-lab/docker-compose.ymladd to fileyaml alloy: image: grafana/alloy:v1.10.0 volumes: - ./alloy/config.alloy:/etc/alloy/config.alloy:ro - /var/run/docker.sock:/var/run/docker.sock:roHardened
obs-lab/docker-compose.ymladd to fileyaml docker-proxy: image: tecnativa/docker-socket-proxy:v0.3.0 environment: CONTAINERS: 1 # allow GET /containers/* (list, inspect, logs) POST: 0 # deny every write (create, start, exec, ...) volumes: ["/var/run/docker.sock:/var/run/docker.sock:ro"] read_only: true alloy: image: grafana/alloy:v1.10.0 volumes: ["./alloy/config.alloy:/etc/alloy/config.alloy:ro"] # in config.alloy: host = "tcp://docker-proxy:2375"02Better still: don't need the API
In Kubernetes, log agents read container log FILES from the node (
/var/log/pods) through a narrow read-only hostPath rather than the runtime socket. Apps can also ship telemetry directly with OTLP. Grant the smallest interface that does the job.
Verify
01Logs still flow; writes through the proxy are refused
Loki keeps receiving container logs. A write request through the proxy (any POST) gets HTTP 403 from the proxy before it ever reaches Docker.
terminal$ docker compose exec alloy wget -qO- --post-data='' http://docker-proxy:2375/containers/create 2>&1 | head -1── output ──wget: server returned error: HTTP/1.1 403 Forbidden
The concepts
Ambient authority
The socket is 'ambient authority': whoever holds it can do anything the daemon can, with no finer-grained check. Security improves when authority is explicit and narrow: a proxy with an allowlist, a scoped API token, a read-only file mount. Look for other ambient-authority mounts too: the containerd socket, the kubelet's credentials, cloud metadata access from containers (Chapter 3).
Your turn
Which other commonly deployed tools often request the Docker socket, and what's the safer alternative for each?
Interview questions
Why is mounting the Docker socket into a container dangerous?