nftables ist seit Debian 10 und RHEL 8 der Standard-Paketfilter unter Linux und löst iptables ab. Die Umstellung lohnt sich: eine Syntax statt drei Werkzeuge (iptables, ip6tables, arptables), echte Mengen (Sets und Maps), und Regeln, die sich in einem Durchlauf auf den Zustand beziehen.
Warum nftables statt iptables
iptables nftables
--------------------------------------------------------------
Ein Befehl je Adressfamilie eine Familie: ip, ip6, inet
Regeln werden nacheinander Regeln in Ketten, Sprung
geprueft (je Kette ein Durchlauf) ueber Ausdruecke
Kein Mengentyp Sets und Maps (Netze, Adressen,
Ports) - extrem effizient
ipset als Zusatz noetig Mengen eingebaut
"save/restore" zum Sichern Regeln als eine Textdatei
Atomarer Wechsel kaum moeglich atomarer Wechsel per "nft -f"
Beim Umstieg: iptables-nft ist ein Uebersetzungswerkzeug.
Bestehende iptables-Regeln laufen also weiter, werden aber
intern als nftables-Regeln gefuehrt. So kann man schrittweise
umstellen.
Grundlagen: Tabellen, Ketten, Regeln
# Die drei wichtigen Tabellen
# filter Standard fuer Ein-/Aus-/Weiterleitung
# nat Adressumsetzung
# mangle Paketkopfzeilen veraendern (selten)
#
# Die Standardketten in filter:
# input an den eigenen Rechner
# forward durch den Rechner hindurch (Routing!)
# output von der eigenen Anwendung nach draussen
#
# Die Grundregel: Eine Kette entscheidet ueber "accept", "drop"
# oder "jump" in eine andere Kette. Ein "drop" verwirft ohne
# Nachricht, ein "reject" schickt eine Ablehnung.
# Was ist geladen und aktiv?
nft list ruleset
# Regeln in einer Tabelle
nft list table inet firewall
# Zaehler zuruecksetzen (nuetzlich bei der Fehlersuche)
nft reset counters
# Alles loeschen (Vorsicht - erst nach Test der neuen Regeln!)
nft flush ruleset
Eine vollständige Konfiguration
# /etc/nftables.conf
#!/usr/sbin/nft -f
flush ruleset
# Benannte Mengen: einmal definieren, mehrfach nutzen
define aussen = { eth0 }
define intern_netze = { 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16 }
define admin_hosts = { 10.0.0.5, 10.0.0.6, 10.0.0.7 }
define dienst_ports = { 22, 80, 443, 8443 }
define web_ports = { 80, 443 }
define mail_ports = { 25, 465, 587, 993 }
table inet firewall {
# ---- Eingang -------------------------------------------------
chain input {
type filter hook input priority 0; policy drop;
# Bereits bestehende Verbindungen weiter durchlassen
# Ohne diese Zeile bricht jede Antwort ab.
ct state established,related accept
# Ungueltige Pakete verwerfen
ct state invalid drop
# Loopback immer erlauben
iif lo accept
# ICMPv6 nicht komplett blockieren - sonst bricht IPv6
# (Nachbarschaftserkennung, Pfad-MTU). Nur begrenzen.
ip6 nexthdr icmpv6 icmpv6 type { nd-neighbor-solicit,
nd-neighbor-advert, nd-router-advert, packet-too-big } accept
ip protocol icmp icmp type { echo-request, echo-reply,
destination-unreachable, time-exceeded } limit rate 10/second accept
# Verfolgte Protokolle (FTP, SIP) - nur falls gebraucht
# ct helper "ftp" ...
# Dienstports oeffentlich
tcp dport $web_ports ct state new accept
tcp dport 22 ct state new ip saddr $admin_hosts accept
# Mailports nur auf dem Mailserver
tcp dport $mail_ports ct state new accept
# Alles andere protokollieren und verwerfen
limit rate 5/minute log prefix "nft-drop-input: " level warn
counter drop
}
# ---- Weiterleitung (Router, Container) -----------------------
chain forward {
type filter hook forward priority 0; policy drop;
ct state established,related accept
ct state invalid drop
# Nur ausgewaehlte Netze duerfen weiterleiten
ip saddr 10.10.0.0/24 ip daddr 10.20.0.0/24 accept
ip saddr 10.10.0.0/24 oifname "eth0" accept
log prefix "nft-drop-forward: " level warn limit rate 5/minute
counter drop
}
# ---- Ausgang -------------------------------------------------
chain output {
type filter hook output priority 0; policy accept;
# Ausgangskontrolle: Nur wenn verlangt!
# Ein "policy drop" hier ist riskant - erst protokollieren.
# tcp dport { 25, 465, 587 } ip saddr != 10.0.0.20 drop
# DNS nur zum eigenen Resolver
# udp dport 53 ip daddr != 10.0.0.53 drop
}
}
# ---- NAT (nur auf Routern) ---------------------------------------
table ip nat {
chain prerouting {
type nat hook prerouting priority -100; policy accept;
# Portweiterleitung nach innen
# Port 8080 von aussen -> 10.0.0.11:80 innen
iifname "eth0" tcp dport 8080 dnat to 10.0.0.11:80
# Zieladresse in Abhaengigkeit vom Port (Map!)
# Die Map ersetzt lange if-Ketten.
# dnat to tcp dport map {
# 80 : 10.0.0.11:80,
# 443 : 10.0.0.11:443,
# 8080 : 10.0.0.12:8080,
# }
}
chain postrouting {
type nat hook postrouting priority 100; policy accept;
# Quelladressumsetzung fuer ausgehenden Verkehr
oifname "eth0" masquerade
# Alternative fuer feste Adresse (schneller als masquerade)
# oifname "eth0" ip saddr 10.0.0.0/24 snat to 203.0.113.7
}
}
# ---- Mengen mit Zeitablauf (praktisch fuer Sperrlisten) ---------
table ip sperren {
set zu_oft {
type ipv4_addr
flags dynamic, timeout # dynamisch befuellbar, laeuft ab
timeout 1h
}
chain eingang {
type filter hook input priority -10; policy accept;
# Adressen in der Menge werden verworfen
ip saddr @zu_oft drop
# Ratenbegrenzung: ueber 100 neue Verbindungen pro
# Minute -> in die Menge aufnehmen
tcp dport { 22, 80, 443 } ct state new \
meter ratenlimit { ip saddr limit rate over 100/minute } \
add @zu_oft { ip saddr }
}
}
Regeln setzen und prüfen
# Konfiguration pruefen und laden - ATOMAR
nft -c -f /etc/nftables.conf # -c = Syntaxpruefung, nichts anwenden
nft -f /etc/nftables.conf # anwenden (ersetzt alles)
# WICHTIG: Bei "policy drop" fuer input niemals aus einer
# SSH-Sitzung heraus arbeiten, ohne eine Notfallregel
# bereitzuhalten. Zweite Sitzung offen halten!
#
# Notfall (falls man sich ausgesperrt hat):
# nft insert rule inet firewall input tcp dport 22 accept
# nft flush chain inet firewall input
# Einzelne Regel anhaengen
nft add rule inet firewall input tcp dport 8443 ct state new accept
# Regel mit Handle anzeigen und loeschen
nft -a list chain inet firewall input
nft delete rule inet firewall input handle 12
# Zaehler auswerten: Welche Regel greift am haeufigsten?
nft list ruleset | grep -A2 counter
# Logik testen
nft monitor trace # Regelverfolgung (dann Regeln mit "trace")
journalctl -k -f # Kernelmeldungen der Regeln
nft list ruleset > /etc/nftables.conf # aktuelle Regeln sichern
# Automatisch laden beim Start
systemctl enable nftables
systemctl reload nftables
Prüfliste
- Ist
ct state established,related acceptvorhanden (ohne das bricht die Antwort)? - Sind ICMPv4/ICMPv6 nicht komplett blockiert (Path-MTU, IPv6-Nachbarschaft)?
- Ist die Grundregel drop statt reject, wo keine Auskunft nötig ist?
- Ist die Protokollierung mit
limit ratebegrenzt (sonst flutet ein Angriff die Protokolle)? - Sind Zähler aktiv, um zu sehen, welche Regeln greifen?
- Ist ein Testpfad für den Aussperrfall dokumentiert und erreichbar?
- Ist
nft -c -fin der Änderungsschleife? - Sind die Regeln in
/etc/nftables.confgespeichert (sonst nach dem Neustart verloren)? - Sind die Mengen mit
timeoutversehen, damit die Tabellen nicht unbegrenzt wachsen? - Ist die Weiterleitungskette korrekt (
policy drop), wenn der Rechner als Router dient?
Häufige Fehler
- Ausgesperrt. Regeln mit
policy dropfürinputohne Ausweichpfad laden. Immer eine zweite Sitzung offen halten und Regeln in einer zeitlich begrenzten Warteschleife laden (nft -f regeln && sleep 60 && ...). establishedvergessen. Alles eingehend wird verworfen, auch Antworten auf eigene Anfragen.- ICMP gesperrt. Path-MTU-Erkennung bricht, IPv6 funktioniert gar nicht. Verbindungen hängen bei großen Übertragungen.
- Regeln nur im Arbeitsspeicher. Nach dem Neustart ist alles weg.
/etc/nftables.confschreiben und den Dienst aktivieren. - Protokollierung ohne Limit. Ein Port-Scan füllt die Festplatte in Minuten.
- Forward-Kette nicht beachtet. Bei Containern und Routern wird forward benötigt –
inputallein reicht nicht. - NAT und Filter vermischt. NAT wird vor dem Filter ausgewertet (Priorität -100). Wer im Filter Zieladressen nach NAT prüft, prüft die falschen Werte (dafür
ct originalnutzen). - Endlos wachsende Mengen. Ohne
timeoutfüllt eine Sperrliste den Speicher.
Verwandte Grundlagen: Firewall, NAT und Portweiterleitung, Fail2ban, SSH.