Skip to content

jdstrand/ufw

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1,723 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

UFW

Project History

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's in a name?

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!

Requirements

  • python 3.8+
  • iptables 1.8+
  • gettext
  • /proc' filesystem support
  • Linux kernel configured with the following modules (not exhaustive):
    • addrtype
    • comment
    • hl (IPv6)
    • limit
    • multiport
    • recent
    • conntrack[1]
  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.

Build System

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).

Makefile Targets

  • make build - Build ufw in the staging directory
  • make install - Build and install ufw (requires build)
  • make clean - Remove build artifacts

Makefile Variables

  • 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)

Install

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 location

To specify a different Python interpreter:

$ make install PYTHON=python3.13

For packaging (e.g., Debian, RPM), use DESTDIR to install to a staging directory:

$ make install DESTDIR=/tmp/ufw-staging

For custom installation paths, override the path variables:

$ make install \
    DESTDIR= \
    PREFIX=/opt/ufw/usr \
    SYSCONFDIR=/opt/ufw/etc \
    LIBDIR=/opt/ufw/lib

When 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 start

For 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.

Basic Layout

  • /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

Usage

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

Chains

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-all

Alternately, 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.

Advanced Configuration

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 port

To 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 enable

It'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 nat

Similarly, to see what rules are in the nat table's chains, use:

$ sudo iptables -L -n -t nat

See man iptables for details.

Default ruleset

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.

Remote Management

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 enable

In this case, the chains are still flushed, but the ssh port will be open after enabling the firewall.

IPV6

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 enable

Application Integration

ufw 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.

Upgrading

If upgrading from 0.17 or below to 0.18, new chains to support the limit command will be added automatically.

Distributions

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.

Reporting bugs

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/ufw

then attach to this bug /tmp/NNN.tar.gz (where NNN corresponds to the bug number.

Testing

To run the test suite:

$ ./run_tests.sh -s

You may also specify an interpreter for the tests:

$ ./run_tests.sh -s -i /usr/local/bin/python3.13

For 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 root

Finally, 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.

Manual Testing

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 ..."

Unit Tests

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_status

Development Environment

For 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/*"

About

ufw is the Uncomplicated Firewall, an application for managing a Linux firewall

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors