Docker audit fix

How to Disable Docker Inter-Container Connectivity

Containers on Docker's default bridge can all reach each other's ports unless the daemon sets icc to false, so one compromised service has a free path to the rest. Put services on user-defined networks, which are isolated from each other.

Hiren KalariyaLast reviewed: Oct 19, 2026Check inter-container-connectivity
Medium
severity
Yes
needs root or sudo
4
results it can return
2 of 5
checks in this audit

Every threshold on this page is transcribed from the docker-security-network-supply-chain audit script that CtrlOps runs, and a build check fails if the two ever disagree. See all 5 Network & Supply Chain checks, or how the audit runs.

Medium severityNeeds sudo

What this check reads

It runs one docker inspect across every running container id with a Go template that walks .NetworkSettings.Networks and prints the container's name whenever one of its network keys is exactly bridge, the default bridge. It then reads com.docker.network.bridge.enable_icc from docker network inspect bridge. A count of zero containers on the default bridge is a PASS, and so is any count when that icc option reads false. Anything else is the WARN, which names up to five of the containers sharing the bridge.

When it applies

Needs a reachable daemon and at least one running container; otherwise SKIP. It looks at exactly one network - the default bridge - and asks two questions: are any running containers attached to it, and is com.docker.network.bridge.enable_icc set to false. User-defined networks are not examined, which is why "nothing on the default bridge" is an immediate PASS: containers on a user-defined network can still all reach each other, but that is considered the properly scoped arrangement. Compose creates user-defined networks by default, so most Compose hosts pass trivially.

What each result means

Result thresholds this check applies
ResultWhenWhat it means
SKIPDaemon unreachable / no running containersNothing was evaluated. No daemon answered, or nothing was running, so the default bridge was never inspected at all.
PASSNothing on the default bridgeNo running container sits on the default bridge. Containers sharing a user-defined network can still reach each other, and this check never looks at those.
PASSDefault bridge with `icc=false`Containers are on the default bridge, but the daemon blocks traffic between them, so the bridge is not a shared free-for-all.
WARNContainers share the default bridge with icc onEvery container named can reach every other one's ports, so compromising any single service hands an attacker a route to the rest.

Why it matters

On the default bridge every container can reach every other container's ports (icc), and there is no DNS-based service discovery, so people use --link or hard-coded IPs. User-defined bridge networks give automatic DNS, and containers on different networks cannot talk at all, which is the segmentation you want between, say, a web tier and a database. CIS Docker 2.2 "Ensure network traffic is restricted between containers on the default bridge" and 5.29 "Ensure that Docker's default bridge 'docker0' is not used"; OWASP Docker Cheat Sheet Rule #5; NIST SP 800-190 4.3.

Why it fails, and when it is wrong

  • docker run without --network lands on the default bridge. Compose projects create their own network and PASS automatically.
  • --icc=false (daemon.json "icc": false) disables default-bridge chatter but also breaks legitimate communication that relies on it; user-defined networks are the usual fix instead.
  • Containers on host network are not on any bridge and are not evaluated here (they have no isolation at all; see Published Container Ports and Capabilities).

How to fix it

Find the containers on the default bridge

docker inspect -f '{{.Name}} {{range $k,$v := .NetworkSettings.Networks}}{{$k}} {{end}}' $(docker ps -q) | grep -w bridge

Create a network per group that must talk

docker network create backend
docker network create frontend

Containers on different user-defined networks cannot reach each other at all, and each network gives DNS by container name.

Attach the containers and recreate them

docker run -d --network backend --name db postgres:16
docker run -d --network backend --name app <image>
docker network connect frontend app

Compose does this for you with its own project network; after changing networks there, run docker compose up -d --force-recreate.

Or switch off icc on the default bridge

{ "icc": false }

Set it in /etc/docker/daemon.json and restart Docker; it cannot be changed on a running daemon. It also breaks traffic that relied on the bridge, including legacy --link setups, which is why user-defined networks are the usual answer.

Verify the fix

# Which containers are on the default bridge:
docker inspect -f '{{.Name}} {{range $k,$v := .NetworkSettings.Networks}}{{$k}} {{end}}' $(docker ps -q)
# expected: no container listing "bridge"

docker network inspect bridge --format '{{index .Options "com.docker.network.bridge.enable_icc"}}'
# expected: false, if anything is still on the default bridge

# Prove isolation between two containers that should not talk:
docker exec <container-a> sh -c 'ping -c1 -W1 <container-b-ip> || wget -qO- --timeout=2 http://<container-b-ip>:80'
# expected: no route / timeout

Debugging

Two different messages. 'Docker daemon not reachable' means the audit account could not talk to the daemon: check systemctl is-active docker, then docker ps as the audit user and sudo docker ps, and run the audit as root or with sudo if only the second works. 'No running containers to evaluate' means there was genuinely nothing on any network to inspect. The Docker Inventory check covers the unreachable case in more detail.

Move them onto user-defined networks, which give per-network scoping and DNS by container name. In Compose this is automatic; with docker run, create a network (docker network create app) and use --network app.

The expected limit of this check. Containers sharing a user-defined network have full connectivity between them, and this check does not look at those at all. Segment by putting unrelated services on separate networks and verify with the docker exec probe above.

Requires {"icc": false} in /etc/docker/daemon.json and a daemon restart; it cannot be changed on the fly. It also breaks any container-to-container traffic that relied on it, including legacy --link setups.

Network membership is set at creation. docker inspect shows the truth; docker compose up -d --force-recreate after changing the network configuration.

Sources

How the script reads this

Next

Re-run the Network & Supply Chain audit after applying the fix and confirm this check moves to PASS. CtrlOps runs all 5 checks over your existing SSH connection and scores the result, so the change is visible without reading another config file.

All 5 Network & Supply Chain fixes
Audit your fleet

Find every one of these on every server, in one click

CtrlOps runs this audit over your existing SSH connection - no agents, no scripts to manage. $7/user/month after a 1 month free trial - no credit card required.

Windows

✓ Start instantly·✓ No credit card·✓ No sneaky autorenewals