Debian migration to nftables

If you have upgraded to Debian 10 buster you will know (if you have read the release notes) that Debian has now migrated to the nftables filtering framework.

See 2.2.6. Network filtering based on nftables framework by default

What is not covered is how you do this and what it really means.

The release notes say “nftables provides a full replacement for iptables” and this is almost true but not quite and I’ll come on to that at the end.

First off, it should be noted that what we’re actually really doing is changing our filtering ruleset from being defined in iptables format to being defined in nftables format.

To convert our existing iptables ruleset to nftables we first need to save a copy of our iptables rulesets:

iptables-save > iptables-active.txt
ip6tables-save > iptables6-active.txt

Once we have these we can convert them to nftables:

iptables-restore-translate -f iptables-active.txt > nftables-active.txt
ip6tables-restore-translate -f iptables6-active.txt > nftables6-active.txt

You can use these auto translated rulesets, or…

Now comes a big change, nftables is dual stack v4/v6 capable so you can merge a lot of your rules into single statements rather than have two completely separate sets of rules.

However, the new functionality can’t cope with a completely combined ruleset if you are doing more complex filtering, simply because IPv4 and IPv6 work differently.

That said, I have managed to condense two completely separate rulesets down to one ruleset with three duplicate rules in one file. This is definitely easier to manage.

The new rulesets live in the /etc/nftables.conf file and this is an executable — it’s actually a script to perform an atomic update, that is, when you run it the rules are parsed and if there are any errors the whole ruleset update is rejected.

This is a useful change as it means that you can’t apply half a ruleset if you have a typo or syntax error. Either the rules install in whole or they don’t.

On my system I moved the stock empty rules file to /etc/nftables.conf.clear to retain a quick fallback script to clear the whole ruleset if ever needed.

If you are using iptables-persistent to install your old iptables rulesets you should now remove this package (or your alternative startup method) and reboot.

Once you’ve confirmed that the iptables rules are no longer loaded from startup you can run /etc/nftables.conf to load your new nftables ruleset.

(if you aren’t 100% sure about your new nftables ruleset it might be an idea to run a sleep 30; /etc/nftables.conf.clear in a screen session to automatically clear the rules to prevent you being locked out)

Once this is all working you can use systemd to load the nftables rules at boot:

systemctl enable nftables.service

Finally there is a proper built-in method of installing filtering rules at boot! You have to wonder why that has taken so long.

At this point having migrated to nftables it’s tempting to remove the iptables package and you can do so, but there is a problem. As soon as you do this apt will start telling you that none of the nftables packages are needed and that they can be removed with apt-get autoremove.

This is why this ‘migration’ to iptables is not quite how it seems, you do need to retain the iptables package if only for one reason.. to keep the nftables packages installed.

One side effect of retaining the iptables package is you can end up with an empty ruleset in nftables that is not used.

# nft list tables
table inet Firewall
table ip filter

This all seems slightly strange. Intentional? Oversight? I don’t know but my limited testing shows that nftables works fine without the iptables package, it’s just a risk that you’ll end up losing all your nftables packages through apt-get autoremove if you don’t retain the iptables package.

Aside from that nft seems to work well, for me the ruleset syntax is better and easier to manage.

I’ve included an example v4/v6 combined ruleset below.

#!/usr/sbin/nft -f

# Define intefaces
define LAN_EXT = eth0

# Flush everything
flush ruleset

# Defaults
add table inet xciv
add chain inet xciv OUTPUT { type filter hook output priority 0; policy accept; }
add chain inet xciv INPUT { type filter hook input priority 0; policy drop; }
add chain inet xciv FORWARD { type filter hook forward priority 0; policy drop; }

# Don't filter loopback interface
add rule inet xciv INPUT iifname "lo" counter accept

# Allow all ICMP incoming
add rule inet xciv INPUT iifname $LAN_EXT ip protocol icmp counter accept

# Allow incoming connections (DNS)
add rule inet xciv INPUT iifname $LAN_EXT udp dport { 53 } counter accept
add rule inet xciv INPUT iifname $LAN_EXT tcp dport { 53 } counter accept

# Allow incoming connections (HTTP/SSL)
add rule inet xciv INPUT iifname $LAN_EXT tcp dport { 80 , 443 } counter accept

# Allow incoming connections (SMTP/SSL)
add rule inet xciv INPUT iifname $LAN_EXT tcp dport { 25, 587 } counter accept

# Allow incoming established traffic (stateful inspection)
add rule inet xciv INPUT iifname $LAN_EXT ct state related,established counter accept

# Block all other IPv4 traffic (send rejects rather than drop)
add rule inet xciv INPUT iifname $LAN_EXT ip protocol udp counter reject with icmp type host-prohibited
add rule inet xciv INPUT iifname $LAN_EXT ip protocol tcp counter reject with tcp reset

# IPv6, allow ICMP incoming and reject other ports
add rule inet xciv INPUT iifname $LAN_EXT ip6 nexthdr ipv6-icmp counter accept
add rule inet xciv INPUT iifname $LAN_EXT ip6 nexthdr udp counter reject with icmpv6 type admin-prohibited
add rule inet xciv INPUT iifname $LAN_EXT ip6 nexthdr tcp counter reject with tcp reset


Posted

in

, , , ,

by

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *