Firewall Management (nftables)
Skill Level: Advanced
1. Overview
NFTables is the subsystem of the Linux kernel which provides filtering and classification of network packets, datagrams, or frames. This software provides an in-kernel packet classification framework that is based on a network-specific Virtual Machine (VM) and a new nft userspace command line tool.
That is a lot of words to say: NFTables is a system that inspects network packets to and from your system, and based on a set of rules does something with those packets (ie: accept, deny, forward).
Primary benefits over the older system (iptables):
-
Better performance
-
Better usability
2. Getting Started
For these exercises, you will be using the host node2
as user root
.
From host bastion
, ssh to node2
.
ssh node2
Use sudo
to elevate your privileges.
[[ "$UID" == 0 ]] || sudo -i
Verify that you are on the right host for these exercises.
workshop-nftables-checkhost.sh
Disable Firewalld
For this unit, we also need to ensure firewalld is disabled.
systemctl disable firewalld --now
systemctl mask firewalld
systemctl unmask nftables
systemctl enable nftables --now
You are now ready to proceed with these exercises.
3. Core Concepts
It is not unusual for this subsystem of the linux kernel to get replaced from time to time. You may be familiar with some of the prior filter systems:
-
ip-firewall (linux kernel v2.0, managed with ipfwadm)
-
ip-chains (linux kernel v2.2, managed with ipchains)
-
ip-tables (linux kernel v2.4, managed with iptables)
-
nf-tables (linux kernel v3.13, managed with nft)
There are others, but these are the most familiar ones that appeared in the major distributions.
In RHEL 10, you have a choice on how to implement and manage the firewall. In which scenario should you use one of the following?
-
firewalld: easy to use and covers traditional firewall use cases
-
nftables: for complex and performance-critical firewalls
-
iptables: the nf_tables API provides backward compatibility so that scripts that use iptables commands still work
For 99% of all use cases the firewalld tools are the best choice. For this lab however we are trying to introduce nft
and demonstrate some differences to the former iptables
.
Let us now begin to understand the constructs that build the firewall rules: Tables, Chains & Rules.
3.1. Tables
Fundamentally speaking, filter rules are organized into chains and the chains are then further organized into tables.
Tables are the top-level construct within the nftables ruleset.
A table consists of:
-
chains
-
sets
-
maps
-
flowtables
-
stateful objects
Each table belongs to exactly one family:
-
ip - IPv4
-
ip6 - IPv6
-
inet - both IPv4 and IPv6
-
arp
-
bridge
-
netdev
So your ruleset requires at least one table for each family you want to filter.
Using nft, show the current state of the network tables.
nft list tables
There is nothing there, so let us create a new table called 'workshop'
nft add table inet workshop
nft list tables
table inet workshop
3.2. Chains
Unlike in iptables, there are no predefined chains like INPUT, OUTPUT, etc. |
A chain is a collection of attached rules that take action on network packets (ie: accept, drop).
Chain types are:
-
filter - filters packets (supported by the arp, bridge, ip, ip6 and inet families)
-
route - reroutes packets (supported by the ip, ip6 and inet families)
-
nat - performs Networking Address Translation (supported by the ip, ip6 and inet families)
Hooks you can use:
-
ingress - sees packets immediately after passing up from NIC driver, before prerouting (only in netdev family since Linux kernel 4.2, and inet family since Linux kernel 5.10)
-
prerouting - sees all incoming packets, before any routing decision has been made
-
input - sees incoming packets that are addressed to and have now been routed to the local system
-
forward - sees incoming packets that are not addressed to the local system
-
output - sees packets that originated from processes in the local machine
-
postrouting - sees all packets after routing, just before they leave the local system
Using nft, show the current state of the network chains.
nft list chains
Again there is nothing there, so let’s create a new chain in our table called 'INPUT'.
nft leverages special characters (i.e. curly braces and semicolons) so we must use single quotes to protect our rules on the command-line. |
nft 'add chain inet workshop INPUT { type filter hook input priority 0 ; }'
nft list chains
table inet workshop { chain INPUT { type filter hook input priority filter; policy accept; } }
3.3. Rules
Rules take actions (e.g. accept, drop, forward) on network packets based on specified criteria.
-
a rule consists of zero or more expressions followed by one or more statements
-
each expression tests whether a packet matches a specific payload field or packet/flow metadata
-
if a packet matches all of the expressions in the rule then the rule statements are executed
Using the Table and Chain created above, let’s proceed to implement and manage some basic rules.
4. Exercise: Managing Rules
4.1. Add Single Rule
nft insert rule inet workshop INPUT tcp dport http counter
Verify the rule change.
nft -n -a list table inet workshop
table inet workshop { # handle 6 chain INPUT { # handle 1 type filter hook input priority 0; policy accept; tcp dport 80 counter packets 0 bytes 0 # handle 2 } }
Now is a good time to point out that the exercises in this unit are deliberately meant to be nondestructive. Meaning, we don’t want this machine to be unusable due to an error in rule insertion or deletion. |
So what does the previous rule do? It merely counts packets that arrive at port 80 on our machine. Let’s send some packets to our localhost address.
curl 127.0.0.1
There is no web server running on this system so you will get a failed connection message. However, now we reexamine the counters.
nft -n -a list table inet workshop
table inet workshop { # handle 6 chain INPUT { # handle 1 type filter hook input priority 0; policy accept; tcp dport 80 counter packets 1 bytes 60 # handle 2 } }
Not too exciting, but now you know how to add a rule.
4.2. Delete Single Rule
Deleting rules takes a little care in that you have to identify a handle. Listing the rules shows the handle number next to each rule.
nft -n -a list table inet workshop
table inet workshop { # handle 6 chain INPUT { # handle 1 type filter hook input priority 0; policy accept; tcp dport 80 counter packets 2 bytes 140 # handle 2 } }
In the example above, the handle = 2. We can now use that handle to delete the rule.
workshop-nftables-gethandle.sh: Because your specific handle-number may differ from our example, we’ve written a script to extract the handle number from the filter table. This technique is simplistic and NOT sanctioned for use beyond this exercise nft -n -a list table inet workshop | grep 'dport 80' | sed -e 's/^.*handle\s*//' |
Now, let’s delete that rule.
nft delete rule inet workshop INPUT handle $(workshop-nftables-gethandle.sh)
Verify the rule change.
nft -n -a list table inet workshop
table inet workshop { # handle 6 chain INPUT { # handle 1 type filter hook input priority 0; policy accept; } }
4.3. Add Multiple Rules at Once
nft insert rule inet workshop INPUT tcp dport { ssh, http, https, 8181 } counter
Verify the new rules.
nft -n -a list table inet workshop
table inet workshop { # handle 6 chain INPUT { # handle 1 type filter hook input priority 0; policy accept; tcp dport { 22, 80, 443, 8181 } counter packets 5 bytes 380 # handle 4 } }
Again, our chosen hook (counter) is meant primarily to be a nondestructive rule. By using the input hook and applying the accept and drop policies, you get into the business of recreating a firewall. Let’s go ahead and cleanup and restore firewalld and take one last look at the state of the system.
4.4. Cleanup
Remove the chain added during this exercise.
nft flush table inet workshop
Now delete the table
nft delete table inet workshop
systemctl disable nftables --now
systemctl mask nftables
systemctl unmask firewalld
systemctl enable firewalld --now
And now check out how firewalld has built the ruleset in the netfilter.
nft list tables
table inet firewalld
Word of caution, the output here is long…
nft -n -a list table inet firewalld
table inet firewalld { # handle 3 chain mangle_PREROUTING { # handle 127 type filter hook prerouting priority -140; policy accept; jump mangle_PREROUTING_ZONES # handle 131 } chain mangle_PREROUTING_POLICIES_pre { # handle 128 jump mangle_PRE_policy_allow-host-ipv6 # handle 297 } chain mangle_PREROUTING_ZONES { # handle 129 iifname "eth0" goto mangle_PRE_public # handle 306 goto mangle_PRE_public # handle 261 } chain mangle_PREROUTING_POLICIES_post { # handle 130 } chain nat_PREROUTING { # handle 132 type nat hook prerouting priority -90; policy accept; jump nat_PREROUTING_ZONES # handle 136 } <... SNIP ...>
Now consider all of those rules to implement these basic firewall policies…
firewall-cmd --list-all
public (active) target: default icmp-block-inversion: no interfaces: eth0 sources: services: cockpit dhcpv6-client ssh ports: protocols: forward: yes masquerade: no forward-ports: source-ports: icmp-blocks: rich rules:
Which is why we encourage you to use firewalld and firewall-cmd for 99% of your netfilter needs.
5. Conclusion
That concludes this unit on nftables.
Time to finish this unit and return the shell to its home position.
workshop-finish-exercise.sh