Skip to content

Instantly share code, notes, and snippets.

@4np
Last active January 16, 2023 18:28
Show Gist options
  • Save 4np/f6eb7fe6be1de07f35271eba593b4dc5 to your computer and use it in GitHub Desktop.
Save 4np/f6eb7fe6be1de07f35271eba593b4dc5 to your computer and use it in GitHub Desktop.
Gentoo Linux NFTables

Basic firewall / network router using Gentoo Linux.

After compiling the 4.19.66-gentoo kernel, udev renamed the network interfaces of my home linux Router / Firewall and my iptables setup stopped working due ip-conntrack kernel deprecations. I decided to move over to nftables, the new packet classification framework that replaces the existing {ip,ip6,arp,eb} tables infrastructure.

Even though nftables has been in the Linux kernel since 2014, the documentation is extremely sparse. As such this gist :)

Subjectively, using nftables setting up connections to my machine seems to be more responsive compared to using iptables. Connections seem to be made more quickly and ssh-ing into my machine seems more responsive. There always used to be a bit of a lag when I was using iptables, not anymore.

Preperations

In order to be able to use nftables, new kernel modules need to be configured.

Kernel configuration

I marked pretty much everything named nf_tables as a <M> module, more than the list taken from the Gentoo HowTo below:

[*] Networking support  --->
    Networking options  --->
        [*] Network packet filtering framework (Netfilter)  --->
            [*]   Advanced netfilter configuratio
            Core Netfilter Configuration  --->
                <M> Netfilter nf_tables support
                <M>   Netfilter nf_tables conntrack module
                <M>   Netfilter nf_tables counter module
                <M>   Netfilter nf_tables log module
                <M>   Netfilter nf_tables limit module
                <M>   Netfilter nf_tables masquerade support
                <M>   Netfilter nf_tables nat module
            IP: Netfilter Configuration  --->
                <M> IPv4 nf_tables support
                <M>   IPv4 nf_tables route chain support
                <M> IPv4 packet rejection
                <M> IPv4 NAT
                <M>   IPv4 nf_tables nat chain support
                <M>   IPv4 masquerade support
                <M>   IPv4 masquerading support for nf_tables

Please note: nftables masquerade will not work if iptables masquerade is in the kernel, so be sure to unload or disable it.

USE flags

In order for other packages to use nftables (like miniupnpd), add the nftables USE flag to /etc/portage/make.conf:

# nftables support (see https://packages.gentoo.org/useflags/nftables)
USE="$USE nftables"

Base rules

The base rules below will set up a Gentoo Linux box with 2 network interfaces (wan and lan) as a home firewall / router.

The goal is to:

  • set up the base firewall
  • set up NAT / Masquerade so that the LAN has access to the internet
  • use MiniUPnPd to allow services on the LAN to manage their own port forwarding via UPnP and NAT-PMP (like Plex Media Server or Transmission)

/root/nftables-init.rules

While some documentation mentions storing rules in /etc/nftables, the directory is absent on my installation. I found managing the rules manually, and persisting them (through nftables save) to work reliably. Of course you could also use something like firewalld instead.

I have stored a file describing my base firewall rules in the home dir of the root user:

#
#   Netfilter's NFTable firewall
#
# To invoke and persist the rules between reboots:
#
#   nft -f nftables-init.rules; /etc/init.d/nftables save
#
# Other example commands:
#
#   $ nft list tables
#   $ nft list tables ip
#   $ nft list table ip filter
#   $ nft list table ip nat
#   $ nft list ruleset
#   $ nft monitor
#   $ nft list ruleset
#
# See: https://gist.github.com/4np/f6eb7fe6be1de07f35271eba593b4dc5
# See: https://wiki.gentoo.org/wiki/Nftables
# See: https://wiki.gentoo.org/wiki/Nftables/Examples
# See: https://github.com/yoramvandevelde/nftables-example/blob/master/nftables-init.rules
# See: https://home.regit.org/2014/01/why-you-will-love-nftables/

# Clear all
flush ruleset

# Network interfaces
define wan = eno0
define lan = enp2s0

# Base firewall
table inet filter {
	# allow LAN to firewall, disallow WAN to firewall
	chain input {
		type filter hook input priority 0

		# ICMP flood protection
		ip protocol icmp icmp type echo-request limit rate over 1/second burst 1 packets counter drop comment "prevent IPv4 ping floods"
		ip6 nexthdr icmpv6 icmpv6 type echo-request limit rate over 1/second burst 1 packets counter drop comment "prevent IPv6 ping floods"

		# Connection state based
		ct state invalid counter drop comment "early drop of invalid packets"
		ct state { established, related } counter accept comment "accept all connections related to connections made by us"

		# accept any localhost traffic
		iif lo accept comment "accept loopback"
		iif != lo ip daddr 127.0.0.1/8 counter drop comment "drop connections to loopback not coming from loopback"

		# accept IPv4 ICMP
		ip protocol icmp counter accept comment "accept all ICMP types"
		# accept IPv6 neighbour discovery (otherwise connectivity breaks)
		ip6 nexthdr icmpv6 icmpv6 type { nd-neighbor-solicit, echo-request, nd-router-advert, nd-neighbor-advert } accept

		# Services
		tcp dport { ssh } ct state new limit rate 2/minute accept comment "accept SSH and avoid brute force"
		tcp dport { http, https } ct state new counter accept comment "accept HTTP"

		# Networks
		iifname $lan accept comment "accept all connection from LAN"
		iifname $wan drop comment "drop all connections from WAN"
	}

	# allow all packets sent by the firewall machine itself
	chain output {
		type filter hook output priority 0
		ct state related, established accept
		accept
	}

	# allow packets from LAN to WAN, and WAN to LAN if LAN initiated the connection
	chain forward {
		type filter hook forward priority 0
		accept
	}
}

# NAT: The first packet in each flow will hit this table; none others will.
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 $wan masquerade
	}
}

To install these rules:

nft -f /root/nftables-init.rules; /etc/init.d/nftables save

This will flush nftables (due to flush ruleset inside the rules above) and configure all the specified chains and their respective rules. /etc/init.d/nftables save will persist the rules between reboots.

MiniUPnPd

MiniUPnPd supports nftables.

Add the nftables USE flag to /etc/portage/make.conf:

# nftables support (see https://packages.gentoo.org/useflags/nftables)
USE="$USE nftables"

install MiniUPnPd:

emerge miniupnpd

and add it to the default runlevel and launch on boot:

rc-update add miniupnpd default

/etc/miniupnpd/miniupnpd.conf

This is my diff from the stock miniupnpd.conf configuration:

2c2
< #ext_ifname=eth1
---
> ext_ifname=eno0
41c41
< #listening_ip=eth0
---
> listening_ip=enp2s0
57c57
< #enable_natpmp=yes
---
> enable_natpmp=yes
104,105c104,105
< #secure_mode=yes
< secure_mode=no
---
> secure_mode=yes
> #secure_mode=no
170,173c170,173
< allow 1024-65535 192.168.0.0/24 1024-65535
< allow 1024-65535 192.168.1.0/24 1024-65535
< allow 1024-65535 192.168.0.0/23 22
< allow 12345 192.168.7.113/32 54321
---
> #allow 1024-65535 192.168.0.0/24 1024-65535
> allow 1024-65535 192.168.2.0/24 1024-65535
> #allow 1024-65535 192.168.0.0/23 22
> #allow 12345 192.168.7.113/32 54321

Note: don't forget to update the uuid with a random uuid as well, which I left out of this diff.

Manual port forwarding

While MiniUPnPd will take care of most of the on-demand hole-punching via UPnP and NAT-PMP, you still might need to manually configure port forwarding.

Below is an example on how to configure forwarding of remote port 32423 to Plex Media Server on LAN at 192.168.1.10:32400:

# Plex
define plex_wan_port = 32423
define plex_lan_port = 32400
define plex_server = 192.168.2.10

table inet filter {
	...
	chain input {
		...
		tcp dport { $plex_wan_port } ct state new counter accept comment "accept Plex Media Server"
	}
	
	...
	chain forward {
		...
		iifname $wan meta oif $lan ip daddr $plex_server tcp dport $plex_lan_port ct state new accept
	}
}

table ip nat {
	...
	chain prerouting {
		...
		iifname $wan tcp dport $plex_wan_port dnat $plex_server:$plex_lan_port comment "Port forwarding to Plex"
	}
}

Fail2ban

I see bots trying to get into ssh every couple of minutes or so, so it is best to use something like fail2ban (or SSHGuard) to protect yourself against those.

emerge fail2ban

and add it to the default runlevel and launch on boot:

rc-update add fail2ban default

Follow the configuration guidelines from the Gentoo Wiki.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment