📙 Nftables#

Note

nftables-logo nftables is a subsystem of the Linux kernel providing filtering and classification of network packets/datagrams/frames. nftables replaces the legacy iptables component of Netfilter.

Basic concepts#

table refers to a container of chains.
chain within a table refers to a container of rules.
rule refers to an action to be configured within a chain.

Table family#

ip, arp, ip6, bridge, inet, netdev

% nft list tables [<family>]
% nft list table [<family>] <name> [-n] [-a]
% nft (add | delete | flush) table [<family>] <name>

Netfilter hooks#

netfilter-mini-flame.webp

Chains#

type refers to the kind of chain : filter, route, nat.
hook refers to an specific stage.
priority refers to a number used to order the chains

% nft (add | create) chain [<family>] <table> <name> [ { type <type> hook <hook> [device <device>] priority <priority> \; [policy <policy> \;] } ]
% nft (delete | list | flush) chain [<family>] <table> <name>
% nft rename chain [<family>] <table> <name> <newname>

Rules#

% nft add rule [<family>] <table> <chain> <matches> <statements>
% nft insert rule [<family>] <table> <chain> [position <position>] <matches> <statements>
% nft replace rule [<family>] <table> <chain> [handle <handle>] <matches> <statements>
% nft delete rule [<family>] <table> <chain> [handle <handle>]

Configuring on Debian11#

Default#

systemctl status nftables.service

/etc/nftables.conf

#!/usr/sbin/nft -f

flush ruleset

table inet 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;
        }
}

Load/Flush on service Start/Stop#

> grep Exec <(systemctl cat nftables.service)
ExecStart=/usr/sbin/nft -f /etc/nftables.conf
ExecReload=/usr/sbin/nft -f /etc/nftables.conf
ExecStop=/usr/sbin/nft flush ruleset
> nft list tables
table inet filter
> nft list ruleset
table inet filter {
        chain input {
                type filter hook input priority filter; policy accept;
        }

        chain forward {
                type filter hook forward priority filter; policy accept;
        }

        chain output {
                type filter hook output priority filter; policy accept;
        }
}

Creating your own Table/Chain/Rule#

nft add table inet ipv4_filter
for i in input output forward; do nft add chain inet ipv4_filter "$i" { type filter hook "$i" priority 0 \; } ; done
nft add rule inet ipv4_filter input tcp dport 80 accept
nft add rule inet ipv4_filter input tcp dport 443 accept
nft add rule inet ipv4_filter input tcp dport 22 accept
nft add rule inet ipv4_filter input drop
 nft add rule inet ipv4_filter output tcp sport 80 accept
 nft add rule inet ipv4_filter output tcp sport 443 accept
 nft add rule inet ipv4_filter output tcp sport 22 accept
 nft add rule inet ipv4_filter output drop

Listing table/chain/rule#

root@debian01:~# nft -a list table inet ipv4_filter
table inet ipv4_filter { # handle 2
        chain input { # handle 1
                type filter hook input priority filter; policy accept;
                tcp dport 80 accept # handle 5
                tcp dport 443 accept # handle 6
                tcp dport 22 accept # handle 7
                drop # handle 8
        }

        chain output { # handle 2
                type filter hook output priority filter; policy accept;
                tcp sport 80 accept # handle 9
                tcp sport 443 accept # handle 10
                tcp sport 22 accept # handle 11
                drop # handle 12
        }

        chain forward { # handle 3
                type filter hook forward priority filter; policy accept;
        }
}
root@debian01:~# nft -a list ruleset
table inet filter { # handle 1
        chain input { # handle 1
                type filter hook input priority filter; policy accept;
        }

        chain forward { # handle 2
                type filter hook forward priority filter; policy accept;
        }

        chain output { # handle 3
                type filter hook output priority filter; policy accept;
        }
}
table inet ipv4_filter { # handle 2
        chain input { # handle 1
                type filter hook input priority filter; policy accept;
                tcp dport 80 accept # handle 5
                tcp dport 443 accept # handle 6
                tcp dport 22 accept # handle 7
                drop # handle 8
        }

        chain output { # handle 2
                type filter hook output priority filter; policy accept;
                tcp sport 80 accept # handle 9
                tcp sport 443 accept # handle 10
                tcp sport 22 accept # handle 11
                drop # handle 12
        }

        chain forward { # handle 3
                type filter hook forward priority filter; policy accept;
        }
}

Edition mode#

nft --help | awk '/-[if]/'
  -f, file <filename>           Read input from <filename>
  -i, interactive               Read input from interactive CLI

Templates#

ls -C /usr/share/doc/nftables/examples
all-in-one.nft     ipv4-filter.nft  ipv6-nat.nft        pf.os
arp-filter.nft     ipv4-mangle.nft  ipv6-raw.nft        README
bridge-filter.nft  ipv4-nat.nft     load_balancing.nft  secmark.nft
ct_helpers.nft     ipv4-raw.nft     nat.nft             sets_and_maps.nft
inet-filter.nft    ipv6-filter.nft  netdev-ingress.nft  sysvinit
inet-nat.nft       ipv6-mangle.nft  overview.nft        workstation.nft

Web Server Filter#

cp /etc/nftables.conf{,.back_20220924}

/etc/nftables.conf

#!/usr/sbin/nft -f
# vim: ts=4:

flush ruleset

table inet filter {
        chain input {
                type filter hook input priority 0;
                ct state established,related accept
                tcp dport http accept
                tcp dport https accept
                ip saddr 192.168.0.0/16 tcp dport 22 accept
                ip saddr 192.168.122.16 tcp dport 5044 accept
                tcp flags & (fin | syn | rst | psh | ack | urg) > urg counter packets 0 bytes 0
                tcp flags & (fin | syn | rst | psh | ack | urg) < fin counter packets 0 bytes 0
                icmp type echo-reply accept
                drop
        }

        chain output {
                type filter hook output priority 0;
                ct state established,related accept
                tcp dport http accept
                tcp dport https accept
                udp dport domain accept
                udp dport ntp accept
                tcp dport 587 accept
                ip daddr 192.168.122.16 tcp dport 5044 accept
                icmp type echo-request accept
                drop
        }

        chain forward {
                type filter hook forward priority filter; policy drop;
        # Drop everything forwarded to us. We do not forward. That is routers job.
        }

}
nft -f /etc/nftables.conf
nft flush ruleset

Source NAT#

Masquerade#

nft add table ip filt
nft add chain ip filt postrouting { type nat hook postrouting priority 0\; }
nft add rule ip filt postrouting masquerade

Unidirectional#

nft -a list table filt
nft delete rule ip filt postrouting handle 5
nft add rule ip filt postrouting ip saddr 192.168.122.0/24 oif enp1s0 snat 192.168.122.73

Destination NAT#

nft add table ip filt
nft add chain ip filt prerouting { type nat hook prerouting priority 0\; }
nft add rule ip filt prerouting iif enp1s0 tcp dport 21 dnat 192.168.122.73
nft -a list ruleset
nft delete rule ip filt prerouting handle 2
nft add rule ip filt prerouting iif enp1s0 tcp dport 2121 dnat 192.168.122.73:21