Self-hosted service
chaifeng/ufw-docker avatar
chaifeng/ufw-docker

chaifeng/ufw-docker: fixing the Docker and UFW port exposure without disabling iptables

To fix the Docker and UFW security flaw without disabling iptables

6,809 stars498 forksShellGPL-3.0

At a glance

What is it?
A shell script and a UFW after.rules fragment that keep Docker's iptables management intact while stopping published container ports from being reachable on the public interface. It is for Ubuntu and Debian hosts where UFW is the firewall and Docker is not allowed to bypass it.
Who is it for?
Adopt it if you run Docker on an Ubuntu or Debian host where UFW is the intended firewall and you publish ports with -p on all interfaces. Skip it if you already set --iptables=false or publish only to a fixed private IP, because the README asks you to roll those changes back first.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 140 days ago.
What is it written in?
Mainly Shell, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The Docker and UFW flaw that chaifeng/ufw-docker targets

UFW is an iptables front end on Ubuntu. When Docker is installed, the README states that Docker bypasses the UFW rules and published ports can be reached from outside. The worked example is concrete: `docker run -d --name httpd -p 0.0.0.0:8080:80 httpd:alpine` publishes container port 80 on host port 8080, and UFW will not block external requests to 8080. Even `ufw deny 8080` does not prevent access, according to the README. The practical consequence is that a service you meant to keep internal is listening on the public interface.

The project is for administrators of Ubuntu and Debian servers who use UFW as the only firewall and do not want to give up Docker's network management. The README is explicit that the common fix found online, disabling Docker's iptables feature, means giving up Docker's network management and prevents containers from reaching the external network. The alternative it rejects is hand-written POSTROUTING rules such as `-A POSTROUTING ! -o docker0 -s 172.17.0.0/16 -j MASQUERADE`, which only cover one subnet and must be repeated for every new Docker network.

How the DOCKER-USER chain and ufw-user-forward make published ports private

Docker creates a DOCKER-USER chain that is evaluated before its own forwarding rules. The project inserts a jump from DOCKER-USER to ufw-user-forward, so UFW's forwarding policy decides what happens to traffic heading for a container. The rules then return early for traffic from the private ranges 10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16, accept traffic between docker0 and docker0, and drop new connections destined for those same private ranges after logging them with the prefix `[UFW DOCKER BLOCK]`, rate limited to 3 per minute with a burst of 10. Everything else falls through to the final RETURN.

The effect is that a container port published on all addresses is no longer reachable from the public network, while containers and internal networks still talk to each other and containers keep outbound access. Nothing in Docker's configuration is changed. The README frames this as the point: no manual iptables maintenance for new Docker networks, and no side effects from turning iptables off. The cost is that the filter is address based. A host that has several private addresses, or whose private addresses change, is covered by the three RFC1918 ranges rather than by a single interface, which is why the rule set is written the way it is.

Installing ufw-docker with the script and a first allow rule

The repository ships a shell script named `ufw-docker` plus a Dockerfile that installs it as `/usr/bin/ufw-docker` with `docker-entrypoint.sh` as the entry point. The README's manual path is to edit `/etc/ufw/after.rules` and append the block between the `# BEGIN UFW AND DOCKER` and `# END UFW AND DOCKER` markers. The script exists to do that edit for you.

Start by confirming UFW is enabled and Docker is running, then run the install subcommand. The README does not print a sample transcript, so the exact output is not documented; the script rewrites the UFW configuration file.

bash
sudo ufw-docker install
sudo systemctl restart ufw

The README warns that for unknown reasons the UFW rules may not take effect after restarting UFW and that a server reboot may be required. Do that before you conclude anything about the result. After the reboot, publish a container port on all interfaces and check it from an external host; it should be unreachable.

To open one container port to the public network, the README gives the route rule. The port in the rule is the container port, not the host port.

bash
ufw route allow proto tcp from any to any port 80
ufw delete allow 80

The first command allows the public network to reach any published port whose container port is 80. The second is the README's example of removing an allow rule. The README notes that if you publish with `-p 8080:8...` the mapping matters, so check which side of the colon you are matching before you assume a rule covers the service you meant.

Where chaifeng/ufw-docker is the wrong tool

The README is honest about the precondition: if you previously fixed this by disabling Docker's iptables, you must roll that back first. That means removing `--iptables=false` and related settings from `/etc/docker/daemon.json`, returning UFW's default FORWARD rule to DROP instead of ACCEPT, and deleting Docker-related rules from `/etc/ufw/after.rules`. If you cannot or will not undo those changes, this project is not for you; it depends on Docker owning its own iptables rules.

The second limitation is scope. The rule set distinguishes public from private by source address using the three RFC1918 blocks. A host whose clients arrive from a public address range that you consider trusted gets no exemption from the drop, and a host with a non-standard internal addressing plan is not covered by those three ranges. The README also does not document rollback of the `install` subcommand, so plan for that: keep a copy of `/etc/ufw/after.rules` before you run it. Finally, the log line is rate limited to 3 per minute with a burst of 10, so a busy scan will not produce one log entry per dropped packet, and you should not read the absence of log lines as the absence of traffic.

How it differs from the iptables=false approach and from firewalld

The widely copied workaround is to start Docker with `--iptables=false` and then write the NAT and forwarding rules yourself. That removes Docker from the packet path entirely, which is why containers lose outbound access unless you add a MASQUERADE rule per network. chaifeng/ufw-docker takes the opposite position: Docker keeps managing its networks, and UFW is inserted into the DOCKER-USER chain that Docker already provides for exactly this purpose. The difference in day-to-day work is that a new Docker network needs no new firewall rule, while the iptables=false approach needs one every time.

Against firewalld, the split is about which front end owns the rules. firewalld has its own zones and its own Docker integration story, and it is the default on Fedora and RHEL-family systems. This project assumes UFW, which is the default on Ubuntu and Debian, and its documentation and test scripts are built around that. If your fleet is not UFW-based, the after.rules fragment is not transferable, and you should look at the firewalld zone approach instead.

Licence, maintenance and upgrade cost

The repository is licensed GPL-3.0. The script is installed onto your system and modifies `/etc/ufw/after.rules`, so if you redistribute a modified version you are inside the terms of a copyleft licence; if you only run it on your own servers, the obligation is lighter. This is not legal advice, and the LICENSE file in the repository is the authoritative text.

On maintenance, the last push was on 2026-05-12 and the most recent release is 251123 from 2025-11-23, so the project is not abandoned. The upgrade cost is low but not zero: the script rewrites a file that other tools and administrators also edit, and the README's rollback guidance covers undoing the old workaround rather than undoing this project. The repository includes `print-iptables.sh`, `trace-iptables.sh` and their ip6tables counterparts, plus a `test/` directory and a `Vagrantfile`, so you can inspect the resulting rules on a throwaway VM before touching a production host.

Editorial conclusion

Adopt it if you run Docker on an Ubuntu or Debian host where UFW is the intended firewall and you publish ports with -p on all interfaces. Skip it if you already set --iptables=false or publish only to a fixed private IP, because the README asks you to roll those changes back first. Before trusting it in production, run the install, reboot the server, then confirm from an external host that a published port is unreachable and that ufw route allow opens exactly one container port.

Frequently asked questions

How do I install ufw-docker on Ubuntu?

The repository provides a `ufw-docker` script that is installed as `/usr/bin/ufw-docker`; the README's manual alternative is to append the block between the `# BEGIN UFW AND DOCKER` and `# END UFW AND DOCKER` markers to `/etc/ufw/after.rules`. After the change, restart UFW with `sudo systemctl restart ufw` or `sudo ufw reload`, and the README notes a server reboot may be needed if the rules do not take effect.

How do I use ufw-docker to allow a container port?

Run `ufw route allow proto tcp from any to any port 80`, where the port is the container port rather than the published host port. The README states this allows the public network to reach all published ports whose container port is 80, and that `ufw delete allow 80` removes an allow rule.

What is ufw-docker?

It is a fix for the case where Docker bypasses UFW rules and published ports can be reached from outside, even when `ufw deny` is set for that port. The approach is to add rules in `/etc/ufw/after.rules` that route Docker's DOCKER-USER chain through UFW's forwarding chain, without disabling Docker's iptables feature.

What is the alternative to ufw-docker?

The README describes the common alternative it is reacting to: disabling Docker's iptables function and adding rules such as `-A POSTROUTING ! -o docker0 -s 172.17.0.0/16 -j MASQUERADE` manually. It notes that this gives up Docker's network management, prevents containers from reaching the external network, and requires a new rule for every new Docker network.

Official sources

  1. chaifeng/ufw-docker on GitHub
  2. Issues
  3. License: GPL-3.0
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/chaifeng-ufw-docker.svg)](https://hysenlabs.com/projects/chaifeng-ufw-docker)