Quick Links

Surprisingly, Docker does not work out of the box with Linux's "Universal Firewall," or UFW. They both modify the same iptables configuration, and this can lead to misconfigurations exposing containers that weren't supposed to be public. Here's how to fix it.

Why Doesn't Docker Work With UFW?

UFW is intended to be a very simple firewall. The problem is that both UFW and Docker try to modify the same underlying firewall rules, and this conflict requires extra setup to sort out if you want to run UFW and Docker together.

If you set up a basic UFW firewall to deny by default and allow HTTP and SSH, this will appear secure---but it will not block Docker from starting containers bound to other ports. This issue can be hard to catch, as UFW and Docker are separate systems. UFW is unknowingly lying to you and will not show open ports from Docker containers.

This can be a major problem if you don't catch it. For example, perhaps you wanted to run an internal admin panel on port 8000, and whitelist it to your own IP address. While this isn't the most secure setup to begin with, it's usually alright, especially if the panel has additional authentication.

However, UFW will show the firewall rule as properly whitelisted, and it will of course be visible to you from your whitelisted location. But, if it's run through Docker, it will be visible on port 8000 from anywhere by default.

Fixing Docker's Config

There is a solution Docker provides, by editing

        /etc/default/docker
    

 or

        /etc/docker/daemon.json
    

 and simply turning off Docker's iptables functionality altogether:

DOCKER_OPTS="--iptables=false"

This works, however, this is only a half solution. It disables Docker's ability to manage its own networking and can cause containers to not be able to access the internet at all out of the box. This can still work, but you'll need to manually maintain iptables rules for Docker containers and custom networks, which is complicated, annoying, and defeats the purpose of UFW's simplicity.

The real solution is complicated, but luckily is common enough that there's a helpful Github repo detailing the problem and the steps to fix it. Essentially, you need to modify UFW's config at /etc/ufw/after.rules to add the following block at the end:

# BEGIN UFW AND DOCKER
    

*filter

:ufw-user-forward - [0:0]

:ufw-docker-logging-deny - [0:0]

:DOCKER-USER - [0:0]

-A DOCKER-USER -j ufw-user-forward

-A DOCKER-USER -j RETURN -s 10.0.0.0/8

-A DOCKER-USER -j RETURN -s 172.16.0.0/12

-A DOCKER-USER -j RETURN -s 192.168.0.0/16

-A DOCKER-USER -p udp -m udp --sport 53 --dport 1024:65535 -j RETURN

-A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16

-A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8

-A DOCKER-USER -j ufw-docker-logging-deny -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12

-A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 192.168.0.0/16

-A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 10.0.0.0/8

-A DOCKER-USER -j ufw-docker-logging-deny -p udp -m udp --dport 0:32767 -d 172.16.0.0/12

-A DOCKER-USER -j RETURN

-A ufw-docker-logging-deny -m limit --limit 3/min --limit-burst 10 -j LOG --log-prefix "[UFW DOCKER BLOCK] "

-A ufw-docker-logging-deny -j DROP

COMMIT

# END UFW AND DOCKER

You can do that manually, but there's a nice utility also provided in this repo that will automate it, and provide some helpful commands for checking the real firewall status. You can download it from this repo:

sudo wget -O /usr/local/bin/ufw-docker https://github.com/chaifeng/ufw-docker/raw/master/ufw-docker
    

sudo chmod +x /usr/local/bin/ufw-docker

Then, install the config, and restart UFW.

ufw-docker install
    

sudo systemctl restart ufw

Once restarted, the changes should apply automatically, but if they don't, you may need to restart Docker or your machine in general. Once it's enabled, the ports should all be properly blocked.

Whitelisting Docker Container Ports With UFW

This solution does require you to configure the port a little differently. The ufw-docker utility has a command that will selectively whitelist ports to specific Docker containers.

ufw-docker allow httpd 80

However, if you want to use a more advanced rule, such as IP based whitelisting, you'll have to use ufw route allow

ufw route allow proto tcp from 1.2.3.4 to any port 9443