Installing and Configuring nftables on Debian

Introduction

Firewalling in Linux is, by necessity, a complex topic. There’s just no easy way around the problem, the firewall has a lot of moving parts and requires a lot of configuration options. To complicate matters further the Linux ecosystem is undergoing something of a change. The older iptables system is being replaced with nftables and in Debian nftables is now the preferred choice. The Wikipedia page on Netfilter provides an excellent introduction to how the Linux kernel performs packet filtering. Once you’ve finished that page you should have a read of the nftables page.

Note that in future nftables will likely be replace with BPF / XDP, some information about which can be found here.

Installation of nftables

There is no need to install nftables on Debian 12 as it is already installed.

Controlling the nftables Service

Enable the Service

While nftables is installed by default it is not enabled by default. To enable it run the following command:

sudo systemctl enable nftables.service

Check it’s state with the command:

sudo systemctl list-units --type=service --all

Restart the Service

You’ll need to restart the service after you make changes to the rules.

sudo systemctl restart nftables.service

Why is nftables Reported as Exited?

$ systemctl status nftables.service

● nftables.service - nftables
     Loaded: loaded (/lib/systemd/system/nftables.service; enabled; preset: enabled)
     Active: active (exited) since Sun 2024-10-20 14:24:14 BST; 27min ago
       Docs: man:nft(8)
             http://wiki.nftables.org
    Process: 53641 ExecStart=/usr/sbin/nft -f /etc/nftables.conf (code=exited, status=0/SUCCESS)
   Main PID: 53641 (code=exited, status=0/SUCCESS)
        CPU: 11ms

The nftables service only has one job, to load the rules. Once it’s done that there’s nothing more for it to do so it exits.

Some Simple Commands

List all the rules in force:

sudo nft list ruleset

Configuring nftables

Typically you don’t modify configuration files directly in Debian but for nftables I can’t really see any other good option:

sudo nano /etc/nftables.conf

Before you get editing though you absolutely need to watch this video and have a read of the nftables wiki which seem to be the only halfway decent information available.

What Happens When You Screw Up the Configuration?

It seems that if you submit a syntactically bad configuration via /etc/nftables.conf the system just drops back to an non-configured state. Importantly, it doesn’t lock you out. Of course this is not going to be the case if you drop all packets on your input chain with no way in. Then it’s time to lay healing hands on the server.

A Basic Configuration

A fairly typical minimal starting point for a configuration would be something like the one shown below. This default drops any incoming packets, allows loopback, established connections, pings, and connections to SSH.

#!/usr/sbin/nft -f

flush ruleset

table inet filter {
        chain input {
                type filter hook input priority filter; policy drop;
                iifname lo accept;
                ct state established,related accept
                ct state invalid drop;
                icmp type echo-request counter limit rate 1/second accept;
                tcp dport ssh accept;
        }
        chain forward {
                type filter hook forward priority filter; policy drop;
        }
        chain output {
                type filter hook output priority filter; policy accept;
        }
}

As and example of a couple of the features of nftables there’s another example below with all the rules above, some new rules, and a couple of comments. Notice the use of variables. I’m just counting the packets from home but this variable could easily be used to lock down the SSH port so only connections from home were allowed. The google_dns variable is a set of IP addresses and I’ve configured it to count the number of packets in the output chain. Both of these rules are basically pointless but it shows some of the things you can do with nftables.

#!/usr/sbin/nft -f

flush ruleset

# Variables can be defined and referenced using $var_name
define home = 207.241.224.2
define google_dns = { 8.8.8.8, 8.8.4.4 }

# A table that has a family of inet filters both IPv4 and IPv6 packets
table inet filter {
        # The name of the chain is not important like it is in iptables the hook determines what it does
        chain input {
                # This line is required, it configures the chain. This is a filtering chain that is
                # hooked to the input steam. The fact this chain has a hook makes it a base chain.
                # This chain has a default policy of drop so be careful!
                type filter hook input priority filter; policy drop;

                #Allow loopback access. This uses a meta selector to match the loopback interface
                iifname lo accept;

                # Connection tracking, if the connection is already established or related to one that is allow it.
                ct state established,related accept

                # Connection tracking but something bad is going on
                ct state invalid drop;

                # Allow IPv4 echo requests but limit the frequency
                icmp type echo-request counter limit rate 1/second accept;

                # Accept incoming connections to the SSH port - this could be limited to certain IP addresses
                tcp dport ssh accept;

                ip saddr $home counter;
        }
        chain forward {
                # This server is not a router so it doesn't need to forward anything
                type filter hook forward priority filter; policy drop;
        }
        chain output {
                # Strictly this chain isn't needed but I like they symmetry
                type filter hook output priority filter; policy accept;

                ip daddr $google_dns counter
        }
}

Further Reading