Eine Host-Firewall ist die letzte verlässliche Schicht zwischen einem Dienst und dem Internet — selbst wenn die Provider-Cloud-Firewall richtig konfiguriert ist, schadet eine zweite Schicht direkt auf dem Server nicht. ufw (Uncomplicated Firewall) ist auf Debian und Ubuntu der pragmatische Default: ein Wrapper um iptables/nftables mit einer einfachen Befehlsstruktur. Dieser Artikel führt durch die saubere Erstkonfiguration: Default-Policy auf deny, gezielte Freigaben für SSH, HTTP, HTTPS, IPv6 korrekt aktiviert — und die Reihenfolge so, dass dabei niemand den eigenen SSH-Zugang verliert.

Was ufw ist und warum

Eine Host-Firewall entscheidet auf dem Server selbst, welche Netzwerk-Pakete den Kernel passieren dürfen und welche verworfen werden. Sie sitzt zwischen der Netzwerkkarte und den Diensten, die auf dem Server laufen — bevor nginx, sshd oder postgres ein Paket überhaupt zu sehen bekommen, hat die Firewall bereits entschieden, ob es durchgelassen wird.

Linux bringt mit iptables (älter) und nftables (modern) zwei mächtige Paketfilter direkt im Kernel mit. Beide sind allerdings komplex: Tabellen, Chains, Match-Bedingungen, Target-Aktionen, Reihenfolgen — wer das alles richtig konfigurieren will, schreibt schnell mehrere Dutzend Zeilen Konfiguration. Für die meisten Server ist die nötige Logik aber sehr einfach:

  • Alles, was hereinkommt, wird abgelehnt — der Server soll von außen nur über die Dienste erreichbar sein, die wir explizit freigeben.
  • Bestimmte Ports werden gezielt geöffnet — typischerweise SSH (Port 22), HTTP (Port 80), HTTPS (Port 443) und je nach Setup ein paar weitere.
  • Alles, was hinausgeht, wird erlaubt — der Server selbst darf zum Update-Server, zum DNS-Resolver, zu APIs etc. verbinden.

ufw (Uncomplicated Firewall) bildet genau diese Logik mit einer minimalen Befehlsstruktur ab. Im Hintergrund schreibt es die Regeln nach iptables (oder, auf neueren Distros, in dessen nftables-Backend). Du arbeitest mit Befehlen wie ufw allow 22/tcp, ufw übersetzt sie in die deutlich verbose Kernel-Syntax. Wer später Spezialfälle braucht, die ufw nicht abdeckt — z. B. Rate-Limiting, Forwarding-Regeln, NAT — kann jederzeit auf das tiefere Werkzeug zurückgreifen (siehe nftables-Artikel). Für 95 % der Anwendungsfälle reicht ufw aber vollkommen aus.

Cloud-Firewall ersetzt keine Host-Firewall. Viele VPS-Anbieter bieten eine Cloud-Firewall an, die Pakete bereits vor dem Server filtert — sehr praktisch, weil Angriffsverkehr gar nicht erst auf der eigenen Maschine landet. Sie ersetzt die Host-Firewall aber nicht: Bei Fehlkonfiguration im Provider-Panel, beim Wechsel auf einen Dedicated-Server ohne diese Funktion, bei VPN- oder Container-Traffic, der die Cloud-Schicht umgeht, oder einfach beim nächsten Anbieter-Wechsel ist die Host-Firewall die einzig verlässliche, mitwandernde Linie. Beide Ebenen redundant einzurichten ist der Standard-Ansatz, kein Doppel-Aufwand.

Installation und Status

Auf Ubuntu ist ufw als Paket standardmäßig vorhanden, aber nicht aktiviert — du kannst es direkt nutzen, ohne etwas nachzuinstallieren. Auf Debian (Server-Variante) fehlt das Paket per Default und muss erst installiert werden:

Bash
sudo apt update
sudo apt install ufw -y

Status prüfen:

Bash
sudo ufw status verbose

Frisch installiert antwortet ufw mit Status: inactive. Das ist mehr als nur eine Statusangabe: es bedeutet, dass keine Filterung stattfindet — alle Ports, auf denen ein Dienst lauscht, sind tatsächlich aus dem Internet erreichbar. Wenn auf dem Server zufällig ein Datenbank-Dienst auf Port 5432 lauscht oder Webmin auf 10000, sind diese Ports in diesem Moment ungeschützt offen, sofern keine andere Firewall (Cloud-Provider) davor sitzt.

Die übliche Reihenfolge ist daher nicht: erst aktivieren, dann Regeln setzen. Sondern umgekehrt: erst alle nötigen Regeln definieren — vor allem die SSH-Regel, sonst sperrst du dich aus — und dann die Firewall mit ufw enable scharf schalten. ufw übernimmt in diesem Moment alle bisher definierten Regeln in einem Schritt und lehnt ab dann alles ab, was nicht ausdrücklich erlaubt ist. Dieser Schritt-für-Schritt-Aufbau wird in den nächsten Sektionen durchgegangen.

Default-Policy: alles dicht, was nicht erlaubt ist

Bevor wir einzelne Regeln hinzufügen, definieren wir das Grundverhalten der Firewall — also: was passiert mit einem Paket, für das keine Regel gilt? Die Antwort darauf prägt die Sicherheit des gesamten Setups. ufw teilt das in drei voneinander unabhängige Richtungen:

Bash
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw default deny routed

deny incoming ist die zentrale Schutzfunktion. Ohne diesen Default wäre jeder Port, der nicht explizit blockiert wurde, offen — also der gegenteilige Ansatz: jeder Dienst, der irgendwann mal startet (auch versehentlich, etwa durch ein automatisches Update, das einen Port-Listener mitbringt), ist sofort weltweit erreichbar. Mit deny incoming wechseln wir auf „Default: alles zu, gezielt öffnen” — das ist die einzig vernünftige Position für einen Server, der im Internet steht.

allow outgoing erlaubt dem Server, selbst beliebige ausgehende Verbindungen aufzubauen. Praktisch heißt das: APT/DNF darf Updates ziehen, der Server darf DNS-Anfragen stellen, deine Anwendung darf zu externen APIs verbinden. Outbound-Filterung wäre theoretisch eine zusätzliche Schicht — etwa um zu verhindern, dass ein kompromittierter Container „nach Hause telefoniert” — aber sie ist in der Praxis kaum sinnvoll zu pflegen, weil moderne Anwendungen mit vielen Hosts kommunizieren. allow outgoing ist der pragmatische Standard.

deny routed betrifft eine dritte Richtung, die oft übersehen wird: Pakete, die der Server weiterleitet, also nicht selbst empfängt, aber an ein anderes Interface zustellt. Auf einem normalen Server gibt es so etwas nicht — die Direktive blockiert eine Funktion, die ohnehin nicht in Gebrauch ist. Sobald der Server aber Router-, VPN-Gateway- oder Container-Host-Funktionen übernimmt (z. B. bei Docker oder WireGuard), wird Forwarding automatisch ein Thema. Mit deny routed als Default vermeidet man, dass Forwarding versehentlich offen bleibt; im Bedarfsfall öffnet man es gezielt.

reject vs. deny: ufw kennt beide Aktionen. deny (Default) verwirft Pakete still — die Verbindung läuft auf Timeout, ein Angreifer bekommt keinen Hinweis, ob der Port geschlossen ist oder ob da überhaupt eine Maschine antwortet. reject schickt eine ICMP-Antwort „Connection refused” zurück, was den Verbindungsversuch sofort beendet — höflicher, aber informativer für Scanner. Für öffentlich erreichbare Server: bei deny bleiben. Für Hosts in vertrauten Netzen, wo schnellere Fehlermeldungen helfen: reject ist akzeptabel.

Regeln hinzufügen — der wichtigste zuerst: SSH

Reihenfolge ist entscheidend. Mit der gerade gesetzten Default-Policy deny incoming wird beim nächsten ufw enable jeder eingehende Verbindungsversuch blockiert — auch der eigene SSH-Zugang. Wer also aktiviert, ohne vorher SSH freigegeben zu haben, sperrt sich beim nächsten Reconnect aus und kommt nur noch über die Provider-Konsole oder einen Rescue-Modus zurück. Erste Regel daher zwingend:

Bash
sudo ufw allow 22/tcp

Die Syntax <port>/<protokoll> heißt: erlaube eingehenden Verkehr auf Port 22, Transportprotokoll TCP. SSH läuft ausschließlich über TCP — UDP gibt es bei SSH nicht. Ohne /tcp öffnet ufw den Port sowohl für TCP als auch UDP, was bei SSH zwar harmlos ist (es lauscht ohnehin keiner auf UDP/22), aber das Regelwerk unnötig öffnet. Generell: bei jeder Regel das Protokoll explizit angeben, sofern man weiß, was der Dienst spricht.

Wenn SSH auf einem anderen Port läuft (siehe SSH-Hardening), den entsprechenden Port öffnen — und ein Kommentar dazuhängen, damit später ohne Nachschlagen klar ist, welche Regel wofür da ist:

Bash
sudo ufw allow 47823/tcp comment 'SSH'

Der comment-Zusatz erscheint später in ufw status verbose. In Setups mit vielen Regeln ist das Gold wert — einen Port allein zu sehen sagt nichts darüber aus, wofür er da ist (Port 8080 kann ein App-Server, ein Proxy, ein Monitoring-Endpunkt oder etwas ganz anderes sein).

Weitere typische Freigaben für einen Webserver:

Bash
sudo ufw allow 80/tcp comment 'HTTP'
sudo ufw allow 443/tcp comment 'HTTPS'

Port 80 wird auch dann gebraucht, wenn der Webserver eigentlich nur HTTPS spricht — Let’s-Encrypt-Renewals laufen über den HTTP-01-Challenge-Mechanismus, der Port 80 erreichbar braucht. Wer Port 80 schließt, weil „eh alles auf 443 redirected”, killt damit das automatische Zertifikats-Renewal.

Service-Namen statt Portnummern: ufw erkennt symbolische Namen aus /etc/servicessudo ufw allow ssh macht dasselbe wie sudo ufw allow 22/tcp. Lesbar, aber bei umkonfigurierten Diensten verwirrend, weil die Auflösung eine Aussage über den Standard-Port macht, nicht über deinen tatsächlichen sshd-Port. ufw allow ssh öffnet immer Port 22, auch wenn dein sshd auf 47823 lauscht. Aus diesem Grund: in Tutorials gerne mit Servicenamen gearbeitet, in der Praxis aber besser die Portnummer angeben, dann gibt es keine Mehrdeutigkeit zwischen „logischem Service” und „tatsächlichem Port”.

Firewall aktivieren und prüfen

Erst nachdem SSH freigegeben und die Default-Policy gesetzt ist:

Bash
sudo ufw enable

ufw warnt vor dem Aktivieren mit dem Hinweis Command may disrupt existing ssh connections. Proceed with operation (y|n)?. Mit y bestätigen. Was hier passiert: ufw lädt im Kernel iptables/nftables-Regeln ein, die ab sofort jeden eingehenden Verkehr filtern. Bestehende SSH-Sessions sind durch die established-Conntrack-Regel abgesichert — sie laufen normalerweise weiter. Falls aus irgendeinem Grund doch Verbindungstrennung passiert (Conntrack-Tabelle wurde geleert, Mismatch in der Konfiguration), bleibt nur die Provider-Konsole bzw. KVM-over-IP, um wieder Zugriff zu bekommen.

Nach dem Aktivieren den Status mit Details abfragen:

Bash
sudo ufw status verbose

Erwartete Ausgabe:

ufw status
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), deny (routed)
New profiles: skip

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW IN    Anywhere
80/tcp                     ALLOW IN    Anywhere
443/tcp                    ALLOW IN    Anywhere
22/tcp (v6)                ALLOW IN    Anywhere (v6)
80/tcp (v6)                ALLOW IN    Anywhere (v6)
443/tcp (v6)               ALLOW IN    Anywhere (v6)

Die Ausgabe zerfällt in zwei Teile. Oben stehen die globalen Eigenschaften:

  • Status: active — die Filterung ist aktiv.
  • Logging: on (low) — geblockte Pakete werden mit niedriger Detailtiefe geloggt.
  • Default: deny (incoming), allow (outgoing), deny (routed) — die in der vorigen Sektion gesetzten Defaults.
  • New profiles: skip — wenn neue Anwendungen ufw-Profile mitbringen (z. B. nginx), werden sie nicht automatisch aktiviert. Sicherer Default.

Unten stehen die einzelnen Regeln mit drei Spalten: To (Zielport, also auf welchem Port der Server lauscht), Action (ALLOW IN = eingehend erlauben, DENY IN = eingehend verwerfen), From (welche Quell-Adresse darf — Anywhere = jede IP der Welt, ansonsten konkrete IP/Subnetz).

Die (v6)-Einträge erscheinen automatisch — ufw legt für jede Regel parallel eine IPv4- und eine IPv6-Variante an, sofern IPv6 aktiv ist (siehe Sektion 7). Auf einem reinen IPv4-Server fehlen die (v6)-Zeilen, das ist normal.

Quell-IP einschränken: noch enger machen

Bisher haben wir Ports global geöffnet — Anywhere heißt: jede IP der Welt darf den Port ansprechen. Für öffentliche Dienste (Webserver auf 80/443) ist das richtig. Für administrative Dienste (Datenbank, Webmin, phpMyAdmin, Monitoring-Backends) ist das fast nie richtig: Solche Ports sollten nur von vertrauten Quellen erreichbar sein, weil ein Datenbank-Port mit Standard-Credentials in unter einer Minute kompromittiert ist, sobald er weltweit offen steht.

ufw erlaubt die Einschränkung über die from-Klausel — entweder mit einer einzelnen IP oder einem ganzen Subnetz in CIDR-Notation:

Bash Nur eine einzelne Quell-IP
sudo ufw allow from 203.0.113.50 to any port 5432 proto tcp comment 'PostgreSQL nur App-Server'
Bash Ein ganzes Subnetz
sudo ufw allow from 198.51.100.0/24 to any port 10000 proto tcp comment 'Webmin nur Büro-VPN'

Die CIDR-Notation 198.51.100.0/24 beschreibt einen Adressbereich — das /24 heißt, die ersten 24 Bits der Adresse sind fix (198.51.100.0 bis 198.51.100.255), die restlichen 8 Bits beliebig. Praktisch sind das 256 IPs, von denen typischerweise 254 als Hosts nutzbar sind. Ein /32 wäre eine einzelne IP (die from <ip>-Form ist Kurzschreibweise dafür), ein /16 deckt 65 536 IPs ab, ein /8 rund 16,7 Millionen.

Wer eine bestehende offen-für-alle-Regel durch eine eingeschränkte ersetzen will, muss die alte erst löschen — ufw versteht sonst nicht, welche Regel gemeint ist. Mit numbered lässt ufw alle aktiven Regeln samt Index anzeigen:

Bash
sudo ufw status numbered
sudo ufw delete 3

Die Indexnummern verschieben sich nach jedem delete — also immer erst status numbered aufrufen, dann den korrekten Index löschen, nicht mehrere delete-Befehle hintereinander mit den ursprünglichen Nummern.

Tipp: Statt einzelne Quell-IPs zu pflegen, lohnt sich für viele dynamische IPs (Heim-Anschluss mit wechselnder IP) ein VPN auf dem Server — z. B. WireGuard. Dann sind Admin-Dienste nur über die VPN-IP erreichbar, und die wechselnde Heim-IP wird zur VPN-IP. Vereinfacht die Firewall-Regeln drastisch und ist sicherer als „Heim-IP whitelisten und hoffen, dass sie sich nicht ändert”.

IPv6 prüfen

IPv6 ist auf modernen VPS-Systemen meistens automatisch aktiv — der Server bekommt zusätzlich zu seiner IPv4-Adresse einen IPv6-Adressbereich (typisch ein /64-Block) und ist über beide Stacks erreichbar. Das ist normalerweise gut, aus Firewall-Sicht aber leicht zu übersehen: eine Regel, die nur IPv4 abdeckt, lässt IPv6-Traffic ungehindert durch. Wenn ein Angreifer den Server über die IPv6-Adresse anspricht, treffen ihn die IPv4-Regeln nicht.

ufw verwaltet IPv6 parallel zu IPv4 — sofern die Konfiguration es zulässt. Standardmäßig ist das an, aber bei selbst kompiliertem ufw, älteren Setups oder bestimmten Provider-Images lohnt der Blick:

Bash
grep IPV6 /etc/default/ufw
# erwartet: IPV6=yes

Steht der Wert auf no, die Datei editieren, auf yes setzen, dann ufw neu laden — ein einfacher reload reicht hier nicht, ufw muss komplett neu starten, um IPv6 zu aktivieren:

Bash
sudo ufw disable
sudo ufw enable

Mit aktiviertem IPV6=yes legt ufw bei jedem allow-Befehl automatisch zwei Regeln an: eine für IPv4 und eine für IPv6. In ufw status verbose sind sie als (v6)-Variante sichtbar. Auf einem ausschließlich IPv4-erreichbaren Server ist IPv6 funktional egal — die Regeln existieren dann, greifen aber nie. Auf Dual-Stack-Systemen ist es kritisch.

Wer IPv6 explizit nicht haben will, kann es alternativ auf Kernel-Ebene komplett ausschalten (net.ipv6.conf.all.disable_ipv6=1 in /etc/sysctl.d/). Das ist aber meistens kontraproduktiv — moderne Dienste (Mail, manche CDNs) nutzen IPv6 zunehmend. Besser: IPv6 anlassen und korrekt firewallen.

Logging und Reload

ufw kann Verbindungsversuche protokollieren — nützlich beim Debugging („warum erreicht mich Port X nicht?”) und für Sicherheits-Beobachtung („wer scannt mich gerade?”). Es gibt vier Logging-Level mit zunehmender Detailtiefe:

LevelWas wird geloggt
offKein Logging.
lowGeblockte Pakete (Default-Deny-Treffer) und nicht zugeordnete Pakete.
mediumWie low, plus Pakete, die durch invalide Regeln laufen, plus Rate-Limit-Treffer.
highWie medium, plus alle erlaubten Pakete (sehr viel Output).
fullKomplettes Audit. Nur für kurzes Debugging — flutet das Log innerhalb von Minuten.

Logging an- und einstellen:

Bash
sudo ufw logging on
sudo ufw logging medium

Logs landen auf neueren Distros im systemd-Journal und sind mit journalctl einsehbar; auf Distros mit klassischem rsyslog zusätzlich in /var/log/ufw.log:

Bash
# Letzte 100 ufw-Einträge im Journal
sudo journalctl -k --grep '\[UFW' -n 100

# Live mitschauen
sudo journalctl -k --grep '\[UFW' -f

medium ist eine gute Einstellung für ein paar Tage Beobachtung nach dem Setup — du siehst Scans aus dem Internet und Versuche auf nicht freigegebene Ports, was beim Debugging fehlerhafter Anwendungen hilft. Auf Dauer fluten geblockte Scans die Logs aber zuverlässig, dann auf low zurück oder off.

Regeln neu laden, ohne die Firewall abzuschalten:

Bash
sudo ufw reload

Der Reload ist atomar — die alten Regeln bleiben aktiv, bis die neuen vollständig geladen sind. Du verlierst dabei keine bestehenden SSH-Sessions, weil das Conntrack-Tracking persistente Verbindungen korrekt erkennt.

Sicherheits-Checkliste

  • SSH-Port als erste Regel freigegeben — vor ufw enable
  • Default-Policy: deny incoming, allow outgoing, deny routed
  • Bei nicht-Standard-SSH-Port: Provider-Cloud-Firewall und ufw beide angepasst
  • IPv6 aktiviert (IPV6=yes in /etc/default/ufw), Regeln existieren als (v6)-Variante
  • Sensitive Dienste (Datenbank, Admin-Panel, Monitoring) nur per Quell-IP/-Subnetz freigegeben — nicht weltweit
  • Öffentliche Dienste (HTTP/HTTPS) sind weltweit offen, das ist gewollt
  • ufw status verbose regelmäßig prüfen, besonders nach apt upgrade-Läufen

Häufige Fehler bei ufw

Aussperrung durch ufw enable ohne SSH-Regel

Klassiker. Default-Policy ist deny incoming, ssh wird mitgeblockt. Reihenfolge: erst ufw allow 22/tcp (oder den eigenen Port), dann ufw enable. Vor jedem enable-Aufruf gibt ufw eine Warnung aus — die ernst nehmen.

IPv6 vergessen

Auf Dual-Stack-Servern werden IPv4-Pakete durch ufw geblockt, IPv6 läuft daran vorbei. Mit IPV6=yes (Default) und ufw-Reload werden Regeln automatisch parallel angelegt. Bei einer Migration von einem alten ufw-Setup unbedingt prüfen.

Cloud-Firewall vs. Host-Firewall doppelt blockt

Wenn die Provider-Cloud-Firewall einen Port nicht durchlässt, hilft auch keine offene ufw-Regel — der Traffic kommt nie auf dem Server an. Bei nicht-erreichbarem Dienst beide Schichten prüfen.

ufw allow ssh statt Portnummer

ssh als Service-Name löst über /etc/services immer auf Port 22 auf — auch wenn dein sshd auf 47823 lauscht. Bei nicht-Standard-Ports immer die Portnummer angeben, dann gibt es keine Mehrdeutigkeit.

Reihenfolge der Regeln nicht beachtet

ufw arbeitet first-match: die erste passende Regel entscheidet. Wer erst allow from any to any port 80 und danach deny from 198.51.100.0/24 to any port 80 setzt, hat das Subnetz nicht gesperrt — die allow-Regel kommt zuerst. Mit ufw insert <position> lassen sich Regeln gezielt vorne einreihen.

ufw disable löscht keine Regeln

Nach ufw disable sind alle Regeln noch da, nur inaktiv. Beim nächsten enable greifen sie wieder. Wer wirklich aufräumen will, nutzt ufw reset — das löscht alles und stellt den Werkszustand her. Vorher Regelliste sichern.

Verwandte Artikel

Externe Quellen

/ Weiter

Zurück zu Server-Bootstrap

Zur Übersicht