Nftables, A Simple Introduciton

Published on April 29, 2023 at 7:45 pm by LEW

What Is Nftables

The nftables package is a part of the latest version of netfilter, a Linux kernel framework for packet filtering, network address translation, and port translation. It is the successor to the venerable iptables for provides the functionality to control packet movement through our network.

The updated nftables addresses some long standing issues with the overall structure and implementation of its predecessor while utilizing the existing netfilter infrastructure, hooks, connection tracking, user-space queening, and logging subsystems. Nftables also comes with its own user space configuration utility, called nft.

In this post we will be looking at setting up rules for nftables, as they are somewhat different than how iptables did it. We will focus on building a simple firewall for a personal computer. Once the methodology is understood, one can advance to more complex examples, like routers.

Nftables Basics

Nftables works by examining various bits of information within the headers of packets moving through the network stack, and then making routing decision based on a rule set we create. The actual information payload contained within the packet is not a concern, only the header information. Hence the term packet filter.

The rule set used by nftables is hierarchical and setup in tables. Each table contains a number of chains. Actual instructions or rules are contained within the chains.

At what point nftables examines the packet headers is controlled by hooks. Hooks are various places in the network architecture where a packet can be pulled, examined, and decisions about what to do with them are made.

In general I will try to avoid comparisons between nftables and iptables, as this can be confusing to new users. However if you have previously used iptables, then beyond the changed instruction set their are other significant differences between the two. For example, when iptables matched a rule it was done. In nftables things are not so simple, and all rules will be checked regardless, with a decision being based on the priority of each matching rule entry.

By default, the nftables rule file is found at /etc/nftables.conf. No default rule set is created by nftables. If there is a default rule set, it was provided by the specific Linux Distribution being used.

Proceed with Caution

Note that I am assuming you have root access to the computer in question via sudo, su or login. In general I will not be specifying if you need to be root or not. If you are not root, and a command does not work, then try it with root privileges.

I am also assuming the use of systemd, as it is standard on most major Linux distributions. If not, then you will, in some cases, need to determine applicable command variants for your system.

If you are accessing the computer remotely, DO NOT turn on nftables until you have checked, modified, and verified any existing rule sets. Otherwise you could lock yourself out of current and future remote computer session.

In this post I will be using a Debian 11 installation. So while trying to be general in nature, the information will be based on how the Debian does things. Different distributions may do some things differently.

First Things First

Is nftables or iptables installed? You can check this with your package manager. For Debian based systems we can use the following commands.

apt list –installed | grep nftables
apt list –installed | grep iptables

Is iptables or nftables running? Use the following commands to check, and/or stop the service.

systemctl status iptables
systemctl status nftables

systemctl stop iptables
systemctl stop nftables

While it is possible to run iptables and nftables side by side, I do not recommend doing this, as it can add needless complications. If iptables is present, then I suggest stopping the service and removing it.

systemctl disable iptables
systemctl stop iptables
apt purge iptables

Note as general good maintenance practices, you might also want to run the apt autoremove command also, to remove unused dependencies and help reduce system clutter.

If nftables is not present you will need to install it.

apt install nftables

Configure a Firewall

Note that you can install iptables-persistent and netfilter-persistent for saving changes you make to your nftables rules on the fly. Or you can write your rules to the default configuration file. My suggestion is to use iptables-persistent and netfilter-persistent only when developing a firewall. Once you have a rule set that will remain somewhat constant, you can write it to the configuration file, and save a bit of additional complexity by uninstalling iptables-persistent and netfilter-persistent.

On Debian based systems you will find a default nftables configuration file at /etc/nftables.conf. Your distribution may create this file for you. I suggest moving it if it exists, and creating a new file for this example.

cp /etc/nftables.conf /etc/nftables.conf.bak
touch nftables.conf
chmod a+x nftables.conf

Now that we have created a blank executable file, we will want to open the file in a text editor. The first thing we need to add is the shebang. For those familiar with bash scripting, we want to run nftables, not a shell. And we want it to use the file we are in. So our shebang looks like the following.

#!/usr/sbin/nft -f

The next thing we want to do is make sure we flush any existing rules. You can argue that this is probably not strictly necessary for an established firewalls with constant rules. But it it is a good habit to get in the practice of clearing the rules when we start.

flush ruleset

The next thing we need to add are our table and chains. It is important to note that nftables does not have any tables or chains created automatically, unlike its predecessor. So we add some lines to create a table, and three chains within the table. I am using standard naming conventions here for clarity. The number of tables, chains within tables, and what they are called is up to you. Just make sure you can tell what they do if you need to go back into the file after a year or so.

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

A table can only belong to one family (ip, ip6, inet, arp, or bridge). The inet family is a combination of ip and ip6, and part of the network layer. The arp and bridge family are part of the data link layer. In our example the table belongs to the ip family, so rules will only apply to IPv4. In our example the table token is immediately followed up by its family (ip), then its name (filter). Curly brackets will enclose all the chains within the table.

Each chain within the table has its own set of curly brackets to hold its rules. Generally you want to name chains by their function (input, forward, and output in our case). Inside a chains curly brackets you must define the chain type, hook, and priority.

Chains are one of three types; filter, route, or nat. Since we are not building a router, we have no need of route or nat, so all our chains type is filters.

Chains must have a hook where they pull packets from. There are five standard hooks; prerout, input, forward, output, and postrout. In our case we are using input, forward, and output as hooks. One could also argue since we are not building a router, we do not need the forward chain. There are a few special cases where a forward chain can be useful, so it is better to have it than not have it. There is a nice diagram here: https://people.netfilter.org/pablo/nf-hooks.png

Finally each chain needs a priority. The priority can affect how nftables reacts to multiple rules for the same packet. Unless you have special needs, I suggest setting the priority for all chains to 0.

Note if you where adding chains on the fly with the nft command, you would also need to include the table name that the chain belongs too.

Adding Some Rules

Default Policy: On the chain definition line, we can also add a default policy action, separated by semicolons. The same action can be spelled out as a separate rule, but for me it is easier to follow in the chain definition. In our example, we will add the default policy of drop to the input and forward chains. This will cause them to drop all packets that do not match a rule within the chain.

type filter hook input priority 0; policy drop;
type filter hook filter priority 0; policy drop;

Local Loop-Back: We probably want to allow local loopback, as some programs require it. To do this we will use the iifname (interface name) meta expression. This will do a string comparison between the packet header and a name we use (lo for local in this case), then decide to accept or reject the packet.

iifname lo accept

We add this to the input chain, so that local packets will be accepted (remember we set default behavior of input chain to drop/reject).

Open Ports: There are several standard ports we want to be able to accept traffic on. Which ones will depend on the usage of the computer on your local network. The nftables program gives us a couple of ways to do this. We can also do it in a single statement. Lets assume we want to allow http, https, and ssh under the tcp protocol.

tcp dport {22, 80, 443} accept

or

tcp dport {ssh, http, https} accept

This can be read as the tcp protocol, destination port(s), a list of ports in curly brackets, followed by accept. This again goes into our input chain. Note that we can use either port numbers or service names (though service names will only refer back to standard ports normally).

Established related traffic: Finally, for this example, we need to add a rule for established and related traffic. That is packets that are related to traffic established on the computer in question. For example a request for software updates. We will use the ct (connection tracking) command for this.

ct state established, related accept

We can read this as connection tracking for state of established related traffic, accept. Hence we send traffic out, netfilters can track it and let a response come though.

Conclusion

In this post we went over a simple example of using nftables. Below is our full file.

#!/usr/sbin/nft -f

flush ruleset

table inet filter {
    chain input {
	type filter hook input priority 0; policy drop;
	iifname lo accept
    	tcp dport { 22, 80, 443} accept
    	ct state established, related accept
    }
    chain forward {
        type filter hook forward priority 0; policy drop;
    }
    chain output {
        type filter hook output priority 0;
    }
}

This was very basic, and I am hoping it was easy to follow. If there is enough interest I will look at posting some more advanced senerios.

Add New Comment

Your email address will not be published. Required fields are marked *