Exposed Service
Exposed Docker Engine API
An exposed Docker Engine API is a daemon reachable on TCP port 2375 (or 2376 without client-certificate verification) that permits operations without effective client access control. Impact depends on allowed methods and daemon privileges. If container create and start operations are allowed on a rootful host, callers may reach host files with elevated privileges. Rootless mode limits host privileges, but accessible data remains at risk.
A misplaced `-H tcp://0.0.0.0:2375` flag can expose Docker's control API to the internet. On a rootful host, unauthorized daemon access can lead to host compromise. Rootless mode narrows that host privilege boundary, but it does not make an unauthenticated API safe.
What it is
The Docker Engine API is the HTTP control plane for the Docker daemon (`dockerd`). By default the daemon listens only on a local Unix socket (`/var/run/docker.sock`). When an operator adds a TCP listener (commonly `-H tcp://0.0.0.0:2375`) to enable remote management, CI integration, or orchestration, the daemon's control API becomes reachable over the network. By convention, port 2375 carries plaintext traffic and 2376 carries TLS. TLS alone does not authenticate clients. Docker's `--tlsverify` requires a trusted client certificate. An authenticated gateway can also block requests before they reach the daemon. Without effective client authentication and authorization, either port can permit unauthorized commands. The API defines read routes such as `GET /version` and `GET /containers/json`, plus write routes such as `POST /containers/create` and `POST /containers/{id}/start`. Gateway and Docker authorization policy determine which routes a caller can use.
The impact depends on permitted API operations, daemon privileges, and accessible assets. If an unauthorized caller can create and start containers on a rootful host, a host bind mount can expose files with elevated privileges. Rootless mode runs the daemon and containers as a non-root user. Anonymous read access can still disclose information. Confirmed create and start access can affect workloads available to that user.
At a glance
How attackers find and exploit it
- Mass-scan the IPv4 space (or a target ASN) for TCP 2375 and 2376 using tools like masscan or Shodan/Censys saved queries, since both ports are static, well-known Docker defaults.
- Probe for a Docker API fingerprint with an unauthenticated `GET /version` and parse the JSON for fields such as `ApiVersion`, `Os`, and `GitCommit`. A matching response needs independent corroboration before attributing it to a live daemon.
- Enumerate the environment with `GET /containers/json`, `GET /images/json`, and `GET /info` to learn running workloads, image names, mounted volumes, and host kernel details that inform the next step.
- Pull or reference an image already present on the host, then use `POST /containers/create` to request a host bind mount into a new container.
- Start the container with `POST /containers/{id}/start`. Host file access follows the daemon's privileges: rootful mode can expose privileged host files, while rootless mode limits access to the non-root user's rights.
- Establish persistence and pivot: deploy a coin miner or reverse shell, harvest secrets and registry credentials, and reuse the Docker API to start additional containers on the same or adjacent hosts.
How to detect it on your surface
- Inventory every internet-facing and internal IP for open TCP 2375 and 2376 from an out-of-network vantage point, since internal-only firewall rules frequently fail to cover cloud security-group or VPC-peering paths.
- On each host, inspect the daemon launch configuration (`/etc/docker/daemon.json` for a `hosts` array and the systemd unit or `dockerd` command line for `-H tcp://` flags) to find TCP listeners that should not exist.
- Confirm whether a direct TCP listener enforces mutual TLS by checking for `--tlsverify`, `--tlscacert`, `--tlscert`, and `--tlskey`. Also check whether an authenticated gateway blocks requests before they reach the daemon.
- Review cloud security groups, network ACLs, and load-balancer rules for any allow rule permitting 2375/2376 from `0.0.0.0/0` or broad internal CIDRs.
- Search CI/CD configuration and infrastructure-as-code (Terraform, Ansible, Helm) for templated `DOCKER_HOST=tcp://...:2375` values that provision exposed daemons at scale.
Detection signals
- An unauthenticated `GET /version` returns HTTP 200 with a JSON body containing `ApiVersion`, `Version`, `GitCommit`, `Os`, and `Arch` fields, the canonical Docker daemon fingerprint.
- An anonymous GET /containers/json returns container data. This shows that read route is accessible, but does not prove container creation or other write operations are allowed.
- The HTTP response header advertises `Server: Docker/<version> (linux)` or `Api-Version:` on responses from the port.
- A direct daemon listener answers API requests over plaintext HTTP, often on 2375, or over TLS without requesting a client certificate, often on 2376. Verify reachability and upstream access controls.
- `GET /info` may expose reported host details such as `KernelVersion`, `OperatingSystem`, `Architecture`, `NCPU`, and `DockerRootDir`. This shows access to that read route, not write authority or definitive daemon identity.
Validate before you report
- Issue a non-destructive `GET /version` and record a 200 with Docker-shaped JSON as a fingerprint. Corroborate service identity through authorized configuration or independent evidence before attributing the response to a live daemon.
- Send GET /_ping and inspect its response and headers as Docker fingerprints. Confirm service identity with additional evidence; a body or header alone can be imitated.
- Call GET /containers/json and GET /images/json within authorization. If they return data without credentials, record anonymous read access. Do not infer that POST /containers/create or other write operations pass gateway or Docker authorization policy.
- Without creating or starting any container, inspect `GET /info` for `Swarm`, `SecurityOptions`, and `DockerRootDir` to characterize exploitability (for example, whether userns-remap or rootless mode is in effect) and to capture evidence of the configuration.
- Record request and response pairs, port, headers, and policy evidence for the operations actually observed. Keep read exposure separate from unverified write access. Do not run POST /containers/create against production targets.
What looks like this but isn't
- A reverse proxy or API gateway in front of 2375/2376 that terminates requests with 401/403 or mutual-TLS challenge. Verify the daemon itself, not the edge, is what answers `GET /version`.
- A daemon started with `--tlsverify` on 2376 that completes the TLS handshake but rejects clients lacking a CA-signed client certificate; this is authenticated and is not the exposure.
- An unrelated service or honeypot bound to 2375/2376 that does not return valid Docker JSON for `/version` and `/_ping`. Absence of the `Api-Version` header and proper body indicates a false match.
- Internal-only listeners reachable solely over a Unix socket or a host-restricted `127.0.0.1` bind that a misconfigured scanner reports as open due to SSH tunneling or port-forwarding rather than a true network exposure.
Remediation
- Stop binding the daemon to a network interface: remove any `-H tcp://...` flag and any `hosts` TCP entry from `/etc/docker/daemon.json` and the systemd unit, leaving only the local Unix socket, then restart `dockerd`.
- If remote management is genuinely required, enable mutual TLS by starting the daemon with `--tlsverify`, `--tlscacert`, `--tlscert`, and `--tlskey`, and distribute client certificates signed by your private CA so only verified clients connect on 2376.
- Prefer the SSH transport (`DOCKER_HOST=ssh://user@host`) or an SSH tunnel over exposing any TCP port, so authentication and encryption ride on hardened SSH rather than a bespoke TLS setup.
- Restrict the port at the network layer regardless of TLS: deny 2375/2376 from `0.0.0.0/0` in cloud security groups, host firewalls (iptables/ufw), and network ACLs, allowing only specific management source IPs.
- Review secrets, SSH keys, registry credentials, and cloud credentials accessible to the daemon. Rotate affected credentials based on the exposure window and investigation.
- Harden the daemon itself: run rootless Docker or enable user-namespace remapping (`userns-remap`), disable inter-container communication where unneeded, and forbid `--privileged` and host bind-mounts in your container-creation policy.
Operational checklist
- Continuously scan external and internal ranges for open 2375/2376 and alert on any new listener so a re-introduced exposure is caught within hours, not at the next pentest.
- Codify the daemon configuration in infrastructure-as-code with no TCP `hosts` entry, and add a policy check (OPA/Conftest or a CI lint) that fails any build provisioning `tcp://...:2375`.
- Require `--tlsverify` with mutual TLS on every Docker host that must accept remote connections, and audit certificate expiry and revocation on a schedule.
- Enforce least privilege on the daemon: rootless mode or userns-remap as the default baseline, with `--privileged` and host-path bind mounts gated behind explicit approval.
- Centralize Docker daemon and API access logs, and alert on `containers/create` or `containers/start` calls originating from unexpected source addresses.
- Include Docker API exposure in quarterly attack-surface reviews and validate that cloud security-group changes have not silently re-opened 2375/2376.
What to do next
An internet-reachable Docker API can disclose daemon data through anonymous read routes. Host-level risk depends on confirmed create and start access and the privileges the daemon holds. Rootless mode limits host privileges, but accessible workloads still need protection. Confirm the specific operation and access policy before assigning impact. Remove unnecessary TCP listeners, restrict network access, and review exposed data.
Each guide is written by our team, reviewed by a named security contributor, and cited against primary sources such as OWASP, CISA, NIST, and MITRE. We update pages when the underlying guidance changes. See our contributors and company.
FAQs.
Why is an exposed Docker API rated Critical instead of just High?
Is port 2376 safe because it uses TLS?
Can an attacker really take over the host just from the API, without an existing shell?
What do the /version and /containers/json endpoints reveal to an attacker?
How does Legba Adversary handle an Exposed Docker Engine API?
References
- 01
- 02
- 03Docker rootless modeDocker
- 04
- 05
- 06
Weakness references (CWE)
Keep exploring
- Legba AdversaryValidated external attack surface, mapped in minutes.
- Exposure libraryBrowse finding types Adversary discovers and validates.
- Exposed API keys scannerFind and validate leaked secrets across your surface.
- EASM glossaryPlain-language explainers for attack-surface terms.
- Browser threat playbooksHow modern attacks start in the browser.