Beginners guide to traffic filtering with nftables

Many Linux administrators became familiar with iptables and ip6tables. Less familiar are tools like arptables and ebtables. Meet the successor of them all: nftables, a packet filtering framework, with the goal to replace all the previous ones. After reading this guide you will be able to configure your own firewall configuration. Step by step we will show how nftables work. Although no knowledge of iptables is needed, we will share some differences with iptables where applicable. This way we can avoid inefficient rules.

Introduction: Netfilter

Before we start with this guide info nftables, it is good to know about netfilter. Both iptables and nftables use the netfilter components in the Linux kernel. This explains also the first two letters from this new traffic filtering solution.

One of the flaws in iptables is the slightly cryptic way of expressing which information flows are allowed. For that reason, the nftables syntax is shorter and easier to understand. Instead of saying “-p tcp”, it simplifies it to just “tcp”. This syntax is also very similar to what tcpdump is using. This is no surprise, as the project was inspired by the elegant syntax of tcpdump. If you know pf from operating systems FreeBSD or OpenBSD, you may also like the new syntax.

Suggested read: differences between iptables and nftables explained

Preparations

For this beginners guide, we assume that you have a recent Linux kernel. Support for nftables should also be compiled into the kernel, together with the related nftables modules. To be sure, check if you have the nf_tables kernel module available. and if the nft binary is installed.

Kernel Modules

To determine if you have the nf_tables kernel module, use the modinfo command.

modinfo nf_tables

The output will look something like this:

Use lsmod to show any active nftables kernel module.

lsmod | grep nf_tables

This should at least reveal the nf_tables modules: nf_tables_inet, nf_tables_ip, nf_tables_ip6. On newer versions, this might be nf_tables_ipv4 and nf_tables_ipv6.

Disable iptables

It is possible to mix iptables and nftables. However, this increases complexity and also the chance to introduce errors. So keep it simple and flush out all iptables rules, and make sure it is not loaded.

iptables -F

Do the same for IPv6:

ip6tables -F

Ensure that during system reboots the iptables configuration or modules are no longer loaded.

Kernel and client

We already have seen the active kernel modules in the sections before. Here is a big difference with iptables. The intelligence of the rule sets has been moved to the client utility nft. This utility parses the rules and compiles them into a language that the kernel module understands. This way the kernel receives an optimized set of instructions. Another benefit is that you can manipulate the rules, instead of having to flush the configuration with every new change.

Configuration of nftables

Before showing examples, it is good to know some basic rules regarding the configuration syntax.

The hash sign (#) is used for comments, similar to your shell. To combine several commands, use the semicolon (;) sign. To split an instruction into several lines, use a backslash () at the end of the line. Then continue with your nft command on the next one.

So in short:

  • # = comment
  • ; = more commands or parameters to follow
  • \ = break a rule into multiple lines

Tip: when a statement includes a semicolon, you can tell your shell to ignore it, by adding a backslash.

Variables

Repetition is bad. To simplify things, nftables supports variables. Instead of repeating an interface multiple times, you define it at the beginning of your configuration file. After that, you will be using the variable.

Example: defining interfaces
define ext_if = eth0
define int_if = eth1
define all_if = { $ext_if, $int_if }

Tables

Within the configuration of nftables, a table is at the top of the rule set. It consists of chains, which are containers for rules. Overview: Tables -> Chains -> Rules.

The maximum length of a table name is 27 characters. At this moment you can create a table (add), delete it (delete), display it (list) or empty it (flush).

Address Families

All objects within nftables have a so called namespace, which includes the address family. This address family specifies what kind of hooks will be applied for further analysis of the information stream. For example this can be ip for IPv4 traffic, or ip6 for IPv6 traffic. As nftables is aware of the ongoing usage of IPv6, it simplifies usage for both protocol families. It does so by combining them both within the inet address family.

For filtering arp traffic, we previously used arptables. With nftables that kind of network traffic belongs to the arp address family.

If you have configured a bridged interface, you may want to use bridge (previously ebtables). Then there is netdev, which is used for ingress filtering, or traffic coming into the system. It allows for early filtering traffic, before it reaches other filters (below layer 3 on OSI model).

Overview:

  • arp
  • bridge
  • inet (= ip + ip6)
  • ip
  • ip6
  • netdev

As you can see, the names are as short as possible. You can combine rule sets for IPv4 and IPv6 traffic with the inet address family. Netdev is one of the latest additions and allows filtering before it is

Chains

After creating a table, the next step is to create chains. Chains are containers holding rules, and are of a defined type. Chains can be 1 of the two types: base or non-base. Being a base type chain, it has a related hook in the kernel. With a hook, the related chain can “see” the traffic, otherwise it can’t.

nft add chain ip traffic-filter output { type filter hook output priority 0 \; policy accept\; }

Chain types: base, non-base

Hook: input, output

Rules

The basic building blocks of rule in nftables consists of the following components:

  • expression(s)
  • operator
  • action

The expressions within a rule are evaluated from left to right. When the first expression matches, it continues with the other parts. If the expression does not result in a positive outcome, the next rule in line will be evaluated.

Examples

nft add rule Firewall Incoming ip daddr 192.168.0.1-192.168.0.19 drop

For example, tcp dport 22 accept

Advanced Configuration 

Sets

A set is a collection of data elements. This could be for example filled with IPv4 addresses, or port numbers. Maximum length of a set name is 15 characters. If you exceed this is, an error will follow:

Could not process rule: Numerical result out of range
Anonymous Sets

dport { 22, 23, 80, 443 }

Usage: Directly used in rules

Named Sets

nft add set inet blacklist blacklist4-perm { type ipv4_addr \; }
nft add element inet blacklist blacklist4-perm { 192.168.1.21, 192.168.1.22 }
nft add rule inet blacklist input ip saddr @blacklist4-perm drop

Usage: Filled with data, then referenced in a rule

Mappings

A map is used to do a mapping. You use one field, to look up the value of another, and act on that.

Examples needed

Dictionaries

Another type is the dictionary (or verdict maps). They use the structure of a set and are a powerful component within nftables, as they can include the verdict.

nft add rule ip Firewall Forward ip daddr vmap {\
      192.168.1.1-192.168.1.10 : jump chain-dmz, \
      192.168.2.1-192.168.2.99 : jump chain-ssn1, \
      192.168.2.100-192.168.2.199 : jump chain:ssn2, \
      192.168.3.1-192.168.3.50 : jump chain-desktops \
}

Traffic Hooks

Each type of traffic has one or more possible traffic hooks. They can be used to make more specific filters.

Interfaces

iifname = Incoming interface
oifname = Outgoing interface

Protocols

Basic syntax
<dport/sport>
icmp
udp
ip
tcp

dport/sport: destination port or source port. For example SSH running on our system, would indicate port 22 as destination for incoming traffic. So in this case: tcp dport 22

Outgoing traffic to another server, would be outgoing traffic to the SSH daemon on the target, which would be dport as well.

oifname lo accept

icmp type {echo-reply} drop
icmp accept
udp sport bootpc dport bootps accept
ip daddr 127.0.0.1 tcp dport {http, postgresql, ipp} accept
udp dport dns accept
tcp dport {dns, http, ntp, https, 9418} accept

Creating Tables, Chains and Rules

Next step is creating tables.

nft add table inet incoming-traffic

Note: if you don’t specify inet, the ip address family will be used by default

Within a table, we then create a chain. A chain is a container of one or more rules and used for the organization of the rule. In other words, it is a rule set.

Now we have created our table, we add an input chain.

nft list table inet incoming-traffic
table inet incoming-traffic {
        chain input {
        }
}

nft add chain inet incoming-traffic management
nft add rule inet incoming-traffic management tcp dport 22
nft add chain inet incoming-traffic web-traffic
nft add rule inet incoming-traffic web-traffic tcp dport 80 counter
nft add rule inet incoming-traffic web-traffic tcp dport 443 counter

Best Practices for nftables

Use clear names

Like in the world of software development, you have to use self-declaring names for your tables. Some examples use the name “filter”, which is confusing on what it is doing specifically.

Frequently Asked Questions

How can I see all tables for IPv4 and IPv6?

Use the inet address family when using both IPv4 (ip) and IPv6 (ip6).

nft list tables inet

Why do I get an error when trying to show an existing table?

You might have forgotten to specify the address family. Use nft list tables first.

How can I see the rule numbers within each table and/or chain?

nft list table inet filter-traffic -a

How can I export my rules and backup them?

Use nft export xml or nft export json

Common Mistakes

Like most firewall types, it is easy to make mistakes. We have collected a few common mistakes, so you can avoid them early on.

Loading rules without flushing

When loading rules from a file, flush them first. Or better, make a backup first by exporting them. Then flush the rules and import them.

Splitting IPv4 and IPv6

nftables has a great facility to combine traffic for IPv4 (ip) and IPv6 (ip6), named “inet”. This way you can enable incoming traffic for your web server on port 80, for both protocol families.

Making rules too complicated

Like in life, more rules result in more complexity. Keep things as simple as possible. Most likely you are not the only one who has to understand your firewall rules, so building rules needs some attention. Another important step is documenting specific rules which are not obvious.

Forgetting the protocol family

When requesting the list of active tables, the result set might seem to be empty. By default, the nft utility will use the ip protocol family. For example, when using the inet family, this will result in no entries listed.

Errors

Invalid table

<cmdline>:1:1-12: Error: Could not process rule: Table 'x' does not exist

If you try to list a non-existing table, you will receive this error. Show the tables with list tables.

nft list tables

Other useful resources

Here are some other resources to use

Feedback

Small picture of Michael Boelen

This article has been written by our Linux security expert Michael Boelen. With focus on creating high-quality articles and relevant examples, he wants to improve the field of Linux security. No more web full of copy-pasted blog posts.

Discovered outdated information or have a question? Share your thoughts. Thanks for your contribution!

Mastodon icon