Lightweight script to manage a nftables based firewall with periodically and atomically updated whitelists and blacklists. Written in DASH (Debian Almquist Shell) to offer POSIX compliance.
This script is compatible with nftables >= 0.9.0 and was tested on Debian 10 Buster, 11 Bullseye¹, 12 Bookworm and 13 Trixie.
In the default configuration, the firewall will drop any incoming traffic which is not either whitelisted using the conf/whitelist.conf file, the presets in conf/presets or the conf/additional_rules.txt.
¹ Warning: Not compatible with nftables 0.9.8–1.0.1 (Debian 11 Bullseye) due to a critical regression bug in nftables.
Issue fixed via kernel patch in 5.10.103-1 on 7th March 2022.
In the following examples, we will use /etc/firewall as script path. Thus, login with root permissions, manually download the code and move its content there:
mkdir /etc/firewall
cd /etc/firewall
wget https://github.com/etkaar/nftm/archive/refs/heads/main.tar.gz
tar -xzf main.tar.gz --strip-components=1
rm main.tar.gzThe configuration files end with .sample as a safety precaution. Remove that file extension to make the configuration files usable:
cd /etc/firewall/conf
mv additional_rules.txt.sample additional_rules.txt
mv blacklist.conf.sample blacklist.conf
mv whitelist.conf.sample whitelist.confAfter that, let the script automatically validate the file permissions:
chmod 0700 /etc/firewall/app.sh
/etc/firewall/app.sh update-permissionsIf you are using Debian, you can let the script automatically setup both the crontab and the startup script:
/etc/firewall/app.sh setup-crontab
/etc/firewall/app.sh setup-startupscriptThe script will warn you, if the crontab or startup script is missing. To suppress that you can just append --no-warnings:
/etc/firewall/app.sh [...] --no-warningsCreate a startup file and allow execution:
touch /etc/network/if-up.d/nftm
chmod 0755 /etc/network/if-up.d/nftmThis startup file needs following content:
#!/bin/sh
if [ ! "$IFACE" = "lo" ]
then
/etc/firewall/app.sh init
fiFinally, you need a crontab to make sure DynDNS records are periodically updated, the default is every three (3) minutes.
Run crontab -e as root user and add following line:
*/3 * * * * /etc/firewall/app.sh cronYou can also use following command:
(crontab -l 2>/dev/null; echo "*/3 * * * * /etc/firewall/app.sh cron") | crontab -You need to enable at least one default preset. At this time, this will be either default ipv4-only or default ipv4-and-ipv6:
/etc/firewall/presets.sh enable default ipv4-onlyList all available presets:
/etc/firewall/presets.sh listTo enable the http and https presets (ports 80 and 443), just type in:
/etc/firewall/presets.sh enable custom http
/etc/firewall/presets.sh enable custom httpsAfter that, you may want to edit the conf/whitelist.conf to add your IP address for SSH access. It is a good idea to use DynDNS for that, so the firewall only allows SSH access from your IP address. See below the default conf/whitelist.conf:
# <dynamic hostname|address|subnet(*)> <enabled> <protocol> <port(s)>
# Single IPv4 or IPv6 addresses:
# 192.168.1.5 1 tcp 22,587
# 2001:0DB8:7654:0010:FEDC:0000:0000:3210 1 udp 27015
# (*) To use subnets, you need nftables >= 0.9.4:
# 192.168.0.0/16 1 tcp 22
# You can use hostnames which are associated
# with an IPv4 and/or IPv6 address:
#
# client-dyndns.example.com 1 tcp 22,587
# Subnets commonly used for private networks:
#
# - 10.0.0.0/8
# - 172.16.0.0/12
# - 192.168.0.0/16
#
# See also:
# https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtmlOnce you are sure all is correct, do a full reload of the firewall but do not end your SSH session yet:
/etc/firewall/app.sh full-reloadNow, try to open a seperate SSH session to your server. If that works, the IP address could be successfully fetched from your DynDNS name. Of course, you can and should manually doublecheck that by listing the firewall ruleset:
/etc/firewall/app.sh listFor debugging purposes, dropped packages may be logged.
You should disable that once all runs fine by commenting out the line in conf/additional_rules.txt:
...
# From Debian 12 Bookworm on this is handled by systemd, so
# use following command to get a live-view of dropped packets:
# journalctl -f
#
#add rule inet filter default_input log prefix "nft dropped: "nftables is the default firewall in Debian 11 Bullseye and already used as backend in Debian 10 Buster, so you don't need to compile it. Nonetheless, in case you want to compile it to the newest available version, you can use following commands (tested on Debian 11 Bullseye):
apt install git autoconf autogen libtool make pkg-config
apt install bison flex asciidoc libgmp-dev libedit-dev python3-distutils
git clone git://git.netfilter.org/libmnl
cd libmnl
sh autogen.sh
./configure
make
make install
git clone git://git.netfilter.org/libnftnl
cd libnftnl
sh autogen.sh
./configure
make
make install
git clone git://git.netfilter.org/nftables
cd nftables
sh autogen.sh
./configure
make
make install
reboot
nft --version