CL-0005: Ports published on 0.0.0.0 (all interfaces)¶
Severity: MEDIUM
Derivation (see severity model):
- Baseline: A — the attacker already has code execution in this container, as the workload uid
- Precondition: Second flaw — an exposed port is only a compromise if the service behind it has an exploitable defect or a weak credential
- Impact: Single container — the service the port belongs to
- Qualifier/modifier: pre-foothold reach — no foothold anywhere is required to reach the port, which is what this modifier prices
- Derived: Second flaw × Single container + pre-foothold reach = HIGH
- Shipped: MEDIUM — override
detection-precision— the dangerous case is the binding that was not meant to be public, and the file does not say which is which. An exposed datastore is serious; an exposed web server is the design. Well-known datastore and admin ports are a usable proxy, so the path off this override is a split rather than a smarter matcher (ADR-020 Appendix B) - Evidence:
_cl0005— a bare published port binds all interfaces, verified live. Docker publishes ports by writing iptables rules directly, so the binding is reachable even where UFW or firewalld would deny it
References: - OWASP Docker Security Rule #5a - CIS Docker Benchmark 5.14 — Ensure that incoming container traffic is bound to a specific host interface
What it detects¶
Port mappings that don't specify a bind address or that bind to a wildcard, in both short syntax ("8080:80", "0.0.0.0:8080:80", "[::]:8080:80") and long syntax (published: without host_ip:, or with host_ip: "0.0.0.0" / host_ip: "::"). A bare short-syntax port with no colon ("80", 3001, a "3000-3005" range) is also flagged: Docker still publishes it, assigning a random (ephemeral) host port bound to all interfaces (docker compose config normalizes - "80" to a published target, and docker compose up binds it on 0.0.0.0). Bind it with 127.0.0.1::80 to keep the ephemeral port on localhost. The bind address is decided by parsing it, not by matching the spelling: every way of writing the unspecified address is the same address and publishes on every interface — 0.0.0.0, ::, [::], [::0], [0:0:0:0:0:0:0:0], [::ffff:0.0.0.0]. Specific bind addresses including loopback (127.0.0.1, [::1]) are not flagged, and a value that is not an address at all (a hostname) is not treated as a wildcard.
Why it matters¶
Docker publishes ports by manipulating iptables directly, bypassing host firewalls like UFW and firewalld. A port published without a bind address is accessible on all network interfaces, potentially exposing services to the public internet even when host firewall rules would otherwise block the traffic.
Fix¶
# Instead of (binds to all interfaces, bypasses host firewall):
ports:
- "8080:80"
# Bind to localhost only:
ports:
- "127.0.0.1:8080:80"
# Long syntax:
ports:
- target: 80
published: 8080
host_ip: "127.0.0.1"
If this service needs to be publicly accessible, place it behind a reverse proxy (Traefik, Caddy, nginx) that handles TLS termination, and bind only the reverse proxy's ports publicly.
For systemic mitigation, configure the DOCKER-USER iptables chain on the host so that Docker's bypass of UFW/firewalld becomes per-rule rather than per-service. This is documented in Docker and iptables. Per-service binding is whack-a-mole; the chain-level rule is the durable fix.
Swarm caveat¶
host_ip in the long syntax is not always honored under Swarm mode — Swarm uses an ingress mesh and may publish ports on all manager nodes regardless. If you're targeting Swarm, port publishing is a Swarm concern; this rule still flags the file but treat the fix as Swarm-specific.
When to suppress¶
- Public-facing service on a VPS where the cloud provider's firewall is the security boundary and
0.0.0.0is intended. Suppress with areason:naming the firewall layer (e.g.reason: "GCP firewall enforces ingress; UFW disabled by design").
ATT&CK coverage¶
No adversary technique maps to this rule. That is a finding about the rule, not an omission — see below.
It does enable T1190 Exploit Public-Facing Application, but enabling is not the same as being a technique: the misconfiguration widens the attack surface an adversary can reach rather than supplying a step they execute.
See also¶
- CL-0008 —
network_mode: host(port-publish controls don't apply at all)