Skip to content

Latest commit

 

History

History
241 lines (169 loc) · 6.19 KB

File metadata and controls

241 lines (169 loc) · 6.19 KB

Fix The Docker and UFW Security Flaw

When Docker is installed, published ports bypass UFW entirely. This guide fixes that.

Source: chaifeng/ufw-docker


I need to install the fix

Step 1 (optional): Clean State

  1. Docker iptables is enabled (remove --iptables=false if present)
  2. UFW FORWARD rule is set to DROP (default) sudo grep DEFAULT_FORWARD_POLICY /etc/default/ufw
  3. Remove old Docker rules from /etc/ufw/after.rules
  4. Restart Docker if configuration was changed

Step 2: Add UFW + Docker Rules

sudo vim /etc/ufw/after.rules

Append 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

Step 3: Restart UFW

sudo systemctl restart ufw && sudo ufw reload

If 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 53
I 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 443
I need to block a container's internet access
sudo ufw route deny from 172.17.0.9 to any
My 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 reload
I 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 80
I 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 enable

Then 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 page
How does this fix work?

The Problem

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

The Solution

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-by-Rule

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

Quick Reference

# 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