brick wall
Fri Dec 09

NFTables Tutorial with Example

NFTables comes as the successor of IPTables. Despite it being the successor of NFTables, the syntax is quite different from IPTables, though IPTables can still be usable as it has backward compatibility. Before going further about NFTables, it is recommended to learn about IPTables and have a good understanding about it which can be learned here.

What is NFTables?

NFTables or Net Filter Tables is a Linux kernel packet framework providing network filtering on multiple networking levels. The syntax used for nftables is nft. In case you need a format to work with, you can find it in /etc/nftables.conf.

Why use NFTable?

There are several reasons why it is better to use NFTable rather than IPTables. Some prominent reasons are duplication and simplicity and dual stack IPv4/IPv6 administration.

Duplication and simplicity

Iptables only allows a single rule with a single action, which leads to inefficiency and possible duplication. On the contrary, the nftables can contain multiple conditions at once. For example, you may open multiple ports at once by setting up the ruleset like this.

tcp dport { 22, 53, 80, 123, 443 } accept

Dual stack IPv4/IPv6 administration

As mentioned from the previous post, the iptables only supports IPv4. On the other hand, nftables allows IPv4 for and IPv6 which you will be using most of the time with inet family.

The Concept

Just like iptables, there are also three terms to understand when using nftables. However, it is quite different as it doesn’t have predefined tables or chains. There are also more things to cover on each term.

Tables

In nftables, there are six types of configurable tables. These are known as family which refers to: ip (for IPv4), ip6(for IPv6), arp (for ARP), bridge (for packet that traverse bridge device), inet(dual stack ipv4/IPv6), netdev (packet handler for ingress).

Chains

Type

Some chains are only supported by certain families. There are three possible types as follows.

  • filter : supported by ip, ip6, arp, bridge, and inet.
  • route : used for mark packets (like the OUTPUT chain of mangle), supported by ip and ip6.
  • nat : supported by ip and ip6.

Hook

Hook is used to specify a specific stage while the packet is being processed. Different families may have different usable hooks.

  • prerouting, input, forward, output, postrouting : for ip, ip6, and inet.
  • input, output : for arp.
  • ingress : for netdev.

Priority

Priority is the number of orders to use between several netfilter operations. The order is ascending, so the lower the number will be executed earlier.

Policy

Policy is the decision to be made when a packet flows in a particular stage if no rules are matched. Possible values are accept and drop.

Rules

Rules and filters can be set based on certain packet information in a particular protocol. Possible protocols to match are ip, ip6, tcp, udp, udplite, sctp, dccp, ah, esp, comp, icmp, icmpv6, ether, dst, frag, hbh, mh, rt, vlan, and arp. The rules can also be set with connection tracking using ct or by the packet metainformation using meta.

Statement

Statement is the action to be made if rules are matched. There are some possible statements.

  • accept : allow packet then stop the rules evaluation.
  • drop : drop packet then stop the rules evaluation.
  • continue : queue packet then stop the rules evaluation.
  • return : return and then proceed to the next rule of the last chain.
  • jump : jump to the other chain rule and then going back to last chain rule
  • goto : goto another and skip last chain rule if any

Either jump or goto only works on regular chains (user-defined chains). It is also possible to use scripts for setting up the ruleset. Predefined script is already available in /etc/nftables.conf so you can use it as a base framework.

The Script and Syntax

Setting up the ruleset can be done using two methods: typing directly in terminal or using script which is more recommended. In this example, we will use script.
First, we need to check the active rulesets by one of the following commands.

List tables

sudo nft list tables

List chains

sudo nft list chains

List ruleset

sudo nft list ruleset

In order to block all connections and only allow some particular packets like ping requests, loopback traffic, http requests, and ssh, use the code below.

#usr/sbin/nft -f
flush ruleset
table inet firewall {
 chain incoming {
   type filter hook input priority 0; policy drop;
   ct state established,related accept
   iifname lo accept
   icmp type echo-request accept
   tcp dport {ssh, http} accept
 }
}

Here is the breakdown of the above example.

The shebang (#usr/sbin/nft -f) means that we want to execute the script using the nft command located in usr/sbin/nft. The flush ruleset is there in order to flush (remove) prior rulesets settings so it doesn’t interfere with our new ruleset.

Unlike the iptables, we need to define the name of the table before making one. Here we give the name firewall to it. If we don’t set any families, it will be set to ip by default. We use inet, so it is supposed to cover both IPv4 and IPv6. Besides, we also need to give the name of the chain. Here we use incoming.

Next, we need to define the type and we use filter because we need it for filtering the incoming and outgoing packet which you are most likely to use as well. For the hook, we bind this incoming chain with the input hook as we want to filter the incoming packet.

Priority order is ascending by default, so setting it to 0 means that it was given the highest priority. Policy is set to drop, thus it will block any packets that do not match the ruleset by default. The next one ct state established,related accept is basically saying that the existing packet (established) and the new packet requested by the already existing connection (related) will be allowed and tracked using the connection tracking module.

Since we drop all the connections by default, we want to allow the loopback traffic. Therefore we set the rules to accept it using iifname lo accept. Iifname defines the input interface name. As we want to set it for loopback we use lo.

Next, we set the protocol. Ping is based on icmp which is also known as echo-request traffic. So, we set it the way it is. Since we want to allow http connection and ssh, we should open the ports. Both are based on tcp. Opening some particular ports can be done by using the name of the network protocol like the example above (ssh and http). Otherwise you may also use the actual port, like 22 for ssh and 80 for http. We use curly brackets so we can use multiple protocols at once.

Executing the script

To execute the aforementioned script, you have to use nft extension on your file. Say that you name your file rules.nft in the current location so the command would be like this.

sudo nft -f rules.nft

The rule set is just temporary and will reset after you reboot your system. To make it persistent, you have to overwrite the existing file of nftables.conf and enable the nftables service.

nft list ruleset > /etc/nftables.conf


systemctl enable nftables

Before doing this, you have to use a root account. Switch from your current account with su and enter the root password.

After executing these two commands, you can reboot your system. If it was successful, you will have your previous setting show up when you enter the command sudo nft list ruleset right after rebooting.

Testing the script

The easiest way to test if the script works is just modifying the statement to drop and see if it still works. The above script can be modified to block loopback traffic by changing this rule iifname lo accept into this iifname lo drop.

Now, try to ping to your own localhost. If you don’t get any reply, it means that the above rule works using the following command. Change it back to accept and you are supposed to get a reply now.

ping 127.0.0.1

Removing the rules with handle

To remove or delete all rules simply use this command.

sudo nft flush ruleset

Handle in nftables works similarly to a line number in iptables. It basically tells you what the identifier number of a certain rule is. To check, use the command below.

sudo nft -a list ruleset

In my Linux box, the above example results in an output like this.

table inet firewall { # handle 5
 chain incoming { # handle 1
   type filter hook input priority 0; policy drop;
   ct state established,related accept # handle 3
   iifname lo accept # handle 4
   icmp type echo-request accept # handle 5
   tcp dport {ssh, http} accept # handle 6
 }
}

For example, in order to get rid of a rule with the handle number 5 you use this.

sudo nft delete rule inet firewall incoming handle 5

Adding new rules with position

In another case, you may also want to add a new rule without modifying the script. You can use position to achieve this. Let’s try to bring back the deleted rule into its original position.

sudo nft add rule inet firewall incoming position 4 icmp type echo-request accept

Conclusion

As a framework, nftables covers multiple layers at once like layer 4 (TCP, UDP, AH, ESP), layer 3 (IPv4, IPv6) and even layer 2 (ARP). Although it seems quite intimidating at first, it really offers better flexibility and efficiency as compared to its predecessor. However, both nftables and itpables, requires you to have at least a good underlying networking knowledge.