This repo is a community fork of https://launchpad.net/ufw starting with
https://git.launchpad.net/ufw/commit/?id=f095c83f1ed as allowed by the
GPL-3.0 license that ufw is
licensed under. The fork was created by ufws primary author on 2025-11-03 and
all source files modified with updated copyright information (ie, assigned to
Canonical for 2024 and before).
What does it mean? It has come to mean 'Uncomplicated Firewall', but you can change it to something more suitable if you want. If you like it, you might pick 'Universal Firewall', or 'Ultimate Firewall'. If you are not a fan, perhaps 'Unbearable Firewall'. Have fun!
python3.8+iptables1.8+gettext/proc' filesystem support- Linux kernel configured with the following modules (not exhaustive):
- addrtype
- comment
- hl (IPv6)
- limit
- multiport
- recent
- conntrack[1]
- As of 0.34, the 'conntrack' modules is used instead of 'state'
ufw has been widely tested on Linux 2.6.24 and higher kernels. You may also
use the check-requirements script in the tests/ directory to see if your
system has all the required iptables/netfilter functionality.
ufw uses a Makefile for building and installing. For development workflows,
ufw also includes a minimal setup.py that allows editable installations via
pip install -r requirements.txt -e . (see Development Environment below).
make build- Buildufwin the staging directorymake install- Build and installufw(requires build)make clean- Remove build artifacts
PYTHON- Python interpreter to use (default:python3)DESTDIR- Root directory for installation (for packaging)PREFIX- Installation prefix (default:/usr)SYSCONFDIR- System configuration directory (default:/etc)LIBDIR- Library directory (default:/lib)DATADIR- Data directory (default:$(PREFIX)/share)
Users can install with:
$ sudo make install # system-wide installation
$ make install DESTDIR=/path/to/staging # for packaging (deb, rpm, etc)
$ make install DESTDIR= PREFIX=/custom/path ... # custom locationTo specify a different Python interpreter:
$ make install PYTHON=python3.13For packaging (e.g., Debian, RPM), use DESTDIR to install to a staging directory:
$ make install DESTDIR=/tmp/ufw-stagingFor custom installation paths, override the path variables:
$ make install \
DESTDIR= \
PREFIX=/opt/ufw/usr \
SYSCONFDIR=/opt/ufw/etc \
LIBDIR=/opt/ufw/libWhen installing ufw from source, you will also need to integrate it into your
boot process for the firewall to start when you restart your system. Depending
on your needs, this can be as simple as adding the following to a startup
script (eg rc.local for systems that use it):
$ sudo /lib/ufw/ufw-init startFor systems that use SysV initscripts, an example script is provided in
doc/initscript.example. See doc/upstart.example for an Upstart example and
doc/systemd.example as a systemd example. Consult your distribution's
documentation for the proper way to modify your boot process.
/usr/sbin/ufw: is the UI for people (have different backends)/etc/defaults/ufw: high level configuration/etc/ufw/before[6].rules: rules evaluated before UI added rules/etc/ufw/after[6].rules: rules evaluated after UI added rules/lib/ufw/user[6].rules: UI added rules (not to be modified)/etc/ufw/sysctl.conf: kernel network tunables/lib/ufw/ufw-init: start script
ufw enable|disable turn firewall on and off (including at boot)
ufw default allow|deny updates default policy
ufw logging on|off updates backend logging (*.rules)
ufw status displays firewall status (user.rules only)
ufw allow|deny|limit RULE add RULE to firewall
ufw route allow|deny|limit RULE add routing (FORWARD) RULE to firewall
See man ufw and also Ubuntu's tutorial at:
http://doc.ubuntu.com/ubuntu/serverguide/C/firewall.html
ufw uses several chains to allow ease of use and flexibility. Control flow
through the various chains is (essentially) as follows:
INPUT ->
ufw-before-logging-input ->
ufw-before-input ->
ufw-user-input ->
ufw-user-logging-input (rule specific) ->
ufw-after-input ->
ufw-after-logging-input ->
ufw-reject-input -> return to INPUT
OUTPUT ->
ufw-before-logging-output ->
ufw-before-output ->
ufw-user-output ->
ufw-user-logging-output (rule specific) ->
ufw-after-output ->
ufw-after-logging-output ->
ufw-reject-output -> return to OUTPUT
FORWARD ->
ufw-before-logging-forward ->
ufw-before-forward ->
ufw-user-forward ->
ufw-user-logging-forward (not used) ->
ufw-after-forward ->
ufw-after-logging-forward ->
ufw-reject-forward -> return to FORWARD
The before chains are setup in before.rules, the after chains in
after.rules and the user chains are maintained by ufw. If an administrator
wants to add rules manually, the rules should be added to before.rules and
after.rules. The reject chains are used for when the default policy is
set to REJECT (because iptables does not support REJECT as a target at this
time). Keep in mind, when using REJECT as default policy, ufw may end up
rejecting rules that are added outside of ufw and after ufw is started.
There is some default configuration in both before.rules and after.rules,
and this configuration is not displayed with ufw status (but can always
be viewed with iptables -L -n or iptables -L [chain] -n. See the iptables
man page for details. There are also 3 chains (for both IPv4 and IPv6) that
can be used to immediately go to POLICY, which are mostly useful to avoid
logging (these chains are used in the default ufw after*.rules configuration to
avoid logging noisy services by default):
ufw-skip-to-policy-input
ufw-skip-to-policy-output
ufw-skip-to-policy-forward
The primary chains are ufw-before-*, ufw-after-* and ufw-reject-*. The
treatment of iptables' built-in chains can be controlled with the
MANAGE_BUILTINS configuration option (in /etc/default/ufw). By default this is
set to no, which means that other than adding the primary chains, the
built-in chains will remain untouched. This also means that these primary
chains will stay in the table, even after disabling ufw. This is to make sure
that the primary chains don't move around other non-ufw rules and chains. To
completely flush the built-in chains with this configuration, you can use:
$ sudo /lib/ufw/ufw-init flush-allAlternately, ufw may also take full control of the firewall by setting
MANAGE_BUILTINS=yes in /etc/defaults/ufw. This will flush all the built-in
rules and delete the non-built-in rules on start, stop and reload.
In addition to the above, the following tracking chains are setup last to support stateful tracking of NEW connections when the default policy is set to ACCEPT:
ufw-track-input
ufw-track-output
ufw-track-forward
When default policy is set to ACCEPT, an -m conntrack --ctstate NEW -j ACCEPT
rule is added to the appropriate chain for tcp and udp connections, otherwise
the chain is empty and the default policy for the builtin chain.
ufw can be thought of two parts, the ufw command-line program and the ufw
framework. The ufw command is intentionally kept as simple as possible, so
users can do common tasks more easily. The framework (ie the bootscripts, setup
of the chains (see above), sysctl configuration, etc) is very flexible, and
since ufw is simply a frontend for iptables, anything that can be done with
iptables can be done within the ufw framework.
As an example, to perform port redirection, users can add to the top of
/etc/ufw/before.rules, before the *filter section:
*nat
:PREROUTING ACCEPT [0:0]
# redirect all incoming requests to tcp port 80 to tcp port 22
-A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 22
COMMIT
then run:
$ sudo ufw disable
$ sudo ufw enable
$ sudo ufw allow 80/tcp #required only if ufw blocks requests to this portTo add NAT masquerading to the above, change the nat table that was just added to something like:
*nat
:PREROUTING ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
-A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 22
# Forward traffic from eth1 through eth0.
-A POSTROUTING -s 192.168.0.0/24 -o eth0 -j MASQUERADE
COMMIT
then adjust /etc/default/ufw to have:
DEFAULT_FORWARD_POLICY="ACCEPT"
and /etc/ufw/sysctl.conf to have:
net.ipv4.ip_forward=1
then run:
$ sudo ufw disable
$ sudo ufw enableIt's important to remember that ufw will only flush the chains and tables it
manages, so if if you need to flush the nat table to restart anew, please do:
$ sudo iptables -F -t natSimilarly, to see what rules are in the nat table's chains, use:
$ sudo iptables -L -n -t natSee man iptables for details.
Enabling ufw creates a ruleset that is intended to protect the host while
allowing some common traffic such as DHCP, ping and mDNS. These defaults are
setup in the before*.rules and after*.rules files (see man iptables for
terminology):
- Default DROP on INPUT
- Default DROP on FORWARD
- Default ACCEPT on OUTPUT
- ACCEPT all on lo
- DROP packets with RH0 headers
- ACCEPT all RELATED and ESTABLISHED on INPUT and OUTPUT
- ACCEPT all RELATED and ESTABLISHED on FORWARD (ip forwarding must be enabled via sysctl for this to be in effect)
- DROP INVALID packets (packets not associated with a known connection)
- ACCEPT certain icmp packets (INPUT and FORWARD):
- destination-unreachable, source-quench, time-exceeded, parameter-problem, and echo-request for IPv4
- destination-unreachable, packet-too-big, time-exceeded, parameter-problem, and echo-request
- ACCEPT certain icmpv6 packets for stateless autoconfiguration (INPUT): neighbor-solicitation, neighbor-advertisement, router-solicitation
- ACCEPT mDNS (zeroconf/bonjour/avahi 224.0.0.251 for IPv4 and ff02::fb for IPv6) for service discovery (INPUT)
- ACCEPT UPnP (239.255.255.250 for IPv4 and ff02::f for IPv6) for service discovery (INPUT)
- ACCEPT ping replies from IPv6 link-local (ffe8::/10) addresses (INPUT)
- ACCEPT DHCP client traffic (INPUT)
- Log all blocked packets not matching the default policy with rate limiting
If you are using a packaged version of ufw supplied by your distribution, the
default ruleset may be different.
On /lib/ufw/ufw-init start and ufw enable the chains are flushed, so
ssh may drop. This is needed so ufw is in a consistent state. Once the ufw is
enabled it will insert rules into the existing chains, and therefore not
flush the chains (but will when modifying a rule or changing the default
policy).
You can insert rules before enabling the firewall however, so it is often a good idea to to:
$ sudo ufw allow proto tcp from any to any port 22
$ sudo ufw enableIn this case, the chains are still flushed, but the ssh port will be open after enabling the firewall.
ufw has full support for IPv6, and it is enabled by default. To disable, modify
/etc/default/ufw (or wherever this is installed) to have:
IPV6=no
Then do:
$ sudo ufw disable
$ sudo ufw enableufw has support for application integration. This allows for administrators
and developers to put profiles in /etc/ufw/applications.d and have users use
these profiles in their rules. Profiles use the .INI syntax, and examples
can be found in the examples/ directory. See man ufw for details.
If upgrading from 0.17 or below to 0.18, new chains to support the limit
command will be added automatically.
While it certainly ok to use /lib/ufw/ufw-init as the initscript for
ufw, this script is meant to be used by ufw itself, and therefore not
particularly user friendly. See doc/initscript.example for a simple
implementation that can be adapted to your distribution.
Simple rules for rsyslog support can be found in doc/rsyslog.example.
Please report bugs at: https://github.com/jdstrand/ufw/issues/new
Sometimes it is useful to provide the ufw configuration and attach it to the
bug. Eg:
$ mkdir /tmp/ufw
$ sudo ufw show raw > /tmp/ufw/raw
$ sudo tar -zcvf /tmp/NNN.tar.gz /tmp/ufw /etc/default/ufw /etc/ufw /lib/ufwthen attach to this bug /tmp/NNN.tar.gz (where NNN corresponds to the bug
number.
To run the test suite:
$ ./run_tests.sh -sYou may also specify an interpreter for the tests:
$ ./run_tests.sh -s -i /usr/local/bin/python3.13For the root tests (these are iptables version dependent, will modify your
existing firewall and insert kernel modules, so they require root privileges
and aren't run by default):
$ sudo ./run_tests.sh -s rootFinally, ufw's behavior may differ based on available kernel features. The
root_kern tests assume all kernel features supported by check-requirements
are enabled. They behave just like the root tests.
To test ufw in a custom location:
$ UFW_SKIP_CHECKS=1 make install DESTDIR= PREFIX=/tmp/ufw/usr SYSCONFDIR=/tmp/ufw/etc LIBDIR=/tmp/ufw/lib(ufw does a number of checks since it is normally run as root. When testing,
this is usually not the case, so you can use UFW_SKIP_CHECKS=1 to disable
these checks.)
Then test with:
$ PYTHONPATH=/tmp/ufw/usr/lib/python3/dist-packages /tmp/ufw/usr/sbin/ufw help
$ sudo sh -c "PYTHONPATH=/tmp/ufw/usr/lib/python3/dist-packages /tmp/ufw/usr/sbin/ufw ..."ufw unit tests are in tests/unit and are run as part of ./run_tests.sh.
To run individual unit tests directly:
$ python3 -m unittest tests.unit.<filename>.<class>.<test>For example:
$ python3 -m unittest tests.unit.test_backend_iptables.BackendIptablesTestCase.test_get_statusFor development, you can set up a virtual environment with editable install:
$ python3 -m venv .venv
$ source .venv/bin/activate
$ pip install -r ./requirements.txt -e .This creates an editable installation where changes to the source code are
immediately reflected without reinstalling. The test suite (./run_tests.sh)
automatically uses the venv Python if VIRTUAL_ENV is set.
Within this environment, you can run various linters and coverage. Eg:
$ python3 -m coverage run ./tests/unit/runner.py
...
$ python3 -m coverage report --show-missing --omit="tests/*"