The UFW and Docker Conflict
Ubuntu server administrators rely on UFW (Uncomplicated Firewall) for its simplicity and ease of use when locking down incoming connections. A standard security practice is to configure UFW to deny incoming by default, and only allow specific ports like 22 for SSH and 443 for HTTPS.
However, when you install Docker on Ubuntu, it creates a massive, often unexpected security hole. Docker manipulates iptables (the underlying routing engine that UFW also uses) directly to facilitate container networking. Because Docker’s rules evaluate before UFW’s rules, any port you expose in a Docker container (e.g., using -p 8080:80) completely bypasses UFW. Even if UFW says port 8080 is blocked, your container will be accessible to the public internet.
The Solution: Modifying Docker’s Iptables Behavior
There are several approaches to fixing this flaw, such as binding Docker containers explicitly to 127.0.0.1, but the most robust method is to patch the UFW routing chains so that Docker traffic respects the UFW firewall rules.
Step 1: Download the UFW-Docker Script
Instead of manually writing complex iptables routing chains, the open-source community maintains a widely accepted script called ufw-docker that automatically patches the integration.
First, download the script using wget:
sudo wget -O /usr/local/bin/ufw-docker https://github.com/chaifeng/ufw-docker/raw/master/ufw-docker
Make the script executable:
sudo chmod +x /usr/local/bin/ufw-docker
Step 2: Install the Patch
Run the installation command. This command appends the necessary routing logic into the /etc/ufw/after.rules file.
sudo ufw-docker install
After installing, you must restart UFW to apply the changes:
sudo systemctl restart ufw
How to Expose Docker Containers Securely
Now that the patch is active, if you run a Docker container (for example, a database on port 3306), it will not be accessible from the outside world, even if you used the -p 3306:3306 flag. UFW is successfully blocking it.
To safely expose that specific container port, you must now use the ufw-docker command instead of the standard ufw allow command.
Exposing a Port to the Public
If you want to open port 80 for an Nginx container named my_web_server to the entire internet:
sudo ufw-docker allow my_web_server 80
Exposing a Port to a Specific IP Address
If you have a MySQL container named my_database on port 3306, and you only want your application server at 192.168.1.100 to access it:
sudo ufw-docker allow my_database 3306/tcp -s 192.168.1.100
Checking Status
To view the current UFW rules that apply specifically to Docker containers, run:
sudo ufw-docker status
Conclusion
The silent bypass of UFW by Docker is one of the most common vulnerabilities found in self-hosted Ubuntu servers. By implementing the ufw-docker patch, you restore the intended behavior of your firewall, ensuring that you have explicit, centralized control over which containerized services are exposed to the internet.