When Docker is installed, published ports bypass UFW entirely. This guide fixes that.
Source: chaifeng/ufw-docker
I need to install the fix
- Docker iptables is enabled (remove
--iptables=falseif present) - UFW FORWARD rule is set to DROP (default)
sudo grep DEFAULT_FORWARD_POLICY /etc/default/ufw - Remove old Docker rules from
/etc/ufw/after.rules - Restart Docker if configuration was changed
sudo vim /etc/ufw/after.rulesAppend at the end (also add to /etc/ufw/after6.rules for IPv6):
# 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
# Private networks (bypass UFW)
-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
# DNS return
-A DOCKER-USER -p udp -m udp --sport 53 --dport 1024:65535 -j RETURN
# Block public access to private ranges
-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
# Logging & drop
-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
sudo systemctl restart ufw && sudo ufw reloadIf rules don't take effect, reboot the system.
I need to allow public access to a container
Use the container's internal port, not the host port (e.g., if mapped as 8080:80, use 80).
# Allow port 80 for all containers
sudo ufw route allow proto tcp from any to any port 80
# Allow specific container only
sudo ufw route allow proto tcp from any to 172.17.0.2 port 80
# Allow UDP (e.g., DNS)
sudo ufw route allow proto udp from any to any port 53I need to allow access from specific IPs only
# From a specific IP
sudo ufw route allow proto tcp from 203.0.113.10 to any port 80
# From a CIDR range
sudo ufw route allow proto tcp from 203.0.113.0/24 to any port 443I need to block a container's internet access
sudo ufw route deny from 172.17.0.9 to anyMy LAN uses 10.x.x.x addresses
By default, all private ranges (10/8, 172.16/12, 192.168/16) bypass UFW.
Pick your scenario:
| Your Setup | Edit /etc/ufw/after.rules |
|---|---|
| LAN=10.x.x.x, Docker=172.17.x.x | Comment out: # -A DOCKER-USER -j RETURN -s 10.0.0.0/8 |
| LAN=10.10.x.x, Docker=10.20.x.x | Replace with: -A DOCKER-USER -j RETURN -s 10.20.0.0/16 |
| Need full control | Comment out all three RETURN lines (may break container communication) |
Then restart UFW:
sudo systemctl restart ufw && sudo ufw reloadI want to use the ufw-docker utility instead
The ufw-docker utility automates rule management:
# Install
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
sudo ufw-docker install
# Usage
ufw-docker check # Check setup
ufw-docker status # Show allowed routes
ufw-docker allow <container> 80/tcp # Allow port
ufw-docker delete allow <container> 80/tcp # Remove rule
# Docker Swarm
ufw-docker service allow web 80I need to set up UFW from scratch
sudo apt install ufw -y
sudo systemctl enable ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw enableThen expand "I need to install the fix" above.
I want to test if the fix is working
# Run test container
docker run -d --name web -p 8080:80 nginx
# Before allowing: should be blocked from external
curl http://your-server-ip:8080 # timeout or refused
# Allow access
sudo ufw route allow proto tcp from any to any port 80
# After allowing: should work
curl http://your-server-ip:8080 # nginx welcome pageHow does this fix work?
Docker adds iptables rules in PREROUTING and FORWARD chains, which run before UFW's INPUT chain:
Normal: Internet → UFW (checked) → Server
Docker: Internet → UFW (BYPASSED) → Docker containers
We hook into DOCKER-USER chain (the only chain Docker doesn't overwrite):
Fixed: Internet → DOCKER-USER → UFW check → Docker containers
│
└── Private networks bypass UFW
| Rule | Purpose |
|---|---|
-A DOCKER-USER -j ufw-user-forward |
Route to UFW (ufw route rules apply here) |
-A DOCKER-USER -j RETURN -s 10.0.0.0/8 |
LAN/container traffic skips UFW |
-A DOCKER-USER -p udp --sport 53... |
DNS responses allowed |
-A DOCKER-USER -j ufw-docker-logging-deny... |
Block external → private |
-A DOCKER-USER -j RETURN |
Continue to Docker if not blocked |
-A ufw-docker-logging-deny -j DROP |
Final drop |
# Allow container port
sudo ufw route allow proto tcp from any to any port <port>
# Allow from specific IP
sudo ufw route allow proto tcp from <IP> to any port <port>
# Block container internet
sudo ufw route deny from <container_ip> to any
# Reload
sudo systemctl restart ufw && sudo ufw reload