Nftables/Examples

From Gentoo Wiki
Jump to: navigation, search

On this page several example nftable configurations can be found. The first two examples are skeletons to illustrate how nftables works. The third and fourth exmaple show how, using nftables, rules can be simplified by combining IPv4 and IPv6 in the generic IP table 'inet'. The fifth example shows how nftables can be combined with bash scripting.

Basic routing firewall

The following is an example of nftables rules for a basic IPv4 firewall that: 1) only allows packets from lan to the firewall machine and 2) only allows packets a) from lan to wan and b) from wan to lan for connections established by lan.

Please note: for forwarding between wan and lan to work, it needs to be enabled with sysctl or in /proc/sys.

FILE /etc/conf.d/nftables_firewall
# firewall
table ip filter {
	# allow all packets sent by the firewall machine itself
	chain output {
		type filter hook output priority 100; policy accept;
	}

	# allow LAN to firewall, disallow WAN to firewall
	chain input {
		type filter hook input priority 0; policy accept;
		iifname "lan0" accept
		iifname "wan0" drop
	}

	# allow packets from LAN to WAN, and WAN to LAN if LAN initiated the connection
	chain forward {
		type filter hook forward priority 0; policy accept;
		iifname "lan0" oifname "wan0" accept
		iifname "wan0" oifname "lan0" ct state established accept
		iifname "wan0" oifname "lan0" ct state related accept
		iifname "wan0" oifname "lan0" drop
	}
}

Basic NAT

The following is an example of nftables rules for setting up basic Network Address Translation (NAT) using masquerade. If you have a static IP, it would be slightly faster to use source nat (SNAT) instead of masquerade. This way the router would replace the source with a predefined IP, instead of looking up the outgoing IP for every packet.

Please note: masquerade is available in kernel 3.18 and up. When using NAT, be sure to unload or disable iptables NAT, as it will take precedence over nftables NAT.

FILE /etc/conf.d/nftables_nat
# NAT
table ip nat {
	chain prerouting {
		type nat hook prerouting priority 0; policy accept;
	}

	# for all packets to WAN, after routing, replace source address with primary IP of WAN interface
	chain postrouting {
		type nat hook postrouting priority 100; policy accept;
		oifname "wan0" masquerade
	}
}

Typical workstation (separate IPv4 and IPv6)

The following is an example of nftables rules for a typical workstation that recreates chains and tables known from iptables.

FILE /etc/conf.d/nftables.rules
#!/sbin/nft -f

flush ruleset

# ----- IPv4 -----

# filter
table ip filter {
	chain input {
		type filter hook input priority 0; policy drop;
		ct state invalid counter drop comment "drop invalid packets"
		ct state {established, related} counter accept comment "accept all connections related to connections made by us"
		iifname lo accept comment "accept loopback"
		iifname != lo ip daddr 127.0.0.1/8 counter drop comment "drop connections to loopback not coming from loopback"
		ip protocol icmp counter accept comment "accept all icmp types"
		tcp dport 22 counter accept comment "accept ssh"
		counter comment "count dropped packets"
	}

	chain output {
		type filter hook output priority 0; policy accept;
		counter comment "count accepted packets"
	}

	chain forward {
		type filter hook forward priority 0; policy drop;
		counter comment "count dropped packets"
	}
}

# nat
table ip nat {
	chain prerouting {
		type nat hook prerouting priority 0; policy accept;
		counter comment "count accepted packets"
	}

	chain input {
		type nat hook input priority 0; policy accept;
		counter comment "count accepted packets"
	}

	chain output {
		type nat hook output priority 0; policy accept;
		counter comment "count accepted packets"
	}

	chain postrouting {
		type nat hook postrouting priority 0; policy accept;
		counter comment "count accepted packets"
	}
}


# ----- IPv6 -----

#filter
table ip6 filter6 {
	chain input {
		type filter hook input priority 0; policy drop;
		ct state invalid counter drop comment "drop invalid packets"
		ct state {established, related} counter accept comment "accept all connections related to connections made by us"
		iifname lo accept comment "accept loopback"
		iifname != lo ip6 daddr ::1/128 counter drop comment "drop connections to loopback not coming from loopback"
		ip6 nexthdr icmpv6 counter accept comment "accept all icmp types"
		tcp dport 22 counter accept comment "accept ssh"
		counter comment "count dropped packets"
	}

	chain output {
		type filter hook output priority 0; policy accept;
		counter comment "count accepted packets"
	}

	chain forward {
	type filter hook forward priority 0; policy drop;
	counter comment "count dropped packets"
	}
}

# nat
table ip6 nat6 {
	chain prerouting {
		type nat hook prerouting priority 0; policy accept;
		counter comment "count accepted packets"
	}

	chain input {
		type nat hook input priority 0; policy accept;
		counter comment "count accepted packets"
	}

	chain output {
		type nat hook output priority 0; policy accept;
		counter comment "count accepted packets"
	}

	chain postrouting {
		type nat hook postrouting priority 0; policy accept;
		counter comment "count accepted packets"
	}
}

Typical workstation (combined)

The following is an example of nftables rules for a typical workstation that recreates chains and tables known from iptables. Here, the filtering sections for IPv4 and IPv6 are combined in the generic IP table 'inet'. The NAT sections remain separate, as inet doesn't support NAT chains.

Please note: inet is available in kernel 3.14 and up.

FILE /etc/conf.d/nftables.rules
#!/sbin/nft -f

flush ruleset

# filter, inet
table inet filter {
	chain output {
		type filter hook output priority 0; policy accept;
		counter comment "count accepted packets"
	}

	chain forward {
		type filter hook forward priority 0; policy drop;
		counter comment "count dropped packets"
	}

	chain input {
		type filter hook input priority 0; policy drop;
		ct state invalid counter drop comment "drop invalid packets"
		ct state {established, related} counter accept comment "accept all connections related to connections made by us"
		iifname lo accept comment "accept loopback"
		iifname != lo ipv4 daddr 127.0.0.1/8 counter drop comment "drop connections to loopback not coming from loopback"
		iifname != lo ip6 daddr ::1/128 counter drop comment "drop connections to loopback not coming from loopback"
		ip protocol icmp counter accept comment "accept all icmp types"
		ip6 nexthdr icmpv6 counter accept comment "accept all icmp types"
		tcp dport 22 counter accept comment "accept ssh"
		counter comment "count dropped packets"
	}
}

# nat, ipv4
table ip nat {
	chain prerouting {
		type nat hook prerouting priority 0; policy accept;
		counter comment "count accepted packets"
	}

	chain input {
		type nat hook input priority 0; policy accept;
		counter comment "count accepted packets"
	}

	chain output {
		type nat hook output priority 0; policy accept;
		counter comment "count accepted packets"
	}

	chain postrouting {
		type nat hook postrouting priority 0; policy accept;
		counter comment "count accepted packets"
	}
}

# nat, ipv6
table ip6 nat6 {
	chain prerouting {
		type nat hook prerouting priority 0; policy accept;
		counter comment "count accepted packets"
	}

	chain input {
		type nat hook input priority 0; policy accept;
		counter comment "count accepted packets"
	}

	chain output {
		type nat hook output priority 0; policy accept;
		counter comment "count accepted packets"
	}

	chain postrouting {
		type nat hook postrouting priority 0; policy accept;
		counter comment "count accepted packets"
	}
}

Stateful router example

The following is an example of nftables configuration script for a stateful router.

Note
Shell scripts break atomicity when applying the ruleset unless using nftables native scripting environment. See Nftables Scripting.
FILE /home/rt/scripts/nft.sh
#!/bin/bash

nft="/sbin/nft";

# ruleset, masquerade and full reject support are available starting with Linux Kernel 3.18
${nft} flush ruleset;

export LAN_IN=enp3s6
export LAN_ML=enp2s0
export WAN=ppp0
LAN_INLOCALNET=192.168.1.0/24
LAN_MLNET=10.52.0.0/14
MLIP=10.54.1.101
TORRENT_PORT_WAN=55414
TRACKER_TORRENT_PORT_WAN=4949
TORRENT_PORT_LAN=55413

MAC[2]=00:23:45:67:89:ab
...
MAC[20]=00:fe:dc:ba:98:76

${nft} -f /etc/nftables/ipv4-filter;
${nft} -f /etc/nftables/ipv4-nat;

# BANNED
${nft} add rule filter input meta iifname ${WAN} ip saddr 121.12.242.43 drop;

# Drop locals from internet
${nft} add rule filter input meta iifname ${WAN} ip saddr \
        { 192.168.0.0/16, 10.0.0.0/8, 172.16.0.0/12 } drop;

# Drop invalid
${nft} add rule filter input ct state invalid drop;

${nft} add rule filter input meta iif lo ct state new accept;

${nft} add rule filter input meta iif ${LAN_ML} ip saddr ${LAN_MLNET} ct state new accept;
${nft} add rule filter input meta iif ${LAN_IN} ip saddr ${LAN_INLOCALNET} ct state new accept;

${nft} add rule filter input ip protocol tcp tcp dport \
        { ${TORRENT_PORT_LAN}, ${TORRENT_PORT_WAN}, \
                ${TRACKER_TORRENT_PORT_WAN} } ct state new accept;
{nft} add rule filter input ip protocol udp udp dport \
        { ${TORRENT_PORT_LAN}, ${TORRENT_PORT_WAN}, \
                ${TRACKER_TORRENT_PORT_WAN} } ct state new accept;

${nft} add rule filter input meta iifname ${WAN} ip protocol tcp ct state new tcp dport 80 accept;

# torrent port forwarding example
${nft} add rule nat prerouting meta iifname ${WAN} tcp dport ${TORRENT_PORT_LAN} \
        dnat 192.168.1.10:${TORRENT_PORT_LAN}

${nft} add rule filter forward meta iifname ${WAN} meta oif ${LAN_IN} ip daddr 192.168.1.10 \
        tcp dport ${TORRENT_PORT_LAN} ct state new accept;

${nft} add rule filter input ip saddr != ${LAN_INLOCALNET} ct state new drop;
${nft} add rule filter forward meta iif ${LAN_ML} ct state new drop;
${nft} add rule filter forward meta iifname ${WAN} ct state new drop;


${nft} add rule filter input ct state established,related accept;

${nft} add rule nat postrouting oif ${LAN_ML} ip saddr ${LAN_INLOCALNET} snat ${MLIP};
${nft} add rule nat postrouting oifname ${WAN} ip saddr ${LAN_INLOCALNET} masquerade;

${nft} add rule filter forward ct state established,related accept;

# Give internet access to internal LAN addresses
for i in {2..20}
do
if grep 1 /var/www/myhost/htdocs/payment/192.168.1.$i > /dev/null;
then
        ${nft} add rule filter forward ether saddr ${MAC[$i]} ip saddr 192.168.1.$i \
                ct state new accept;
        echo ACCEPT 192.168.1.$i ALL;
else
        ${nft} add rule filter forward ether saddr ${MAC[$i]} ip saddr 192.168.1.$i \
                meta oif ${LAN_ML} ct state new accept;
        echo ACCEPT 192.168.1.$i ${LAN_ML};
fi
done



# Policies
${nft} add rule filter input drop;
${nft} add rule filter forward drop;
${nft} add rule filter output accept;

/etc/init.d/nftables save;