Einstiegt in nftables: So funktioniert die neue Firewall – Unterschiede zu iptables

Als Video ansehen
Bereitgestellt über YouTube

Einstiegt in nftables: So funktioniert die neue Firewall – Unterschiede zu iptables

Sowohl Nftables als auch Iptables ermöglichen grundlegende Funktionen einer Firewall, darunter etwa Netzwerkpakete anhand bestimmter Regeln zu filtern (erlauben/blockieren), protokollieren oder umzuleiten. Network Adress Translation (NAT) kann ebenfalls umgesetzt werden. Dieser Beitrag soll Neulingen einen Einstieg in iptables ermöglichen. Zusätzlich werden wir die Unterschiede zu iptables betrachten und die Frage beantworten, wieso es mit iptables und nftables zwei in ihrer Funktion recht ähnliche Werkzeuge gibt.

Was ist eine Firewall?

Eine Firewall kann man sich wie einen Türsteher vorstellen: Anhand selbst festgelegter Regeln schützt sie einen Computer oder das gesamtes Netzwerk vor unerwünschten Netzwerkzugriffen. Beispielsweise kann man damit den Zugriff auf Dienste wie SSH nur für bestimmte IP-Adressen erlauben. Oder böswillige IP-Adressen blockieren, sodass sämtliche Verbindungsversuche von Ihnen abgelehnt werden. Es gibt Hardware-Firewalls, also physische Geräte mit dieser Funktionalität. In nahezu jedem DSL-Modem ist beispielsweise eine Firewall eingebaut, um das Heimnetzwerk gegenüber dem Internet zu schützen. Man kann aber auch Software-Firewalls direkt auf dem zu schützenden Gerät selbst installieren, etwa einem Computer oder Server. Grundlegende Funktionen dafür bieten Nftables und Iptables.

Was sind iptables?

Grundlage beider Projekte ist Netfilter, eine seit 1998 existierende Softwareschicht im Linux-Kernel. Dazu gehört eine gleichnamige Bibliothek, mit der man auf bestimmte Ereignisse im Linux Network-Stack reagieren kann, wie z.B. eingehende oder Ausgehende Datenpakete. Iptables entstand ebenfalls 1998 und war das erste Kommandozeilenwerkzeug, um Filterregeln anzulegen. Die Regeln werden in Form von Tabellen dargestellt. Jede gibt an, was an einem Paket überprüft wird und was bei einer Übereinstimmung geschieht. Beispielsweise könnten Datenpakete von einer bestimmten IP-Adresse oder einem Port erlaubt oder verworfen werden.

Warum gibt es nftables?

Die iptables-Werkzeuge wurden mehrfach erweitert und ergänzt. So gibt es unter andrem iptables für IPv4, ip6tables für den Nachfolger IPv6 und arptables. Bei ARP geht es um die Zuordnung von IP- zu Hardware-Adressen (MAC) der Netzwerkschnittstellen. Durch die getrennten Werkzeuge sind Teile des Programmcodes mehrfach vorhanden. Nftables ist eine Neuentwicklung, die ebenfalls auf Netfilter basiert – allerdings sämtliche Funktionen in einem einzigen Werkzeug namens nft bündelt. Das Projekt wurde als Nachfolger entwickelt und existiert seit 2009. Seit 2016 bietet nftables die gleichen Funktionen wie iptables, mittlerweile ist es sogar mächtiger. Lange Zeit existierten beide Projekte daher parallel und iptables war verbreiteter.

Das ändert sich zunehmend, da es eine Kompatibilitätsschicht gibt. Mithilfe von iptables-translate kann man zudem bestehende Regeln in nftables umwandeln lassen. Einige Linux-Distributionen sind bereits gewechselt oder haben dies angekündigt. Unter Debian (und damit auch dem Raspberry Pi OS) ist nftables seit Buster der neue Standard. Man kann mithilfe von update-alternatives zurück zum alten iptables wechseln:

sudo update-alternatives --set iptables /usr/sbin/iptables-legacy
# Oder zunächst nur einsehen, was als Standard für iptables konfiguriert ist
# Ein Verweis auf iptables-nft steht für nftables, "iptables" ohne nft = iptables
update-alternatives --display iptables

Dies funktioniert analog für ip6tables, arptables und ebtables. Allerdings ist iptables veraltet und wird daher voraussichtlich in einer der nächsten Hauptversionen entfernt. Zumindest mittelfristig sollte man daher zu nftables wechseln, wie es auch im Debian-Wiki empfohlen wird.

Worin unterscheiden sich iptables und nftables hauptsächlich?

Unter nftables werden sämtliche Regeln mit dem Kommandozeilenwerkzeug nft verwaltet. Ob es sich dabei um IPv4, V6 oder andere Protokolle wie ARP handelt, spielt keine Rolle. Es gibt keine getrennten Werkzeuge wie iptables, ip6tables etc. mehr.

Sowohl iptables als auch nftables arbeiten mit Tabellen und Ketten (chains). Während es bei iptables standardmäßig bestimmte Tabellen (z.B. Filter, NAT) und Ketten (INPUT, FORWARD, usw.) gab, ist dies bei nftables nicht der Fall. Dies wird mit der Leistung begründet, da man so nur jene Tabellen/Ketten anlegen kann, die auch wirklich benötigt werden.

Die Handhabung wurde in nftables an mehreren Stellen verbessert. Beispielsweise sind alle Regeln automatisch persistent. Manuelles importieren beim Serverstart mit iptables-save und iptables-restore entfällt.

Die Regeln sind flexibler geworden: Man kann mehrere Aktionen in einer einzelnen Regel angeben. Bei iptables war nur eine einzige (z.B. -j ACCEPT oder -j LOG) möglich. Wer mehrere Regeln erstellt, spart sich Wiederholungen durch den hierarchischen Aufbau.

Interessant ist ebenfalls die komfortable Möglichkeit, um Regeln in einer Datei angeben und laden zu können. Das vermeidet lange, unübersichtliche und im Zweifel undokumentierte Konsolenbefehle.

Einstieg in nftables

Ist nftables bei mir installiert?

Unter Debian (ab Buster), Ubuntu (ab 20.10), CentOS (ab 8) und Fedora (32) ist nftables bereits standardmäßig installiert. Unter anderen/älteren Distributionen muss es ggf. nachinstalliert werden, meist heißt das Paket dafür ebenfalls nftables.

Wenn ihr euch nicht sicher seid, könnt ihr prüfen, ob das Kernel-Modul nf_tables geladen ist:

$ sudo lsmod | egrep ^nf_tables
nf_tables             241664  1649 nft_reject_ipv4,nft_ct,nft_compat,nft_log,nft_nat,nft_fib_ipv4,nft_counter,nft_masq,nft_chain_nat,nft_reject,nft_fib

Eine Liste aller aktiven Regeln lässt sich mit folgendem Befehl anzeigen:

sudo nft list ruleset

In der Regel wird diese Liste leer sein. Außer ihr habt Software installiert, die eigene Regeln anlegt. Das ist beispielsweise bei Docker der Fall, um Funktionen wie Portfreigaben umzusetzen.

Befehle per CLI ausführen: Anlegen der ersten Tabelle, Kette und Regel

Man hat die Wahl, ob man einzelne Konsolenbefehle wie bei iptables verwendet – oder die Konfiguration in einer Datei speichert. Beginnen wir mit einer Tabelle, die eine Protokollfamilie festlegt. Hier wird inet verwendet für alle Internetprotokolle (IPv4 und der Nachfolger v6). Insgesamt sechs Familien existieren, man könnte z.B. auch ip angeben, um ausschließlich IPv4 Datenverkehr zu behandeln. Oder ip6 für V6. Zuletzt folgt der frei wählbare Name der Tabelle basic-filter:

sudo nft add table inet basic-filter

Innerhalb einer Tabelle können wir nun Ketten anlegen. Eine Kette hat einen bestimmten Typ und reagiert auf ein Ereignis. Wichtig: Ketten und Regeln erhalten Sonderzeichen wie z.B. geschweifte Klammern, die von der Shell (meist Bash) interpretiert werden! Ihr müsst diese daher entweder mit einem umgekehrten Schrägstrich \ escapen, etwa \{ statt { oder in einfachen Anführungsstrichen ‚ maskieren. Ich empfehle einfache Anführungsstriche. Doppelte funktionieren nicht, da Bash nur innerhalb einfacher Anführungszeichen nichts interpretiert. Die Anführungsstriche sollten nach dem nft Befehl beginnen.

sudo nft 'add chain inet basic-filter input { type filter hook input priority filter; ... }'

An der … Stelle würden nun die Regeln folgen, die wir uns im folgenden aber besser in einer eigenen Datei anschauen. Das ist deutlich komfortabler und übersichtlicher, als längere Konsolenbefehle zu nutzen.

Grundlegende Regeln als Datei

Dazu legen wir eine frei wählbare Datei (z.B. nft_basic_rules.conf) an und definieren die Regeln wie folgt:

#!/usr/sbin/nft -f

# Optional: Löscht alle möglicherweise bereits existierenden Regeln
# flush ruleset

table inet basic-filter {
        chain input {
                type filter hook input priority filter
                policy drop
                ct state { established, related } accept

                iifname lo accept
                ct state new tcp dport ssh log prefix "Neue SSH-Verbindung " accept
        }
}

Diese legt eine Tabelle und Kette (Chain) an, die auf eingehende IP-Pakete (V4 und V6) reagiert. Nur SSH und lokale Verbindungen auf der Loopback-Schnittstelle (127.0.0.1) wird erlaubt, alle anderen Pakete verworfen.

  • Mit flush ruleset werden alle möglicherweise bereits existierenden Regeln gelöscht. Dies ist Sinnvoll, wenn es keine weiteren Regeln gibt. So ist sichergestellt, dass nur die folgenden definierten vorhanden sind. Sollte allerdings nicht genutzt werden, falls es bereits wichtige Regeln gibt. Diese können auch automatisch von anderen Programmen generiert worden sein, ohne dass dies einem bewusst ist – beispielsweise Docker.
  • Da nftables wie schon iptables in Tabellen organisiert ist, legen wir zunächst eine an. Hier steht inet für die Protokollfamilie, d.H. IPv4 und IPv6. Der Name basic-filter ist frei wählbar, sollte aber möglichst aussagekräftig sein. Sehr generische Namen wie filter, nat etc. sollten nicht gewählt werden, hier kann es ggf. zu Konflikten kommen. Etwa derzeit mit KVM, dort wird derzeit noch eine Kompatibilitätsschicht verwendet, die mit nativen nftables Regeln nicht funktioniert.
  • Es folgt eine Kette, die ich (ebenfalls frei wählbar) input genannt habe. Es empfiehlt sich eine Benennung, die auf den Hook (siehe unten) schließen lässt.
  • filter ist einer von drei Typen. Wie der Name vermuten lässt, können wir damit Pakete anhand verschiedener Kriterien filtern. Dieser Typ ist am meisten verbreitet. Für andere Anwendungsfälle gibt es u.a. noch NAT.
  • hook gibt ein Ereignis von Netfilter an. Oder anders ausgedrückt die Stelle im Lebenslauf eines Pakets, an der diese Regel greift. Insgesamt stehen sechs Hooks zur Verfügung. Je nach verwendetem Hook erreichen möglicherweise nicht alle Pakete eine Regel. Mit input können wir uns sicher sein, dass die Pakete für unser System bestimmt sind.
  • Mit der Priorität (priority) lässt sich festlegen, welche Regel Vorrang hat. Dies ist eher für komplexere Regelsätze relevant, wir setzen daher hier den Standard für Filter.
  • Die Standardaktion policy drop verwirft alle Pakete, die nicht unseren folgenden Regeln entsprechen.
  • ct state steht für connection tracking und gibt den Zustand eines Paketes an. Established sind weitere Pakete einer Verbindung, die bereits berechtigt wurde. Related dagegen erwartete neue Verbindung einer bestehenden Hauptverbindung, wie etwa ein FTP-Datenkanal. Bei FTP gibt es einen Steuerkanal, der Meta-Informationen wie Verzeichnisinhalte überträgt. Soll eine Datei hoch- oder heruntergeladen werden, öffnet dieser Steuerkanal eine zweite Verbindung für den Inhalt. Diese zweite Verbindung wird hiermit erlaubt. Weitere Details zum Connection tracking finden sich hier.
  • Es folgt eine Regel, die per iifname nur auf die Loopback-Schnittstelle (lo ist der Standardname) angewendet wird. Sie erlaubt jeglichen Datenverkehr auf dieser Schnittstelle, die nur für lokale Verbindungen auf dem Server selbst genutzt wird.
  • Schlussendlich erlauben wir neue SSH-Verbindungen auf dem Standardport 22, da wir uns ansonsten selbst aussperren würden. Die Anweisung dport steht für destination port, also Zielport. Hier kann man auch eine beliebige Portnummer angeben, z.B. 81. Zusätzlich habe ich hier mit log die Protokollierung aktiviert, prefix schreibt die folgende Zeichenkette in das Log, damit wir sie wieder finden.
  • Ein weiterer Vorteil der Datei: Man erspart sich Wiederholungen. Auf der Konsole müsste man für die Kette (chain) beispielsweise immer Name und Typ der Tabelle angeben. Da die Datei hierarchisch aufgebaut ist, entfällt dies dort.

Laden kann man die Regeln aus der angelegten Datei mit dem -t Schalter. Mit list ruleset lässt sich sicherstellen, dass diese tatsächlich aktiv angewendet werden:

$ sudo nft -f nft_basic_rules.conf
$ sudo nft list ruleset
table ip basic-filter {
	chain input {
		type filter hook input priority filter; policy drop;
		ct state { established, related } accept
		iifname "lo" accept comment "allow from loopback"
		ct state new tcp dport 22 log prefix "Neue SSH-Verbindung " accept
	}
}

Einsehen der Protokolle: Welche Regeln wurde wann angewendet?

Bei der Erlaubnisregel für SSH haben wir die Protokollierung aktiviert. Dies ist vor allem bei Tests hilfreich. Im Protokoll des Kernels (/var/log/kern.log) lassen sich diese Einträge auslesen. Am einfachsten geht das mit dem Werkzeug dmesg. Der Schalter –follow folgt dem Protokoll, sodass neue Einträge in Echtzeit angezeigt werden:

sudo dmesg --follow

Für eine neue SSH-Verbindung erscheint dort beispielsweise folgender Eintrag:

[  441.703526] Neue SSH-Verbindung IN=eth0 OUT= MAC=... SRC=... DST=... LEN=60 TOS=0x00 PREC=0x00 TTL=46 ID=24203 DF PROTO=TCP SPT=50492 DPT=22 WINDOW=29200 RES=0x00 SYN URGP=0

Aus Leistungsgründen ist zu empfehlen, die Protokollierung abzuschalten, wenn sie nicht (mehr) benötigt wird. Weitere Informationen finden sich im nftables Wiki zur Protokollierung (englisch).

Automatisches Laden der Regeln beim Systemstart

Damit die gespeicherten Regeln beim Start automatisch geladen werden, muss der Dienst nftables aktiv sein. Standardmäßig ist dies bei Ubuntu 22.04 LTS nicht der Fall (Active: inactive):

$ sudo systemctl status nftables
○ nftables.service - nftables
     Loaded: loaded (/lib/systemd/system/nftables.service; disabled; vendor preset: enabled)
     Active: inactive (dead)

In diesem Falle den Dienst aktivieren:

sudo systemctl enable nftables

Der Dienst lädt alle Regeln aus der Datei /etc/nftables.conf. Wenn ihr eure Regeln in einer lokalen Datei abgespeichert habt wie im vorherigen Schritt gezeigt, könnt ihr diese dort hin kopieren:

sudo cp nft_basic_rules.conf /etc/nftables.conf

Sind allerdings noch andere Regeln vorhanden, weil ihr z.B. zuvor bereits händische per CLI angelegt habt, empfiehlt es sich, die aktiven zu exportieren:

sudo nft list ruleset | sudo tee /etc/nftables.conf

Gibt es Alternative Firewalls?

Sowohl iptables als auch nftables sind Low-Level Werkzeuge, d.H. recht nah am Kernel und damit an der Hardware. Das ist perfekt zum lernen oder wenn man möglichst wenige Tools einsetzen bzw. möglichst viel Kontrolle haben möchte. Dafür bringt es aber relativ viel Komplexität mit, vor allem iptables. Alternativ kann man daher Firewalls nutzen, die wiederum auf nftables/iptables aufbauen. Unter Ubuntu/Debian ist beispielsweise ufw (uncomplicated firewall, auf Deutsch: Unkomplizierte Firewall) verbreitet und standardmäßig aktiv:

$ sudo systemctl status ufw
● ufw.service - Uncomplicated firewall
     Loaded: loaded (/lib/systemd/system/ufw.service; enabled; vendor preset: enabled)
     Active: active (exited) since Tue 2022-05-24 12:12:44 CEST; 4 days ago

Dort kann man beispielsweise den SSH-Zugriff mit folgendem Befehl vergleichsweise einfach erlauben:

sudo ufw allow ssh

In der Red-Hat/CentOS und Suse-Szene ist dagegen firewalld die Standard-Firewall. Während diese Werkzeuge alle konsolenbasiert sind, gibt es teils auch grafische Oberflächen zur Verwaltung.

Übersicht der wichtigsten Befehle (Cheatsheet)

Nftables muss grundsätzlich als root (su/sudo) ausgeführt werden. Der Aufbau der Befehle kann auch für andere Typen übernommen werden.

# Alle Regeln löschen
nft flush ruleset
# Nur alle Regeln innerhalb der Tabelle basic-filter löschen
nft flush table basic-filter
# Löschen der Tabelle selbst
nft delete table basic-filter
# Kette (Chain) leeren
nft flush chain basic-filter
# Löschen der Kette input in der Tabelle basic-filter
nft delete chain basic-filter input

# Löschen einer spezifischen Regel: Zunächst brauchst du das Handle (Nummer am Ende der Zeile)
# basic-filter ist der Name der Tabelle, inet dessen Protokollfamilie
sudo nft -a list table inet basic-filter
# Löscht Regel mit dem Handle 5 (Beispiel, Nummer s.o.) aus der Kette input in der Tabelle basic-filter
sudo nft delete rule inet basic-filter input handle 5

# Sämtliche Regeln aus der angegebenen Datei laden
nft -f my_rules.conf
# Alle derzeit aktiven Regeln auflisten
nft list ruleset

Fazit und Erweiterungen

Nftables optimiert einiges, was bei iptables über die Jahre historisch gewachsen ist, wie man so schön sagt. Dieser Beitrag dient als Einstieg für Neulinge und zeigt nur eine sehr einfache Konfiguration als grundlegendes Beispiel. Man kann jedoch noch deutlich mehr damit machen, daher wird man die Regeln in der Praxis erweitern. Vom komplexeren Filterregeln bis hin zu NAT ist vieles möglich.

NAT (Network Adress Translation) ist in Form von Destination-NAT z.B. dann nützlich, wenn man Ports nach außen hin auf andere Ports mappen möchte. Etwa eine Anwendung die auf Port 8080 läuft auf Port 80 erreichbar machen. Oder mehrere VMs auf dem Server laufen, von denen man gezielt einzelne Ports ins Internet freigeben möchte (z.B. Port 80/443 auf Webserver-VM1, IMAP-Ports auf Mailserver-VM2 usw).

Weiterführende Informationen

Leave a Reply