udwall is a declarative tool to manage UFW and Docker firewall rules using a single Python configuration file. It fixes the Docker security flaw where containers bypass UFW, and it automates rule management so you never have to run manual ufw allow commands again.
UFW is a popular iptables front end on Ubuntu that makes it easy to manage firewall rules. However, when Docker is installed, Docker modifies iptables directly, bypassing UFW rules. This means published ports (e.g., -p 8080:80) are accessible from the outside world, even if UFW is set to deny them.
The issue is detailed as follows (Source: ufw-docker/problem):
- UFW is enabled on a server that provides external services, and all incoming connections that are not allowed are blocked by default.
- Run a Docker container on the server and use the
-poption to publish ports for that container on all IP addresses. For example:docker run -d --name httpd -p 0.0.0.0:8080:80 httpd:alpine. This command will run an httpd service and publish port 80 of the container to port 8080 of the server. - UFW will not block external requests to port 8080. Even the command
ufw deny 8080will not prevent external access to this port because Docker's iptables rules take precedence. - This is a serious security flaw, as internal services can be inadvertently exposed to the public internet.
- Searching for "ufw docker" on the web reveals a lot of discussion on this critical security flaw (source):
The tool ufw-docker solved these issues but had a few drawbacks:
- It fixed the Docker security flaw where containers bypass UFW.
- Prerequisites: It required downloading a script to
/usr/local/binand running it with sudo. - Mechanism: It modified the
/etc/ufw/after.rulesfile to add a customDOCKER-USERchain that correctly filters traffic destined for Docker containers, ensuring UFW rules are respected. (See ufw-docker README for more details).
- Manual Steps: It required a lot of manual steps to manage rules for each container.
- Persistence Issues: Whenever UFW was disabled, Docker ports were still blocked (or rules persisted unexpectedly).
- Difficult Uninstall: To uninstall
ufw-docker, you historically needed to remove iptables rules manually and restart the server (source).Note: Recently
ufw-dockeradded an uninstall command to remove the configuration (source).
udwall is a declarative tool to manage UFW and Docker firewall rules using a single configuration file.
- It fixes the Docker security flaw where containers bypass UFW.
- It automates rule management so you never have to run manual
ufwcommands again. - Configuration as Code: Define your entire firewall state in one file (
udwall.conf). - True Synchronization:
udwallperforms atomic updates, removing old unused rules and applying new ones automatically. - Safety First: Automatically backs up
/etc/ufwandiptablesbefore every change.
You can install udwall with a single command:
curl -fsSL https://raw.githubusercontent.com/HexmosTech/udwall/main/install.sh | sudo bashTo install a specific version (e.g., v0.0.2), run:
curl -fsSL https://raw.githubusercontent.com/HexmosTech/udwall/main/install.sh | sudo bash -s -- --v0.0.2This script will:
- Check for dependencies (
python3,ufw,curl). - Download
udwallto/usr/local/bin/udwall. - Set up a default configuration at
/etc/udwall/udwall.conf.
Currently udwall supports the following rule patterns:
- Docker Forwarding (Any IP): Allow traffic to a Docker container from anywhere.
ufw route allow from any to any port <PORT> proto tcp
- Host Service (Any IP): Allow traffic to a service on the host (e.g., PostgreSQL) from anywhere.
ufw allow <PORT>
- Docker Forwarding (Specific IP): Allow traffic to a Docker container only from a specific IP.
ufw route allow from <IP> to any port <PORT> proto tcp
- Host Service (Specific IP): Allow traffic to a host service only from a specific IP.
ufw allow from <IP> to any port <PORT> proto tcp
- Rule Deletion: Setting
isEnabled: falsewill automatically generate the correspondingdeletecommand for any of the above patterns.
Follow these simple steps to configure and activate udwall on your system. This process ensures your current firewall state is captured and safely managed going forward.
Note:
udwallrequiressudoprivileges.
You can create a configuration file manually or use the --create command to generate one from your current live UFW rules.
sudo udwall --createThis creates a udwall.conf file in /etc/udwall/udwall.conf.
You can create a backup of your current UFW rules with the --backup command.
sudo udwall --backupThis creates a timestamped backup in /home/ubuntu/backup/firewall-backup/, containing both iptables and UFW rules.
Edit the configuration file at /etc/udwall/udwall.conf.
# udwall.conf
rules = [
# Allow SSH access from any source
{'from': 'any', 'connectionType': 'tcp', 'to': 'OpenSSH', 'isDockerServed': False, 'isEnabled': True},
# Allow HTTP and HTTPS traffic to the host
{'from': 'any', 'connectionType': 'tcp', 'to': 80, 'isDockerServed': False, 'isEnabled': True},
{'from': 'any', 'connectionType': 'tcp', 'to': 443, 'isDockerServed': False, 'isEnabled': True},
# Allow traffic to a Docker container on port 8080 from a specific IP
{'from': '192.168.1.100', 'connectionType': 'tcp', 'to': 8080, 'isDockerServed': True, 'isEnabled': True},
# Allow a UDP port range for an application like Mosh
{'from': 'any', 'connectionType': 'udp', 'to': '60000:61000', 'isDockerServed': False, 'isEnabled': True},
]udwall has 5 config options:
from: The source IP address orany.connectionType: The protocol type (e.g.,tcp,udp).to: The destination port or service name.isDockerServed: Whether the rule is for a Docker container.isEnabled: Whether the rule is enabled.dip: (Optional) The Docker container IP address. Useful for targeting specific containers.
If you are running a simple python server on port 4050, here's how to handle it with udwall.
rules = [
# Allow access to Docker container on port 4050 from any IP
{'from': 'any', 'connectionType': 'tcp', 'to': 4050, 'isDockerServed': False, 'isEnabled': True},
]If you are running a Docker container that exposes a port (e.g., port 4050), here's how to handle it with udwall.
Docker Compose Example:
version: '3.8'
services:
web:
# We've removed the 'build: .' line.
# Now, Docker will pull the 'python:3.9-slim' image from Docker Hub.
image: python:3.6
# Set the working directory inside the container
working_dir: /app
ports:
# This now maps a DYNAMIC (random) host port
# to container port 4050.
- "4050"
volumes:
# Mount the current directory (containing app.py and requirements.txt)
# to /app in the container.
- .:/app
# This command runs when the container starts.
# 1. It installs the packages from requirements.txt.
# 2. It starts the Flask application (app.py).
command: sh -c "pip install -r requirements.txt && python app2.py"udwall Configuration to Allow Access:
If you want to open port 4050 to any user, add this rule to /etc/udwall/udwall.conf:
rules = [
# Remaining Default rules
# Allow access to Docker container on port 4050 from any IP
{'from': 'any', 'connectionType': 'tcp', 'to': 4050, 'isDockerServed': True, 'isEnabled': True},
# Allow access to a SPECIFIC Docker container IP (e.g. 172.17.0.2)
# You can find this IP using `sudo udwall --dip`
{'from': 'any', 'connectionType': 'tcp', 'to': 4050, 'isDockerServed': True, 'isEnabled': True, 'dip': '172.17.0.2'},
]This will back up your current state, remove undefined rules, and apply the new ones based on the configuration file.
sudo udwall --applyBackups are stored in /home/ubuntu/backup/firewall-backup/.
This sets up the iptables rules required to make Docker respect UFW.
sudo udwall --enableThis removes the iptables rules and custom chains, effectively disabling the Docker-UFW integration.
sudo udwall --disableUdwall has basic ssh port checks to ensure you don't accidentally disable your ssh port. If you want to bypass these checks, you can use the --force flag.
This is not recommended as it skips the ssh port checks (i.e., port 22/tcp).
This is only for enabling and applying rules without ssh port checks.
This will back up your current state, remove undefined rules, and apply the new ones based on the configuration file.
sudo udwall --apply -fThis enables the firewall without ssh port checks.
sudo udwall --enable -fIf you have multiple docker container running on same port and you want to allow access to specific container then you can use this feature.
By default udwall will allow access to all docker container running on same port.
For example:
let assume we have two docker container running on port 5000.
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
19d49f401a91 simple-flask-app:latest "python app.py" 3 minutes ago Up 2 minutes 0.0.0.0:5000->5000/tcp, [::]:5000->5000/tcp keen_wilson
ccd3436070b4 python:3.6 "sh -c 'pip install …" 6 minutes ago Up 6 minutes 0.0.0.0:5001->5000/tcp, [::]:5001->5000/tcp simple-python-flask-dockerized-application-web-1
If we use this config
rules=[
{'from': 'any', 'connectionType': 'tcp', 'to': 5000, 'isDockerServed': True, 'isEnabled': True},
]udwall will allow access to all docker container running on port 5000.
That means both http://<ip>:5000 andhttp://<ip>:5001 will be open to any ip.
To allow access to specific docker container you can use this feature.
By running this command you can find docker ip address
sudo udwall --dip
Output:
--- Finding Docker IPs ---
IP | External Port | Internal Port | Container Name
----------------------------------------------------------------------
172.17.0.2 | 5000 | 5000 | keen_wilson
172.17.0.2 | 5000 | 5000 | keen_wilson
172.18.0.2 | 5001 | 5000 | simple-python-flask-dockerized-application-web-1
172.18.0.2 | 5001 | 5000 | simple-python-flask-dockerized-application-web-1
rules=[{'from': 'any', 'connectionType': 'tcp', 'to': 5000, 'dip': '172.17.0.2', 'isDockerServed': True, 'isEnabled': True},]Only http://<ip>:5000 will be open to any ip.
http://<ip>:5001 will be closed to any ip.
| Command | Description |
|---|---|
sudo udwall --dry-run |
Preview: Shows exactly which ufw commands would be run, without making any changes. |
sudo udwall --create |
Import: Generates a udwall.conf file at /etc/udwall/udwall.conf based on your current active UFW rules. |
sudo udwall --backup |
Backup: Manually creates a timestamped backup of /etc/ufw and iptables rules in ~/.udwall/backups/. |
sudo udwall --status |
Check Status: Displays the current UFW status and active rules (numbered). |
sudo udwall --dip |
Docker IPs: List Docker container IPs and port mappings. |
sudo udwall --disable |
Uninstall: Removes the Docker-UFW integration, deletes custom chains, and disables UFW. |
sudo udwall --enable -f |
Initialize: Sets up the Docker-UFW integration and enables UFW. Run this first. Use -f to skip SSH check. |
sudo udwall --apply -f |
Apply Rules: Reads udwall.conf, backs up current state, and applies the new firewall rules. Use -f to skip safety checks. |
sudo udwall --version |
Version: Displays the installed version of udwall. |
sudo udwall --help |
Help: Shows the help message and available options. |
The core iptables logic to fix the Docker/UFW security flaw is based on the work by chaifeng/ufw-docker. udwall extends this by adding declarative state management.
LiveReview - I'm building a private AI code review tool that runs on your LLM key (OpenAI, Gemini, etc.) designed for Organizations. Check it out if that's your kind of thing.
LiveReview helps you get great feedback on your PR/MR in a few minutes.
Saves hours on every PR by giving fast, automated first-pass reviews. Helps both junior and senior engineers work faster.
If you're tired of waiting for your peers to review your code or are not confident they'll provide valid feedback, LiveReview is here for you.
If you find these tools helpful, please consider giving us a ⭐ star on GitHub! It helps us reach more developers who could benefit from these utilities.