Get Rewarded! We will reward you with up to €50 credit on your account for every tutorial that you write and we publish!

Easily managing your Firewall using nftables

profile picture
Author
Tuxifan
Published
2024-01-29
Time to read
6 minutes reading time

About the author- A hobbyist C++ programmer who loves to make a mess with pointers...

Introduction

Many of you have probably used Iptables before and probably found it quite cumbersome:

  • Hard to read rules
  • No really streamlined way to persist rules across reboots
  • Generally a mess to script with, using shell scripts

Well, turns out there is a great successor to Iptables! It's pretty simple to use and surprisingly few people know about it: nftables, short "nft" (that abbreviation really hasn't aged well...)!

In this tutorial we'll be setting up a simple firewall allowing all ports outgoing as well as HTTP, HTTPS, SSH and DNS incoming.

Prerequisites

  • All you need is any Linux distro that provides nftables. That is pretty much any Linux distro that has been updated within the last 10 years.

    On Debian and Ubuntu the package is called nftables:

    sudo apt install nftables
  • You should also make sure no frontends like UFW are currently in use. For UFW, run this command to make sure:

    sudo ufw disable

You can use nft --version and sudo ufw status to check their status.


Example terminology

  • Main network interface: enp5s0

Step 1 - Opening the configuration file

Most distros have either /etc/nftables.conf or /etc/sysconfig/nftables.conf. Whichever one exists on your system, open it in your favorite editor:

sudo editor /etc/nftables.conf

Step 2 - Deleting the skeleton

The file may either be empty or look something like this:

#!/usr/sbin/nft -f

flush ruleset

table inet filter {
        chain input {
                type filter hook input priority filter;
        }
        chain forward {
                type filter hook forward priority filter;
        }
        chain output {
                type filter hook output priority filter;
        }
}

If this is the case, clear the file before continuing.

Otherwise STOP: You are going to have to make sure to carry over any existing rules. One way could be to not clear the file but to append instead (but that could potentially be problematic). Handling this case is out of scope of this tutorial.

Important note: Do not delete any important existing rules (for virtualization, for example).

Step 3 - Writing the firewall configuration

Here's a template. You can go ahead and paste it into the config file:

Replace the example interface and custom settings as explained in "Step 3.1".

#!/usr/sbin/nft -f

# Variables
define main_interface = "enp5s0"

# Delete previous table
table ip my_filter
delete table ip my_filter

# Create new table
table ip my_filter {
    # Filter ingoing traffic
    chain input {
        type filter hook input priority 0;

        iifname $main_interface tcp dport {22, 80, 443} accept
        iifname $main_interface udp dport 53 accept
        iifname $main_interface ip protocol icmp accept # Accept all ICMP traffic
        iifname $main_interface ct state established,related accept # Accept any input traffic originated from us
        iifname $main_interface ct state invalid drop # Drop invalid packets...
        iifname $main_interface icmpv6 type {echo-request,nd-neighbor-solicit} accept # Accept IPv6 neighbour discovery
        iifname $main_interface drop # Drop everything else
    }

    # Drop all packages to be forwarded (we're not a gateway!)
    chain forward {
        type filter hook forward priority 0; policy drop;
    }

    # Allow all outgoing traffic
    chain output {
        type filter hook output priority 0; policy accept;
    }
}

Step 3.1 - Customize the configuration file

Make sure to change enp5s0 to your public-facing network interface. If you have multiple, you can specify it like so:

define main_interface = {"enp5s0", "enp7s0"}

Now just add whatever ports you'd like to be reachable to those lines:

iifname $main_interface tcp dport {22, 80, 443} accept
iifname $main_interface udp dport 53 accept

Note that you must only use {} when specifying multiple ports. Alternatively, if you don't need any UDP ports accessible you can just omit the whole line.

You can also specify port ranges like this:

iifname $main_interface udp dport {53, 1000-1999} accept

Tip: Chains with lower priority are processed first.

Step 3.2 - Allow forwarding to bridges (Optional)

In some cases, you'd have bridge networking set up for virtual machines. In this case, you'd have to allow forwarding to those bridges.

To do this, simply add the following rule to the end of the forward chain, replacing br0 with your bridge interface for each:

iifname $main_interface oifname "br0" accept

And remove the comment above the chain.

Step 4 - (Re)load the firewall rules

Now that we are ready to apply our new rules, just mark the configuration file as executable (only needed once):

sudo chmod a+x /etc/nftables.conf

And simply execute it like:

sudo /etc/nftables.conf

You can repeat this step any time in the future you'd like to change the rules. They are also going to be applied automatically when rebooting.

Step 5 - Troubleshooting

  • I can't connect anymore!

    That sucks, but it's fixable! If your server is a cloud server, you can log in via the cloud web interface and run sudo nft delete table ip my_filter to disable the firewall temporarily.

    If your server is a dedicated server, you could either order a KVM (if you don't want to reboot the server) and run the command above, or you could reboot into the rescue system via the Hetzner Robot web interface and manually delete the firewall configuration.

    You've probably just forgotten to allow incoming connections on your SSH port!

  • The firewall doesn't work

    If the configuration applies without an error but has no effect, please make sure you've changed main_interface to the public-facing network interface:

    define main_interface = "enp5s0"

Conclusion

Now you have a nice and modern Firewall set up using nftables! There is also a nftables wiki available that may be worth a read.

License: MIT
Want to contribute?

Get Rewarded: Get up to €50 in credit! Be a part of the community and contribute. Do it for the money. Do it for the bragging rights. And do it to teach others!

Report Issue
Try Hetzner Cloud

Get 20€ free credit!

Valid until: 31 December 2024 Valid for: 3 months and only for new customers
Get started
Want to contribute?

Get Rewarded: Get up to €50 credit on your account for every tutorial you write and we publish!

Find out more