5. Firewalling using nftables

nftables adds in addition to protocol specific tables “ip” (IPv4) and “ip6” (IPv6) support for a IPv4/IPv6 aware table named “inet”. Using this table it's possible to add only one rule and match both protocols (in case of UDP and TCP).

Take care if rules are contained in more than one table, because the tables are checked in sequence:

IPv4-Packet --> table "ip"  --> table "inet" --> further checks
IPv6-Packet --> table "ip6" --> table "inet" --> further checks

If table “ip6” accepts the packet, also table “inet” must accept the packet, otherwise it can be dropped by a later drop rule.

5.1. Preparation for nftables usage

Install a Linux distribution which has nftables support already included. At time of writing (May 2014) at least Fedora Rawhide (upcoming version 21) has support in conjunction with nftables version 0.2.0.

5.2. Basic nftables configuration

Load kernel modules:

# modprobe nf_tables
# modprobe nf_tables_ipv4
# modprobe nf_tables_ipv6
# modprobe nf_tables_inet 

Flush iptables and ip6tables to avoid interferences:

# iptables -F
# ip6tables -F

Create filter table:

# nft add table inet filter 

Create input chain:

# nft add chain inet filter input { type filter hook input priority 0 \; }

5.3. Simple filter policy with nftables using only table “inet”

5.3.1. Configuration

Allow packets which are related to existing connection tracking entries

# nft add rule inet filter input ct state established,related counter accept

Allow IPv4 and IPv6 ICMP echo-request (aka ping)

# nft add rule inet filter input meta nfproto ipv4 icmp type { echo-request } counter accept
# nft add rule inet filter input meta nfproto ipv6 icmpv6 type echo-request counter accept 

Allow some important IPv6 ICMP traffic, without counter, but checking hop-limit for security

# nft add rule inet filter input meta nfproto ipv6
¬  icmpv6 type { nd-neighbor-advert, nd-neighbor-solicit, nd-router-advert} ip6 hoplimit 1 accept
# nft add rule inet filter input meta nfproto ipv6
¬  icmpv6 type { nd-neighbor-advert, nd-neighbor-solicit, nd-router-advert} ip6 hoplimit 255 counter accept

Allow incoming SSH for IPv4 and IPv6

# nft add rule inet filter input tcp dport 22 ct state new tcp flags \& \(syn \| ack\) == syn counter accept

Reject/drop others

# nft add rule inet filter input tcp dport 0-65535 reject
# nft add rule inet filter input udp dport 0-65535 counter drop
# nft add rule inet filter input counter drop

5.3.2. Result

Table for IP version aware filter

table inet filter {
	chain input {
		 type filter hook input priority 0;
		 ct state established,related counter packets 0 bytes 0 accept
		 ip protocol icmp icmp type { echo-request} counter packets 0 bytes 0 accept
		 ip6 nexthdr ipv6-icmp icmpv6 type echo-request counter packets 0 bytes 0 accept
		 ip6 nexthdr ipv6-icmp ip6 hoplimit 1 icmpv6 type { nd-neighbor-advert, nd-neighbor-solicit, nd-router-advert} accept
		 ip6 nexthdr ipv6-icmp ip6 hoplimit 255 icmpv6 type { nd-neighbor-advert, nd-neighbor-solicit, nd-router-advert} accept
		 tcp dport ssh ct state new tcp flags & (syn | ack) == syn counter packets 0 bytes 0 accept
		 tcp dport >= 0 tcp dport <= 65535 counter packets 0 bytes 0 reject
		 udp dport >= 0 udp dport <= 65535 counter packets 0 bytes 0 drop
		 log prefix counter packets 0 bytes 0 drop
	}
}

5.3.3. Hints for logging

To enable logging, an additonal kernel module must be loaded

# modprobe xt_LOG

BUT TAKE CARE, IT LOOKS LIKE THAT NO LOG LEVEL CAN BE SPEFICIED CURRENTLY IN nftables, resulting that events are logged with kern.emerg - POSSIBILITY OF FLODDING THE CONSOLE WITH LOG ENTRIES!

Fir initial test with logging it can be useful to disable kernel console logging in e.g. /etc/rsyslog.conf by putting a “#” in front of the related entry and restart logging daemon

#*.emerg    :omusrmsg:* 

Rule from above accepting SSH on port 22, but now with logging:

# nft add rule inet filter input tcp dport 22 ct state new tcp flags \& \(syn \| ack\) == syn log prefix \"inet/input/accept: \" counter accept

5.4. Filter policy with nftables using tables “ip”, “ip6” and “inet”

As written above, if rules should be stored in related tables, it must be assured that earlier accepts are not discarded in the further table. This can be done using “meta mark set xxxx” on every accept rule and generic rules which accepts packets with “mark xxxx”. A resulting filter set would look like the following:

# for table in ip ip6 inet; do nft list table $table filter; done
table ip filter {
	chain input {
		 type filter hook input priority 0;
		 ct state established,related counter packets 241 bytes 25193 accept
		 counter packets 2 bytes 120 mark 0x00000100 accept
		 icmp type { echo-request} counter packets 0 bytes 0 meta mark set 0x00000100 accept
	}
}
table ip6 filter {
	chain input {
		 type filter hook input priority 0;
		 ct state established,related counter packets 14 bytes 4077 accept
		 counter packets 4 bytes 408 mark 0x00000100 accept
		 icmpv6 type echo-request counter packets 1 bytes 104 meta mark set 0x00000100
		 icmpv6 type { nd-neighbor-advert, nd-neighbor-solicit, nd-router-advert} counter packets 2 bytes 224 meta mark set 0x00000100 accept
	}
}
table inet filter {
	chain input {
		 type filter hook input priority 0;
		 ct state established,related counter packets 307 bytes 31974 accept
		 counter packets 6 bytes 528 mark 0x00000100 accept
		 tcp dport ssh ct state new tcp flags & (syn | ack) == syn log prefix "inet/input/accept: " meta mark set 0x00000100 counter packets 3 bytes 200 accept
		 log prefix "inet/input/reject: " counter packets 0 bytes 0 reject
	}
}