docker will bypass your firewall
Docker has networking-related behaviour that in my opinion is at best surprising and at worst dangerous.
Here is a minimal but innocent-looking
docker run line that exposes the external port
8080 to the internal port
80 of the container defined by the
docker run -p 8080:80 nginx
The problem is the implicit meaning of
8080:80 which, when put more explicitly, can be written as
0.0.0.0 prefix is the bind address, i.e., all addresses on all interfaces. In other words, you are completely exposed.
But I'm firewalled!
You might be thinking this is an acceptable and common default, because even if the container is only intended to be accessed locally, the system in question is firewalled with iptables or an iptables-based firewall like ufw.
It would be reasonable to think so, but this is where Docker knows better: it has configured iptables to give itself priority over all your other rules, and it will allow external incoming connections anyway.
In fairness the documentation does state this clearly, but most people don't read documentation unless they have to.
Docker installs two custom iptables chains named DOCKER-USER and DOCKER, and it ensures that incoming packets are always checked by these two chains first.
docker-compose is affected, too
docker-compose is used to declaratively run containers with a specific parameters, like exposed ports and volume paths. Rather than passing these values as arguments to the
docker command they can be encoded as YAML in
docker-compose.yml and then started using the simple command
docker-compose.yml is functionally equivalent to the
docker run line above.
version: '3' services: app: image: nginx ports: - 8080:80
docker-compose is just calling the underlying Docker engine the result would be exactly the same here too.
The simplest solution is to make it a habit to always specify the bind address (e.g.,
127.0.0.1) and never leave a port definition bare. This is a good habit to get into even when the intended address is
0.0.0.0 as it forces you to think about what you actually want.
A more robust general-purpose solution that entirely avoids this class of problem is to not use a local firewall at all. Use a hardware firewall or the one available from most cloud providers.
The lesson here is check your assumptions and always try to hack yourself first. Running regular port scans on your own infrastructure can be an invaluable tool to help you find unintentionally exposed services before someone else does.
This isn't 100% equivalent. A lack of bind address will default to IPv4 and IPv6 if available, but using
0.0.0.0limits it to IPv4. ↩