
đ Auch auf: English · Français · Español
Kurzes Repo-Update: Das GitHub-Repo www_k3s hat gerade eine ordentliche Firewall-GeneralĂŒberholung bekommen. Das alte Setup nutzte klassisches iptables â hat gut funktioniert, fĂŒhlte sich aber langsam an wie ein Oldtimer mit Choke und Zwischengas. Zeit fĂŒr ein Upgrade. Die Firewall ist jetzt komplett in nftables neu geschrieben, dem modernen Nachfolger, der seit Kernel 3.13 als Linux-Standard ausgeliefert wird. Hier erklĂ€re ich, warum das die richtige Entscheidung ist, und gehe das neue Regelwerk komplett durch. đ§”
⥠nftables vs. iptables â warum das wichtig ist
iptables ist seit Ende der 90er der Standard-Paketfilter unter Linux. Es funktioniert, ist praxiserprobt, und ungefÀhr eine Milliarde Stack-Overflow-Antworten beziehen sich darauf. Aber man merkt ihm sein Alter an:
- Keine atomaren Regel-Updates â Ănderungen werden Zeile fĂŒr Zeile angewendet, es gibt also ein kurzes Zeitfenster, in dem dein Regelwerk nur halb aktiv ist
- Getrennte Tools fĂŒr IPv4/IPv6 â iptables vs. ip6tables, zwei getrennte Regelspeicher, die man synchron halten muss
- Keine nativen Sets â IP-Blocklisten brauchen Zusatztools wie ipset
- Performance â Regeln werden linear abgearbeitet; je mehr Regeln, desto langsamer
nftables (eingefĂŒhrt mit Linux 3.13, Standard auf den meisten Distributionen seit ca. 2019) löst all das:
- â
Ein einziges Regelwerk fĂŒr IPv4 + IPv6 (
table inet) - â Atomarer Austausch der Regeln â das komplette Regelwerk wird mit einem einzigen Syscall ersetzt
- â Native Sets mit Timeout-UnterstĂŒtzung (eingebaute IP-Blocklisten, kein ipset nötig)
- â Bessere Performance dank einer JIT-kompilierten virtuellen Maschine im Kernel
Unter AlmaLinux 9 ist nftables der Systemstandard. Der Befehl iptables ist dort buchstĂ€blich nur eine KompatibilitĂ€tsschicht ĂŒber nftables. Wer 2025 also noch echte iptables-Syntax schreibt, schreibt iptables-Regeln, die sowieso in nftables-Regeln ĂŒbersetzt werden â und verzichtet dabei nur auf die ganze schöne Syntax. đ
đ Das Regelwerk â Abschnitt fĂŒr Abschnitt
Die Firewall ist ein Ansible-Jinja2-Template (roles/common_firewall/templates/fw.nft.j2), das pro Host gerendert und ĂŒber nftables angewendet wird. Gehen wir es durch.
đïž Die Blocklisten-Sets
set banned4 {
type ipv4_addr
flags timeout
timeout 1d
}
set banned6 {
type ipv6_addr
flags timeout
timeout 1d
}Zwei Sets â eins fĂŒr IPv4, eins fĂŒr IPv6 â, die als dynamische Blockliste dienen. flags timeout + timeout 1d bedeutet, dass jeder Eintrag nach 24 Stunden automatisch verfĂ€llt. fail2ban schreibt in diese Sets, sobald es Brute-Force- oder Portscan-AktivitĂ€t erkennt. Kein Cronjob nötig, um alte Sperren aufzurĂ€umen. đ§č
đ Chain: INPUT â das Hauptprogramm
Die Standard-Policy ist DROP â alles, was nicht ausdrĂŒcklich erlaubt ist, wird stillschweigend verworfen.
iif lo acceptLoopback-Traffic (127.0.0.1, ::1) wird immer akzeptiert. Die gesamte hostinterne Kommunikation â kubectl, kubelet, Prometheus-Scraping, Datenbank-Sockets â lĂ€uft hierĂŒber.
ct state established,related accept
ct state invalid dropDas Connection Tracking erledigt die Schwerstarbeit. Bereits bestehende Verbindungen werden akzeptiert, ohne weitere Regeln zu prĂŒfen. Pakete mit ungĂŒltigem Zustand (z. B. TCP-Pakete, die zu keiner bekannten Verbindung gehören) werden sofort verworfen.
tcp flags & (fin|syn|rst|psh|ack|urg) == fin|syn|rst|psh|ack|urg drop # XMAS
tcp flags & (fin|syn|rst|psh|ack|urg) == 0x0 drop # NULLDer Klassiker: kaputte Pakete wegwerfen. Bei XMAS-Paketen sind alle TCP-Flags gleichzeitig gesetzt â in echtem Traffic unmöglich, genutzt von Portscannern (nmap -sX). NULL-Pakete haben ĂŒberhaupt keine Flags â gleiche Geschichte. Beides sind Tricks zum Fingerprinting bzw. zum Umgehen von Filtern. Weg damit. đđ«
ct state new tcp flags & (syn|ack) == syn|ack reject with tcp resetAnti-Spoofing: Ein SYN,ACK, das als neue Verbindung ankommt, heiĂt, dass jemand aus dem Nichts eine Antwort auf einen TCP-Handshake fĂ€lscht. Wird mit einem TCP-RST abgewiesen.
tcp flags & (syn|ack|fin|rst) == rst limit rate 1/second accept
tcp flags & (syn|ack|fin|rst) == rst dropSchutz vor RST-Floods. RST-Pakete sind legitim (damit werden Verbindungen abgebaut), aber eine Flut davon ist eine DoS-Technik. Höchstens eins pro Sekunde erlauben, den Rest verwerfen.
ip saddr @banned4 drop
ip6 saddr @banned6 dropPrĂŒfung der dynamischen Blocklisten-Sets von fail2ban. Sie stehen vor den K3s-Ausnahmeregeln, damit sich eine gesperrte IP niemals ĂŒber die Pod-CIDR-Ausnahmen weiter unten durchschleichen kann.
{% if 'nextcloud' in group_names %}
ip saddr {{ k3s_pod_cidr }} tcp dport 3306 accept
ip saddr {{ k3s_pod_cidr }} tcp dport 6379 accept
{% endif %}Eine Jinja2-Bedingung â dieser Block taucht nur auf Nextcloud-Hosts auf. Auf den Nextcloud-Servern laufen MariaDB und Redis als Host-Dienste (nicht in K3s), also mĂŒssen Pods sie ĂŒber die Host-IP erreichen. Auf WordPress-Hosts lĂ€uft MariaDB als Pod im Cluster, daher sind diese Regeln ĂŒberflĂŒssig und fallen weg. đ§
ip saddr {{ server_ipv4 }} tcp dport { 3000, 10254 } acceptingress-nginx lĂ€uft mit hostNetwork: true, lebt also im Netzwerk-Namespace des Hosts. Wenn es eine Anfrage ĂŒber einen Kubernetes-ExternalService an Grafana (Port 3000) weiterleitet, ist die Quell-IP die eigene öffentliche IP des Hosts â keine Pod-IP. Dasselbe gilt fĂŒr die Health-Probes des kubelet auf Port 10254. Also erlauben wir die eigene IP des Hosts, aber nur fĂŒr genau diese zwei Ports â kein pauschales Accept. đŻ
ip saddr {
0.0.0.0/8, 10.0.0.x/8, 127.0.0.0/8, 169.254.0.0/16,
172.16.0.x/12, 192.168.0.x/16, 224.0.0.0/4, 240.0.0.0/5
} dropAlle Quelladressen aus RFC-1918- und reservierten Bereichen, die auf dem externen Interface ankommen, werden verworfen. Wenn ein Paket aus dem öffentlichen Internet behauptet, von 10.x.x.x oder 192.168.x.x zu kommen, ist es gefÀlscht. Die K3s-Pod-CIDR-Ausnahmen oben stehen genau deshalb vor diesem Block, damit legitimer Pod-Traffic hier nicht hÀngen bleibt.
icmp type { destination-unreachable, echo-request, time-exceeded, parameter-problem } acceptNur die ICMP-Typen erlauben, die wir wirklich brauchen: Ping, Traceroute und Meldungen zu nicht erreichbaren Netzen. Alles andere (Redirect, Router-Advertisement usw.) wird verworfen.
icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem,
router-advertisement, router-solicitation,
nd-neighbor-solicitation, nd-neighbor-advertisement,
echo-request, echo-reply } acceptICMPv6 braucht eine lĂ€ngere Allowlist, weil IPv6 grundlegend darauf angewiesen ist. Das Neighbor Discovery Protocol (NDP) â das IPv6-GegenstĂŒck zu ARP â lĂ€uft ĂŒber ICMPv6. Ohne diese Typen funktioniert IPv6 schlicht nicht.
ip daddr {{ server_ipv4 }} tcp dport { 80, 443 } ct state new accept
ip daddr {{ server_ipv4 }} udp dport 443 ct state new accept
ip6 daddr {{ server_ipv6 }} tcp dport { 80, 443 } accept
ip6 daddr {{ server_ipv6 }} udp dport 443 acceptHTTP, HTTPS und UDP/443 (QUIC/HTTP3) fĂŒr den Webserver öffnen. FĂŒr IPv4 und IPv6. Die UDP-Regel ist es, die HTTP/3 ermöglicht â Browser bekommen es per Alt-Svc angekĂŒndigt und wechseln dann bei folgenden Verbindungen darauf. đ
ip daddr {{ server_ipv4 }} tcp dport 10022 ct state new acceptSSH auf einem Nicht-Standard-Port. Port 22 ist inzwischen nur noch Rauschen.
limit rate 2/minute log prefix "nftables DROP IN: " level warn flags allRate-limitiertes Logging von allem Ăbrigen vor dem Drop durch die Policy. So siehst du, was geblockt wird, ohne das Kernel-Log zu fluten. Mit dem PrĂ€fix lĂ€sst es sich im journald leicht greppen.
đ Chain: FORWARD
K3s verwaltet seine eigenen KUBE-*-Chains fĂŒr das Service-Routing â diese Chain muss nur Pod-zu-Pod- und Pod-zu-Service-Traffic innerhalb der Pod-CIDR durchlassen und alles andere blocken. Die Blocklisten-Sets werden auch hier geprĂŒft, damit gesperrte IPs weitergeleiteten Traffic nicht ausnutzen können.
đ€ Chain: OUTPUT
Auch ausgehender Traffic ist per Standard-DROP eingeschrĂ€nkt. Erlaubt sind: DNS, NTP, HTTP/HTTPS fĂŒr Paket-Downloads und die ACME-Zertifikatserneuerung, SMTP fĂŒr den Mailversand, der K3s-API-Server auf 6443 und ausgehendes QUIC. Alles andere â auch zufĂ€llige ausgehende Verbindungen, die Malware versuchen könnte â wird verworfen und geloggt.
đ Fazit
Das nftables-Regelwerk leistet eine Menge in relativ wenigen Zeilen. Eine einzige Datei, Dual-Stack IPv4/IPv6, native dynamische Blocklisten, Connection Tracking und Kubernetes-taugliche Ausnahmen â alles in einer atomaren Einheit, die bei jedem Ansible-Lauf sauber angewendet wird. Genau bei so etwas lernt man Infrastructure as Code zu schĂ€tzen. đ€
Das Repo findest du unter github.com/aptupgrademe/www_k3s, falls du tiefer einsteigen willst.




