Some setups expose the Docker Engine API over a TCP port instead of the local Unix socket. What is the danger of binding it to tcp://0.0.0.0:2375, and how do you expose it remotely without opening the host to anyone?
answer
- 2375 = plaintext no-auth; 2376 = TLS
- 0.0.0.0:2375 is internet-scanned, instant compromise
- mTLS: CA + server cert + client certs, --tlsverify
- SSH transport DOCKER_HOST=ssh:// reuses SSH auth
- still root-equivalent: guard keys like root
basics
~20 sPlain TCP on port 2375 is unauthenticated and unencrypted; anyone who can reach it gets root-equivalent daemon control, and internet scanners actively hunt for it. Expose remotely only over port 2376 with TLS mutual authentication (verify client certs), or better, tunnel over SSH (DOCKER_HOST=ssh://).
solid answer
~50 sThe Unix socket is protected by filesystem permissions. A raw TCP listener has **none**: `dockerd -H tcp://0.0.0.0:2375` publishes the full Engine API to the network with no auth and no encryption. Since the API is root-equivalent, anyone who can route to that port owns the host, and automated scanners crawl the internet for open 2375 specifically to plant cryptominers. To expose it remotely safely, use **TLS mutual authentication** on the conventional port **2376**: generate a CA, a server cert for the daemon, and client certs; start the daemon with `--tlsverify --tlscacert --tlscert --tlskey`; clients present their own cert (`docker --tlsverify`). Now only holders of a CA-signed client cert can connect, over an encrypted channel. Often simpler and safer is not to open a Docker port at all: use the **SSH transport**, `DOCKER_HOST=ssh://user@host`, which tunnels the socket over an existing authenticated SSH connection. No new listener, no cert management, and you reuse SSH's access controls.
code
bash · 7 lines# DANGER: unauthenticated, unencrypted, root-equivalent to the whole network
dockerd -H tcp://0.0.0.0:2375
# Safer: mutual-TLS on 2376, daemon requires a signed client cert
dockerd --tlsverify \
--tlscacert=ca.pem --tlscert=server-cert.pem --tlskey=server-key.pem \
-H tcp://0.0.0.0:2376go deeper
Know that plain TCP 2375 is unauthenticated and must never be exposed; remote access needs TLS or SSH.
Explain 2375 vs 2376, why --tlsverify (mutual auth) is required, and that SSH transport is often simpler.
Weigh mTLS cert lifecycle vs SSH tunneling, and treat client keys as root credentials with rotation/revocation.
Set policy: no plaintext daemon ports anywhere; prefer SSH/mediated access; manage any client-cert PKI with the same rigor as host root access.
## Two ways the daemon can listen The Docker daemon can bind its Engine API to a Unix socket (`-H unix:///var/run/docker.sock`, the default) and/or a TCP socket (`-H tcp://...`). The Unix socket inherits filesystem permissions as its access control. A TCP socket, by itself, has **no** access control: TCP does not authenticate callers. ## Why tcp://0.0.0.0:2375 is a disaster Port **2375** is the conventional **plaintext** Docker API port. Binding it to `0.0.0.0` publishes the entire root-equivalent API to every network the host is on, with: - **No authentication:** any client that can open the socket issues commands. - **No encryption:** credentials and data traverse the wire in clear. Because the payoff is root on the host, this is one of the most actively scanned ports on the internet; misconfigured cloud VMs get compromised within minutes and are used to run cryptominers or pivot deeper. Even on a 'private' network, it trusts every host and container that can route to it. Treat an open 2375 as an immediate critical finding. ## Doing it right, option 1: TLS mutual auth on 2376 Port **2376** is the conventional **TLS** port. Mutual TLS gives both encryption and authentication: 1. Create a **CA** key/cert. 2. Issue a **server** cert for the daemon (with the host's DNS/IP in the SAN). 3. Issue **client** certs signed by the same CA. 4. Start the daemon with `--tlsverify --tlscacert=ca.pem --tlscert=server-cert.pem --tlskey=server-key.pem -H tcp://0.0.0.0:2376`. `--tlsverify` makes the daemon **require and verify** a client cert. 5. Clients connect with `--tlsverify` and their key/cert, or set `DOCKER_HOST=tcp://host:2376`, `DOCKER_TLS_VERIFY=1`, `DOCKER_CERT_PATH=...`. Now only a client holding a CA-signed certificate can talk to the daemon, over an encrypted channel. Operationally you must protect the CA and client keys, rotate certs, and revoke lost ones, which is real work; a leaked client key is again root on the host. ## Doing it right, option 2: SSH transport (often better) Since Docker 18.09 the CLI supports `DOCKER_HOST=ssh://user@host`, which tunnels the Engine API over SSH to the remote host's Unix socket. Advantages: - **No new network listener** to secure or scan; you reuse SSH. - **Reuses existing auth** (SSH keys, bastions, MFA, audit). - **No cert lifecycle** to manage. The remote user still needs socket access (docker group / root), so it remains root-equivalent; but access is gated by SSH rather than an open API port. For most 'I need to run Docker on a remote box' cases this is the simplest secure answer. ## Decision summary - **Never** bind plaintext `2375` to anything reachable, least of all `0.0.0.0`. - **Prefer SSH transport** for ad-hoc/remote control: least new surface. - **Use mTLS on 2376** when a persistent programmatic TCP endpoint is genuinely required (e.g. a remote CI builder), and manage the certs seriously. - Whatever the transport, the API stays root-equivalent, so scope who holds credentials as tightly as you would root SSH.
- Is enabling TLS on port 2376 without --tlsverify enough?No. Plain `--tls` encrypts the channel but does not require the client to present a certificate, so anyone who can reach the port can still connect, just over TLS. You need `--tlsverify`, which enforces mutual authentication against your CA. Encryption without client-cert verification is not access control.
- Why is the SSH transport often preferred over mTLS?It opens no new network listener, so there is nothing extra to scan or firewall, and it reuses your existing SSH authentication, bastions, and audit trail. There are no certificates to issue, rotate, or revoke. The tradeoff is you need SSH access and socket permission on the remote host.
- The API is still root-equivalent over TLS, so what must you protect?The client private keys and the CA. A leaked client key lets the holder run privileged host-root containers remotely, i.e. full host root. Store keys like root SSH keys, scope which clients get certs, and have a revocation path for the CA.
saying these in an interview costs you the question
- Binding tcp://0.0.0.0:2375 and calling a private network 'safe enough'
- Thinking `--tls` (encryption only) authenticates clients; it does not
- Assuming TLS removes the root-equivalence of the API
- Not protecting/rotating client certs and the CA
- Being unaware of the SSH transport as a simpler alternative