Firewalls mit nftables

Erinnerung – „Datenpaket“: Daten werden im Netz nicht am Stück übertragen, sondern in kleine Stücke zerlegt. Jedes Stück – ein Datenpaket – bekommt einen Kopf (Header) mit Zusatzinfos wie Quell-/Ziel-IP und Port (vgl. Netzwerk-Grundlagen).

Eine Firewall ist ein Filter zwischen Netzen (oder direkt vor einem einzelnen Rechner): Sie entscheidet anhand von Regeln, welche Datenpakete durchgelassen und welche blockiert werden.

Dieses Kapitel baut direkt auf den Netzwerk-Grundlagen auf. Falls Begriffe wie Port, IP-Adresse, TCP/UDP, CIDR (/16) oder NAT unklar sind, lohnt ein Blick zurück in Computernetze - Einführung.

Lernziele. Nach diesem Kapitel solltest du

  • erklären können, was eine Firewall tut und wonach sie filtert (IP, Port, Protokoll, Richtung);
  • das Prinzip Default-Deny (Whitelist) verstehen;
  • den Unterschied zwischen stateless und stateful Filtern kennen;
  • Host- von Netz-Firewalls unterscheiden;
  • eine einfache nftables-Regel lesen können - am Beispiel des IaC-Belegs.

Motivation: Warum eine Firewall?

Kurz erklärt – was ist ein Port? Eine IP-Adresse benennt den Rechner im Netz; ein Port (eine Zahl von 0 bis 65535) benennt den Dienst auf diesem Rechner. So können auf einer IP viele Dienste gleichzeitig laufen, jeder auf seinem eigenen Port. Ein Datenpaket trägt darum neben der Ziel-IP auch einen Ziel-Port, der angibt, welcher Dienst gemeint ist.

Sobald ein Rechner Dienste im Netz anbietet, ist er über seine Ports erreichbar (z. B. Port 80/443 für einen Webserver, 22 für SSH). Aus den Netzwerk-Grundlagen wissen wir: Ein Datenpaket trägt u. a. eine Ziel-IP und einen Ziel-Port.

Das Problem: Standardmäßig kann jeder aus dem Internet versuchen, jeden offenen Port zu erreichen. Läuft auf dem Server z. B. eine Datenbank oder ein Admin-Zugang, soll dieser aber keinesfalls für die ganze Welt offen sein.

Erinnerung – „Verbindung“: Zwei Rechner, die über TCP kommunizieren, bauen dafür zuerst eine Verbindung auf (Connection-Request / -Reply), tauschen dann Daten aus und beenden sie wieder – siehe Netzwerk-Grundlagen.

Eine Firewall schränkt das gezielt ein: Sie lässt nur die gewollten Verbindungen zu und blockiert den Rest - noch bevor das Paket die eigentliche Anwendung erreicht.

Was ist eine Firewall?

Eine Firewall ist ein Paketfilter. Für jedes Datenpaket, das sie passieren will, prüft sie eine Liste von Regeln und trifft eine Entscheidung:

  • ACCEPT - das Paket darf passieren,
  • DROP - das Paket wird verworfen (der Absender bekommt keine Antwort),
  • REJECT - das Paket wird abgelehnt, der Absender bekommt eine Fehlermeldung zurück.

Die Regeln werden der Reihe nach geprüft; die erste passende Regel entscheidet. Passt keine Regel, greift die Default-Policy (siehe unten).

Wonach wird gefiltert?

Eine Firewall trifft ihre Entscheidung anhand der Metadaten eines Pakets - also genau der Felder, die wir aus dem Schichtenmodell kennen:

Kriterium Schicht Beispiel
Quell-/Ziel-IP-Adresse Internetschicht (IP) nur Pakete aus 141.45.0.0/16
Protokoll Transportschicht TCP oder UDP
Quell-/Ziel-Port Transportschicht Ziel-Port 8080 (Webserver)
Richtung - eingehend (input) vs. ausgehend (output)

So lässt sich z. B. die Regel formulieren: "Erlaube eingehende TCP-Pakete mit Ziel-Port 8080, aber nur, wenn die Quell-IP aus dem HTW-Netz stammt."

Hinweis: Ein klassischer Paketfilter schaut nur auf diese Kopf-Informationen (Header), nicht in den Inhalt der Pakete. Firewalls, die auch den Anwendungsinhalt prüfen (z. B. HTTP-Inhalte), nennt man Application-Level-Firewalls bzw. Proxies - das ist hier nicht das Thema.

Regeln und Default-Policy

Passt auf ein Paket keine einzige Regel, muss die Firewall trotzdem entscheiden. Das regelt die Default-Policy. Es gibt zwei grundsätzliche Strategien:

  • Blacklist (Default-Allow): Alles ist erlaubt, außer dem, was eine Regel ausdrücklich verbietet. Problem: Man muss an jede Gefahr denken - vergisst man eine, bleibt sie offen.
  • Whitelist (Default-Deny): Alles ist verboten, außer dem, was eine Regel ausdrücklich erlaubt. Vergisst man etwas, ist es im Zweifel zu, nicht offen.

Merksatz - Default-Deny: Sichere Firewalls arbeiten nach dem Whitelist-Prinzip: erst alles blockieren, dann gezielt einzelne Dinge freischalten. Das ist das Standard-Vorgehen und auch die Idee hinter dem Beleg-Beispiel weiter unten.

Stateless vs. stateful

Wenn ein Rechner eine Webseite abruft, baut er (per TCP, vgl. Netzwerk-Grundlagen) eine Verbindung zum Server auf und erwartet eine Antwort. Eine Firewall muss diese Antwort wieder hereinlassen - sonst käme nie eine Webseite an.

  • Eine stateless (zustandslose) Firewall betrachtet jedes Paket einzeln, ohne Zusammenhang. Für Antwortpakete bräuchte man dann zusätzliche, oft unsaubere Regeln.
  • Eine stateful (zustandsbehaftete) Firewall merkt sich aufgebaute Verbindungen. Antwortpakete zu einer bereits erlaubten Verbindung erkennt sie automatisch und lässt sie durch (Zustand established, related), ohne dass man dafür eine eigene Öffnungsregel braucht.

Moderne Firewalls (auch nftables) arbeiten in der Regel stateful. Das macht die Regelwerke kürzer und sicherer.

Wo sitzt eine Firewall?

  • Host-Firewall: läuft auf dem zu schützenden Rechner selbst und filtert dessen ein- und ausgehenden Verkehr. Unter Linux steckt sie direkt im Kernel (siehe unten). Genau das nutzen wir im Beleg.
  • Netz-Firewall: ein eigenes Gerät (oder ein Router) am Übergang zwischen zwei Netzen - z. B. zwischen dem Heim-/Firmennetz und dem Internet. Sie schützt alle dahinterliegenden Rechner gemeinsam.

Aus den Netzwerk-Grundlagen kennen wir bereits einen solchen Übergangspunkt: den Router / das Gateway, das per NAT das lokale Netz mit dem Internet verbindet. Häufig ist die Firewall genau dort angesiedelt. NAT selbst wirkt schon wie ein einfacher Schutz: Rechner mit privaten IPs (10.x, 192.168.x, ...) sind von außen nicht direkt erreichbar.

Firewalls unter Linux

Die eigentliche Filterung steckt im Linux-Kernel - im Subsystem netfilter. Die bekannten Kommandozeilen-Werkzeuge konfigurieren nur die Regeln; filtern tut der Kernel selbst.

Werkzeug Rolle
nftables (Befehl /usr/sbin/nft) aktueller Standard, löst iptables ab - wir verwenden es hier
iptables klassisches, älteres Werkzeug; sehr verbreitet, wird aber abgelöst
ufw Uncomplicated Firewall: einfaches Frontend, das im Hintergrund Regeln erzeugt

Im Folgenden betrachten wir nftables.

Beispiel:

Im Beleg läuft auf einer VM ein Web-Analytics-Stack. Zwei Ports sollen von außen erreichbar sein:

  • 8080 - die Webseite (Nginx),
  • 3000 - das Analyse-Werkzeug Umami.

Aber: erreichbar nur aus den HTW-Netzen, nicht aus dem ganzen Internet. In der nftables-Konfiguration (/etc/nftables.conf) steht dafür im chain input-Block die Regel:

ip saddr { 10.4.0.0/16, 141.45.0.0/16 } tcp dport { 3000, 8080 } counter accept

Stück für Stück gelesen:

Teil Bedeutung
ip saddr { ... } Quell-IP-Adresse (source address) muss in einem der Bereiche liegen
10.4.0.0/16, 141.45.0.0/16 zwei Netze in CIDR-Notation - die HTW-Adressbereiche
tcp dport { 3000, 8080 } Ziel-Port (destination port) ist 3000 oder 8080, Protokoll TCP
counter zählt mit, wie viele Pakete die Regel treffen (nur Statistik)
accept passende Pakete werden durchgelassen

Damit greifen alle vier Filterkriterien von oben zusammen: Quell-IP (saddr), Protokoll (tcp), Ziel-Port (dport) und - durch die input-Chain - die Richtung (eingehend).

Entscheidend ist das Zusammenspiel mit der Default-Policy: Die input-Chain blockiert standardmäßig alles (Default-Deny). Diese eine Regel öffnet gezielt nur die beiden Ports und nur für die HTW-Netze. Ein Zugriff aus einem anderen Netz passt nicht auf die Regel und wird daher verworfen.

Hands-on: bestehende Regeln ansehen

Das Anzeigen der aktiven Regeln ist ungefährlich (es verändert nichts). Das Setzen von Regeln erfordert dagegen Root-Rechte und kann - falsch gemacht - den eigenen (SSH-)Zugang aussperren. Daher hier nur lesende Befehle; Änderungen zeigen wir als Code-Schnipsel, ohne sie auszuführen.

In [1]:
# Aktuelles nftables-Regelwerk anzeigen (benötigt i. d. R. root, also 'sudo')
/usr/sbin/nft list ruleset 2>/dev/null || echo "nftables nicht verfügbar oder keine Rechte (mit 'sudo' erneut versuchen)"

# Fallback: klassisches iptables, nur anzeigen (-L), numerisch (-n) - nicht ausgeführt:
# sudo iptables -L -n
nftables nicht verfügbar oder keine Rechte (mit 'sudo' erneut versuchen)

Regeln ändern – für den Beleg

Im Beleg müssen Sie die Firewall so anpassen, dass die beiden Ports der Anwendung von den HTW-Netzen aus erreichbar sind. Die Regeln liegen in der Datei /etc/nftables.conf und werden als root bearbeitet (dieser Schritt ist bei uns kein IaC, sondern manuelle Server-Administration).

⚠️ Vorsicht – du kannst dich aussperren! Eine falsche Firewall-Regel kann dir den Zugang zur VM nehmen:

  • Blockierst du versehentlich Port 22 (SSH) oder setzt eine Default-Deny-Policy ohne eine Regel, die SSH erlaubt, ist die VM per SSH nicht mehr erreichbar.
  • Ein Syntaxfehler in /etc/nftables.conf kann verhindern, dass das gesamte Regelwerk korrekt lädt.

So gehst du auf Nummer sicher:

  • Lass deine aktuelle SSH-Sitzung geöffnet, während du testest – schließe sie erst, wenn der Zugang in einer zweiten Sitzung bestätigt ist.
  • Stelle sicher, dass eine Erlaubnis-Regel für SSH (Port 22) vorhanden ist und bleibt.
  • Teste das Regelwerk zuerst (siehe unten), bevor du es dauerhaft machst.

Konkret für den Beleg: Öffne /etc/nftables.conf als root und ergänze im Block chain input die Regel aus dem Beispiel oben:

sudo nano /etc/nftables.conf      # oder ein anderer Editor

Im chain input-Block einfügen:

ip saddr { 10.4.0.0/16, 141.45.0.0/16 } tcp dport { 3000, 8080 } counter accept

Damit werden die Ports 3000 (Umami) und 8080 (Nginx) genau für die HTW-Netze freigegeben – alle anderen eingehenden Zugriffe auf diese Ports bleiben durch die Default-Deny-Policy blockiert.

Hinweis – Ports kommen aus der .env: 3000 und 8080 sind die Default-Werte des Belegs. Im Projekt stehen sie als UMAMI_PORT/WEB_PORT in deploy/.env und werden von dort in compose.yaml und html/index.html eingesetzt (Single Source of Truth). Da nftables die .env nicht selbst liest, kannst du die passende Regel-Zeile einmalig daraus erzeugen, damit sie zu deinen Ports passt:

source /home/local/iac/.env   # liefert WEB_PORT / UMAMI_PORT
echo "ip saddr { 10.4.0.0/16, 141.45.0.0/16 } tcp dport { $UMAMI_PORT, $WEB_PORT } counter accept"

Die ausgegebene Zeile fügst du dann in /etc/nftables.conf ein. (Vgl. README.md, Abschnitt „Firewall".)

Änderungen testen und dauerhaft machen

Nach dem Bearbeiten muss das Regelwerk neu geladen werden. Erst testen, dann dauerhaft aktivieren:

Als root:

# 1. Syntaxprüfung + sofort laden (zeigt Fehler an, ohne Reboot):
/usr/sbin/nft -f /etc/nftables.conf

# 2. Prüfen, ob die Regel aktiv ist:
/usr/sbin/nft list ruleset

# 3. Dauerhaft (über Reboots) aktivieren bzw. neu laden:
systemctl enable nftables      # beim Booten automatisch laden
systemctl restart nftables     # Regelwerk aus /etc/nftables.conf neu laden

Anschließend von einem Rechner im HTW-Netz (ggf. über VPN) prüfen, ob die Anwendung erreichbar ist:

curl http://<VM-IP>:8080      # Nginx-Webseite
curl http://<VM-IP>:3000      # Umami

Tipp – mehrere Deployments auf einem Server: Laufen mehrere Stacks mit unterschiedlichen Host-Ports, müssen die zusätzlichen Ports in die Regel aufgenommen werden, z. B. tcp dport { 3000, 8080, 3001, 8081 }.

Aufgaben

Aufgabe 1 (Wiederholung CIDR). Die Regel oben erlaubt Zugriffe aus 141.45.0.0/16. Liegt die IP 141.45.200.17 in diesem Netz? Und 141.46.0.3? (Tipp: bei /16 müssen nur die ersten 16 Bit - also die ersten zwei Bytes - übereinstimmen.)

Aufgabe 2 (Default-Policy). Warum ist es sicherer, die input-Chain auf Default-Deny zu stellen und einzelne Ports freizugeben, als alles zu erlauben und nur einzelne Ports zu sperren?

Aufgabe 3 (stateful). Der Server darf selbst Pakete ins Internet senden (z. B. um Updates zu laden) und Antworten empfangen. Warum braucht man dank einer stateful Firewall dafür keine eigene Regel, die alle möglichen Antwort-Ports öffnet?

Aufgabe 4 (optional). Sieh dir mit /usr/sbin/nft list ruleset (bzw. sudo iptables -L -n) das Regelwerk deines eigenen Rechners an. Welche Ketten (chains) gibt es, und was ist deren Default-Policy?

Aufgabe 5 (Beleg). Ergänze in /etc/nftables.conf die Regel, die die Ports 3000 und 8080 für die HTW-Netze freigibt, lade das Regelwerk neu und prüfe, dass dein SSH-Zugang weiterhin funktioniert. Warum solltest du dabei die aktuelle SSH-Sitzung geöffnet lassen?