docker ufw – Why Does Docker Bypass UFW Rules Intermittently?

dockerlinuxufw

I am using ufw to setup a firewall on my host system. It seems that ufw would let me bypass certain rules when using it combined with docker in some cases.

I am aware that docker by default alters iptables directly, which leads to certain problems, especially with ufw, but I have encountered an issue that seems very strange to me.

Here is a breakdown of what I did.

  1. I want to deny all incoming traffic:
sudo ufw default deny incoming
  1. I want to allow ssh:
sudo ufw allow ssh
  1. I want to allow everything that goes from my host system back to my host system on port 8181 (Context: this shall later be used to build a ssh tunnel to my host and access port 8181 from anywhere – but is not important for now)
sudo ufw allow from 127.0.0.1 to 127.0.0.1 port 8181
  1. I enable my firewall settings:
sudo ufw enable

If I now have a look at the firewall status via sudo ufw status it states the following:

Status: active

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW       Anywhere                  
127.0.0.1 8181             ALLOW       127.0.0.1                 
22/tcp (v6)                ALLOW       Anywhere (v6)

Looks good to me, but now comes the strange part. I have an API, that runs inside a docker container available at port 8080 internally.

If I now run the docker container with the following command and map port 8080 to port 8181 on my host system

docker run -d --name ufw_test -p 8181:8080 mytest/web-app:latest

it seems to bypass my rule that I have set earlier to only allow traffic from 127.0.0.1 to 127.0.0.1 on port 8181. I was able to access my API from anywhere. I tried it with different PCs on the same Network and my API was accessible via 192.168.178.20:8181 from another PC. I figured, a way to fix this would be starting my container like so:

docker run -d --name ufw_test -p 127.0.0.1:8181:8080 mytest/web-app:latest

This would restrict access to my API the way I intended it to, however I wonder, what would be the reason that the second command worked, where the first did not?

Best Answer

ufw shows only the ufw configuration and any rules inserted directly in your firewall configuration (with iptables directly or another tool such as docker) without going through ufw are NOT displayed.

Firewall rules in Linux are applied in the order they are listed. When you start a docker container docker will insert the rules your docker containers need before existing rules and the rule-set you maintain with ufw.

In other words Docker exposing a port takes precedence over a subsequent ufw rules closing a particular port.

Check for instance with [sudo] iptables-save what your effective rule set is.

As to why -p 127.0.0.1:8181:8080 works differently?
The firewall rule docker creates will still take precedence to your ufw rules, but rather than exposing the port on all interfaces, including to the public, you now instruct docker to be much more restrictive and only expose the port on localhost.