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 blocklist blocklist4-perm { type ipv4_addr \; }
nft add element inet blocklist blocklist4-perm { 192.168.1.21, 192.168.1.22 }
nft add rule inet blocklist input ip saddr @blocklist4-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
<protocol> <dport/sport> <port> <action>
Values
- 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