Geoblocking mit Hilfe von nftables einrichten

Letzte Aktualisierung am 06.12.2025, 11:12:05 Uhr

In vielen Artikel im Internet geht es darum, Geoblocking auf Basis des verwendeten Webservers umsetzen. Das geht mir aber persönlich nicht weit genug bzw. setzt nicht direkt am Betriebssystem an. die Verbindungen sollen direkt mit der Firewall von Debian/Ubuntu ab- bzw. gehandelt werden.

Nachstehend beschreibe ich die Einrichtung eines Geoblocking mit Hilfe von nftables unter Ubuntu Server 22.04 LTS. Die Rahmenbedingungen dabei ist, dass ca. 384MB Arbeitsspeicher frei sein müssen. Falls der Arbeitsspeicher nicht ausreicht und nicht erweitert werden soll/kann, ist evtl. eine SWAP Datei eine gute Alternative (siehe hier).

Download von Geo IP Informationen.

git clone https://github.com/dwydler/nftables-geoip.git /opt/nftables-geoip/
/opt/nftables-geoip/nft_geoip.py --file-location /opt/nftables-geoip/location.csv --download --output-dir /opt/nftables-geoip/

Separates Verzeichnis für die zusätzlich nftables Dateien.

mkdir /etc/nftables

Geoblocking Dateien kopieren.

cp /opt/nftables-geoip/geoip-def-all.nft /etc/nftables/
cp /opt/nftables-geoip/geoip-ipv4.nft /etc/nftables/
cp /opt/nftables-geoip/geoip-ipv6.nft /etc/nftables/

Sicherung der Originaldatei nftables.

cp /etc/nftables.conf /etc/nftables.conf.original

Benutzerdefinierte Konfiguration erstellen.

cat << \EOF > /etc/nftables.conf
#!/usr/sbin/nft -f

flush ruleset

table inet filter {
        include "/etc/nftables/geoip-def-all.nft"
        include "/etc/nftables/geoip-ipv4.nft"
        include "/etc/nftables/geoip-ipv6.nft"

        chain geoip-mark-output {
                type filter hook output priority -1; policy accept;

                meta mark set ip daddr map @geoip4
                meta mark set ip6 daddr map @geoip6
        }

        chain geoip-mark-input {
                type filter hook input priority -1; policy accept;

                meta mark set ip saddr map @geoip4
                meta mark set ip6 saddr map @geoip6
        }

        chain input {
                type filter hook input priority 0;

                iif lo accept

                ct state established,related counter accept
                meta nfproto ipv4 icmp type { echo-request } counter accept
                meta nfproto ipv6 icmpv6 type echo-request counter accept

                meta nfproto ipv6 icmpv6 type { nd-neighbor-advert, nd-neighbor-solicit, nd-router-advert} ip6 hoplimit 1 accept
                meta nfproto ipv6 icmpv6 type { nd-neighbor-advert, nd-neighbor-solicit, nd-router-advert} ip6 hoplimit 255 counter accept

                # Logging
                log prefix "nft.inet.input.mark "

                # Freigabe von Port 22 für Deutschland
                meta mark $DE tcp dport 22 counter accept

                # Freigabe von Port 22 für Deutschland und USA
                # meta mark {$DE, $US} tcp dport 22 counter accept

                # Freigabe von Port 22 und 80 für Deutschland und USA
                # meta mark {$DE, $US} tcp dport {22, 80} counter accept

                # Logging
                log prefix "nft.inet.input.drop "

                tcp dport 0-65535 counter reject
                udp dport 0-65535 counter drop
                counter drop
        }
        chain forward {
                type filter hook forward priority 0;
        }
        chain output {
                type filter hook output priority 0;
        }
}
EOF

In diesen Fall werden SSH-Verbindungen ausschließlich von IPv4/IPv6 Adressen erlaubt, die Deutschland zugeordnet sind (Zeile 37). Weiter Länder können jeweils mit einer neuen Konfigurationszeile und dem Länderkürzel hinzugefügt werden.

Einlesen der neuen Konfiguration.

nft -f /etc/nftables.conf

Es kann vorkommen, dass beim Versuch die neuen Konfiguration einzulesen Fehler wie diese auftreten:

In file included from /etc/nftables.conf:8:1-33:
/etc/geoip-def-all.nft:260:2-5: Error: syntax error, unexpected type
type mark : mark
^^^^
In file included from /etc/nftables.conf:8:1-33:
/etc/geoip-def-all.nft:261:2-6: Error: syntax error, unexpected flags
flags interval
^^^^^

In diesen Fall sind in der Konfiguration vor TABS und keine Leerzeichen zum Einrücken benutzt werden. Somit die Einrückung nochmals ausschließlich mit Hilfe Leertaste vornehmen.

Dienst aktivieren und (neu) starten.

systemctl enable nftables.service
systemctl restart nftables.service

Kontrolle, ob die Regeln auch ordnungsgemäß erstellt worden sind.

nft list chain inet filter input
nft list chain inet filter outout

Erstellen eines Scripts zum Download und Einspielen von neuen Geoblocking Dateien sowie Aktualisierung des Regelwerks der Firewall.

cat << \EOF > /opt/nftables-geoip/update.sh
#!/bin/bash


# Download new files
/opt/nftables-geoip/nft_geoip.py --file-location /opt/nftables-geoip/location.csv --download --output-dir /opt/nftables-geoip/

# Copy files to productive folder
cp /opt/nftables-geoip/geoip-def-all.nft /etc/nftables/
cp /opt/nftables-geoip/geoip-ipv4.nft /etc/nftables/
cp /opt/nftables-geoip/geoip-ipv6.nft /etc/nftables/

# Reload new map
nft flush chain inet filter input
nft flush chain inet filter geoip-mark-input
nft flush chain inet filter geoip-mark-output
nft flush map inet filter continent_code
nft flush map inet filter geoip4
nft flush map inet filter geoip6
nft -f /etc/nftables.conf

EOF

Berechtigungen der Datei anpassen.

chmod 700 /opt/nftables-geoip/update.sh

Das Update Script als Cronjob einrichten.

ln -s /opt/nftables-geoip/update.sh /etc/cron.daily/nftables-geoip

Viel Spaß beim Ausprobieren. 🙂

Abonnieren
Benachrichtige mich bei
5 Kommentare
neueste
älteste
Inline Feedbacks
View all comments
M.L.
05.12.2025 08:57

vielen Dank für die Anleitung. Damit klappt es auch für den Laien dies einzurichten.
Frage (weil ich die Technik tatsächlich nicht wirklich verstehe): wie kann ich verschiedene Ports für mehrere Länder freigeben (Zeile 37 in der Konfig).
meta mark $DE $AT $CH $FR tcp dport 22 443 counter accept – funktioniert nicht (?)

M.L.
Reply to  Daniel
11.12.2025 19:36

Vielen Dank! Das hilft mir sehr!

André
17.04.2025 15:08

Mega Idee das direkt Kernel nah zu realisieren! Bei größeren NFT-Rules ist ein kompletter flush kontraproduktiv. Ich denke da schon an fail2ban. Folgend meine Umsetzung. Ich baue da auf die dynamischen Regeln von NFT auf und habe alle meine Regeln (inkl. deiner) in /etc/nftables/input_rules.nft gepackt (ohne einf flush dort) und diese al inlude in die haupt conf von nft. So kann das dynamisch geladen werden. Nur müssen dann vorher die Chains uns Maps geleert werden (da sonst doppelter Müll entsteht) #!/bin/bash # Download new files /opt/nftables-geoip/nft_geoip.py --file-location /opt/nftables-geoip/location.csv --download --output-dir /opt/nftables-geoip/ # Copy files to productive folder cp /opt/nftables-geoip/geoip-def-all.nft /etc/nftables/… Weiterlesen »