Lion Cipher Lab
Lock down an exposed Docker daemon socket
- Cyber / Security Engineer
- Intermediate
- ~45 min
- Free · no account required
Why it matters
The Docker daemon exposes a full REST API, and that API can do anything root can do on the host — pull and run images, bind-mount the host filesystem into a container, read every secret on the box. By default that API is served only over a unix socket at /var/run/docker.sock, a file owned by root and readable only by root and the `docker` group, so the blast radius is 'a local privileged user.' The misconfiguration that turns that into a disaster is a single configuration choice: telling the daemon to ALSO listen on a TCP port (classically 2375) with no TLS and no authentication. The moment…
What you'll learn
- explaining in plain language what the Docker daemon API is, where it listens (the unix socket by default), and why reaching it is exactly equivalent to root on the host
- reading the default protection: /var/run/docker.sock is a root-owned, root/docker-group-only file, so only a local privileged user can talk to the daemon
- safely reproducing the classic misconfiguration on your OWN host — binding the Engine API to a TCP port on 127.0.0.1 ONLY (never 0.0.0.0) via daemon.json and a systemd drop-in — and restarting the daemon cleanly
- measuring the exposure from the local host with curl alone: GET /version and /images/json return 200 with no credentials, and POST /containers/create starts a container — unauthenticated, root-equivalent
- locking it back down: removing the TCP listener, returning to the unix socket, restarting, and confirming the same curl now gets 'connection refused'
- configuring the ONLY safe remote-access path when it is genuinely needed — mutual TLS with dockerd --tlsverify and CA/server/client certificates on port 2376
- holding the judgment straight: 0.0.0.0 is the real catastrophe, 'internal network' is not authentication, the docker group is root, and rootless Docker changes the blast radius
- developing the reflex to grep infrastructure (daemon.json, systemd units, compose, CI) for `-H tcp://` / `hosts: tcp://` and treat every unauthenticated 2375 listener as a finding
Steps in this lab
- Before anything: this is YOUR loopback host, and that is why it is defense
- What the Docker daemon API is, and why reaching it means root
- Inspect the default protection: a root-only unix socket
- Reproduce the misconfiguration — safely, on loopback only
- MEASURE (before): curl the open port and get root, no password
- The honest judgment: what is and isn't a defense here
- Lock it down: remove the TCP listener, back to the socket
- MEASURE (after): the same curl now gets 'Connection refused'
- When remote access is genuinely needed: mutual TLS, not an open port
- Do it better — the reflex to carry out of this lab