A server with ufw set to deny incoming traffic looks closed. Then a Compose file publishes a database port, and the database is reachable from the internet anyway.
This explainer shows the three places a request can come from, and how each way of publishing a port changes which of them reach the container.
Explore it
- The internetBlocked The port is not published.
- The Docker hostReaches The host reaches every container port on its bridge networks.
- A container on the same networkReaches Containers on the same network reach each other's ports by name.
Container, port 80
Port mapping: No ports: entry
ufw: off
From the internet: Blocked. From the host: Reaches. From the same network: Reaches.
Step by step
Compose puts the services of a project on their own bridge network. Every port of a container on that network is reachable from the Docker host and from the other containers on the same network, by service name, whether or not it is published.
Nothing outside the server reaches it: without
ports:, the container has no address on the internet.ports: ["8080:80"]has no host address, so Docker publishes the port on all of them:0.0.0.0and[::]. Anyone who can reach the server's public IP on port 8080 now reaches the container's port 80.That is what a port mapping copied from a README usually does.
Turn on ufw with its default "deny incoming" and the port stays open. Docker routes traffic to a published port with its own packet-filtering rules, before ufw's rules are applied, so ufw never sees it.
To filter a published port, use Docker's
DOCKER-USERchain or the provider's firewall, or don't publish it on a public address at all.ports: ["127.0.0.1:8080:80"]publishes the port on the loopback address only. The host reaches it atlocalhost:8080; the internet does not, with or without ufw.Put a reverse proxy in front of what should be public, and reach admin interfaces through an SSH tunnel:
ssh -L 8080:127.0.0.1:8080 <user>@<server>.
Example values Ports 8080 and 80 are example values.
What the diagram shows
The diagram shows a container with port 80, a port mapping, ufw, and three sources: the internet, the Docker host and another container on the same network. Each source is marked "reaches" or "blocked", with the reason.
The controls choose how the port is published (not published, on every address, on 127.0.0.1) and whether ufw denies incoming traffic. Only publishing on every address lets the internet in, and ufw does not change that; the host and the containers on the same network always reach the port.
Try it in the tool
What this does not cover
Covers Docker Engine's default bridge networking with its default iptables rules on Linux. Rootless Docker, Docker Desktop, the host network mode and custom firewall setups behave differently.
Related pages
Sources
How ANAROQ sources its data and numbers is described in the methodology. Read the methodology