Firewall iptables und UFW steuern, welche Verbindungen ein Webserver akzeptiert und welche ich blockiere – das entscheidet über Angriffsfläche und Ausfälle. In diesem Praxisartikel zeige ich klare Regeln, sichere Defaults und getestete Befehle für SSH, HTTP(S), Datenbanken, Logging, IPv6 und Docker – direkt einsetzbar auf produktiven Hosts.
Zentrale Punkte
Die folgenden Stichpunkte geben mir eine schnelle Orientierung, bevor ich mit der Konfiguration starte.
- Restriktiv starten: Default-Deny für eingehend, gezielt öffnen
- SSH zuerst erlauben: Zugang nicht riskieren
- UFW als Oberfläche: einfache Syntax, iptables im Hintergrund
- Logging aktivieren: Regeln prüfen, Angriffe erkennen
- Persistenz sicherstellen: Regeln über Neustarts erhalten
Grundlagen: iptables und UFW im Überblick
Ich setze auf iptables, wenn ich feine Kontrolle über Pakete, Chains und Matches brauche. UFW nutze ich, wenn ich schnell verlässliche Regeln anwenden will, die intern als iptables-Regeln landen [1]. So kombiniere ich einfache Befehle mit der Kraft des Linux-Netfilters, ohne mich in Details zu verlieren. Für Webserver bedeutet das: Ich baue einen klaren Filter vor Apache, Nginx oder Node, damit nur gewünschter Traffic ankommt [2]. Diese Trennung reduziert Angriffsflächen und lässt Angriffe häufiger ins Leere laufen.
Beide Tools ergänzen sich und ich entscheide situativ, welches passt. UFW punktet mit sauberer Lesbarkeit, gerade auf Ubuntu und Debian [3]. iptables gibt mir erweiterte Optionen, etwa bei NAT, spezifischen Interfaces und komplexen Matches. Wichtig: Ich dokumentiere meine Regeln knapp, damit Wartung später leicht fällt. Wer das Sicherheitskonzept vertiefen will, findet eine verständliche Einführung hier: Firewall als Schutzschild.
Startkonfiguration: Default-Policies sicher setzen
Ich beginne mit Default-Policies: Eingehend blockieren, ausgehend erlauben. So verhindere ich, dass neue Dienste ungewollt erreichbar sind. Loopback lasse ich immer zu, damit interne Prozesse stabil arbeiten. Bestehende Verbindungen akzeptiere ich, um Abbrüche zu vermeiden. Diese Reihenfolge minimiert Fehler beim Aktivieren der Firewall [2][5].
Mit UFW setze ich die Basis mit wenigen Befehlen. Ich prüfe anschließend den Status im Detail, um Tippfehler sofort zu bemerken. Bei besonders sensiblen Hosts schränke ich auch ausgehende Ports ein. Damit senke ich das Risiko von Datenabfluss, falls ein Dienst kompromittiert wurde. Die folgenden Befehle setze ich häufig ein:
# UFW: Standardregeln
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Alternativ strengere Outbound-Policy
sudo ufw default deny outgoing
sudo ufw allow out 53
sudo ufw allow out 80
sudo ufw allow out 443
# Status prüfen
sudo ufw status verbose
Stateful Filtering: Zustände und Reihenfolge
Ein sauberer Paketfluss steht und fällt mit Conntrack-Zuständen. Ich akzeptiere etablierte Verbindungen zuerst, verwerfe ungültige Pakete früh und lasse Loopback offen. Das reduziert Last und verhindert Seiteneffekte durch späte Drops. Für iptables setze ich die Reihenfolge bewusst:
# iptables: solide Basis
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
# Loopback immer erlauben
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT
# Bestehende/zugehörige Verbindungen zulassen
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# Ungültige Pakete früh verwerfen
iptables -A INPUT -m conntrack --ctstate INVALID -j DROP
Mit UFW werden ESTABLISHED/RELATED intern bereits berücksichtigt. Ich achte zusätzlich darauf, ob ich lieber DROP (still) oder REJECT (aktiv, schnelleres Failover) verwende. Für interne Netze bevorzuge ich REJECT, im Public-Netz meist DROP.
Essenzielle Regeln für Webserver: SSH, HTTP und HTTPS
Ich schalte SSH zuerst frei, sonst sperre ich mich leicht aus. Danach erlaube ich HTTP und HTTPS, damit der Webserver erreichbar ist. Ich setze nur die Ports, die ich wirklich brauche. Später ergänze ich optional Ratenbegrenzung oder Fail2ban, um brutale Anmeldeversuche zu dämpfen. Jede Änderung kontrolliere ich sofort mit Status- oder List-Kommandos.
Die Kommandos dafür halte ich simpel. UFW bietet sprechende Aliase für Webports, was die Lesbarkeit erhöht. Mit iptables kann ich präzise Ports und Protokolle setzen. Ich speichere iptables-Regeln im Anschluss, damit sie den Neustart überstehen. Hier die minimalen Schritte:
# SSH
sudo ufw allow 22
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# HTTP/HTTPS
sudo ufw allow http
sudo ufw allow https
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
SSH absichern: Begrenzen und gezielt öffnen
Zusätzlich zur Freigabe dämpfe ich Angriffe mit Rate-Limiting oder Whitelists. UFW bringt einen einfachen Schutz mit:
# SSH-Rate-Limiting (UFW)
sudo ufw limit 22/tcp comment "SSH Rate Limit"
Mit iptables setze ich feinere Limits. Das verhindert massives Durchprobieren von Passwörtern, ohne legitime Admins auszuschließen:
# SSH: Verbindungsversuche pro Quelle begrenzen
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --name SSH --set
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --name SSH --update --seconds 60 --hitcount 10 -j DROP
Wo möglich, erlaube ich SSH nur von Admin-IP-Adressen und arbeite mit SSH-Keys. Ein Portwechsel ist kein Ersatz für Sicherheit, kann aber Rauschen reduzieren. Ich dokumentiere die Ausnahmen und prüfe sie regelmäßig.
Datenbanken absichern und IP-Quellen beschränken
Datenbankports öffne ich nie global, sondern nur lokal oder für definierte Quell-IP-Adressen. So verhindere ich, dass Scanner offene MySQL-, PostgreSQL- oder MongoDB-Ports finden. Für lokale Apps genügt 127.0.0.1 als Ziel, externe Admin-Zugriffe steuere ich strikt per IP. Änderungen dokumentiere ich kurz, etwa im Server-Wiki. Das spart mir Zeit bei Audits.
Die folgenden Beispiele nutze ich häufig in Projekten. Jede erlaubte IP prüfe ich vorab auf Korrektheit. UFW erlaubt eine saubere “from-to”-Notation, iptables setzt dieselbe Logik technisch um. Für temporäre Wartungsfenster nutze ich zusätzliche Allow-Regeln und lösche sie danach. So bleibt die Oberfläche klein:
# Nur lokal: MySQL
sudo ufw allow from 127.0.0.1 to any port 3306
iptables -A INPUT -p tcp -s 127.0.0.1 --dport 3306 -j ACCEPT
# Einzelne IP erlauben
sudo ufw allow from 203.0.113.10
iptables -A INPUT -s 203.0.113.10 -j ACCEPT
# Port für spezifische IP freigeben
sudo ufw allow from 10.1.2.3 to any port 4444
iptables -A INPUT -p tcp -s 10.1.2.3 --dport 4444 -j ACCEPT
# Bekannte Angreifer blockieren
sudo ufw deny from 192.0.2.24
iptables -A INPUT -s 192.0.2.24 -j DROP
Logging, Interfaces und IPv6 sauber behandeln
Ich schalte Logging ein, um Regeln zu verifizieren und auffälligen Traffic zu erkennen. Level “on” in UFW reicht für die meisten Hosts, höhere Level nutze ich nur gezielt. Logs werte ich mit journalctl, fail2ban oder SIEM-Tools aus. So erkenne ich Muster von Scans oder Brute-Force-Versuchen. Bei Auffälligkeiten passe ich Regeln zeitnah an [2].
Regeln binde ich oft an ein bestimmtes Interface, etwa eth0 in Public-Netzen. So verhindere ich, dass interne Netze unnötig betroffen sind. UFW kann “allow in on eth0 to any port 80”, iptables nutzt -i für Eingangsinterfaces. Für IPv6 prüfe ich die Aktivierung in /etc/default/ufw auf “IPV6=yes” und nutze ip6tables für native Regeln [2]. Diese Trennung vermeidet Lücken bei dual-stack-Hosts.
ICMP und ICMPv6: Erreichbarkeit ohne Lücken
Ich lasse notwendige ICMP-Typen zu, damit Path MTU Discovery, Timeouts und Diagnose funktionieren. ICMP ist kein Feind, sondern Kern des IP-Protokolls. Ich begrenze nur übermäßige Echos.
# IPv4: Echo begrenzen, wichtige ICMP-Typen erlauben
iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 5/second --limit-burst 20 -j ACCEPT
iptables -A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT
iptables -A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT
# UFW: ICMP allgemein erlauben (Feintuning in before.rules möglich)
sudo ufw allow in proto icmp
Unter IPv6 ist ICMPv6 absolut notwendig (Neighbor Discovery, Router Advertisement). Ich erlaube die Kern-Typen und begrenze Echo-Requests:
# IPv6 (ip6tables)
ip6tables -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type router-advertisement -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type neighbor-solicitation -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type neighbor-advertisement -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type echo-request -m limit --limit 5/second --limit-burst 20 -j ACCEPT
Outbound-Restriktion und NAT/Masquerading richtig nutzen
Ausgehenden Traffic beschränke ich, wenn ich Risikoprofil und Compliance schärfen will. Ich erlaube DNS und HTTPS, alles andere blocke ich bis auf definierte Ziele. Das reduziert exfiltrierte Daten, sollte ein Dienst gekapert sein. Für Applikationen mit Update- oder API-Bedarf lege ich definierte Ausnahmen an. Ich dokumentiere diese Ausnahmen sauber und prüfe sie regelmäßig.
Für Routing-Setups nutze ich NAT/Masquerading über UFW-Before-Rules oder roh mit iptables. Dabei achte ich auf die Reihenfolge der Chains, damit Pakete korrekt umgeschrieben werden. Nach Änderungen teste ich Konnektivität und Latenzen. Bei Produktivsystemen plane ich ein Wartungsfenster und sichere die Konfiguration. So halte ich die Netzpfade nachvollziehbar [7].
Outbound-Details: Systemdienste und Protokolle
Bei strikter Outbound-Policy erlaube ich gezielt DNS (53/udp), HTTPS (443/tcp) und bei Bedarf NTP (123/udp). Für Mail-Server ergänze ich 25/tcp und 587/tcp. Domainbasierte Ausnahmen löse ich nicht auf Paketebene, sondern über Proxys oder Applikationslogik.
# UFW: typische Systemdienste
sudo ufw allow out 123/udp # NTP
sudo ufw allow out 25/tcp # SMTP - nur wenn Mail-Server
sudo ufw allow out 587/tcp # Submission - nur wenn nötig
# iptables: gezieltes Allow
iptables -A OUTPUT -p udp --dport 123 -j ACCEPT
iptables -A OUTPUT -p tcp --dport 25 -j ACCEPT
iptables -A OUTPUT -p tcp --dport 587 -j ACCEPT
Docker und Firewalls: Fallstricke vermeiden
Docker setzt eigene iptables-Regeln, die meine Policy beeinflussen können. Ich prüfe deshalb die NAT- und FORWARD-Ketten nach jedem Compose- bzw. Daemon-Start. Exponierte Ports gebe ich bewusst frei und meide “-p 0.0.0.0:PORT”. Stattdessen binde ich sie an die Management-IP oder den Reverse-Proxy. So bleibt der Angriffsvektor kleiner und besser sichtbar [1].
Ich halte Host-Firewalls trotz Docker aktiv. Zusätzlich kontrolliere ich Security-Gruppen auf der Infrastruktur-Ebene, falls vorhanden. Bei Konflikten zwischen UFW und Docker nutze ich dokumentierte Workarounds oder setze Regeln in DOCKER-USER. Wichtig ist eine klare Zuständigkeit: Host blockt grundsätzlich, Container öffnen nur explizit. Diese Ordnung verhindert unbewusste Freigaben.
# DOCKER-USER: globale Host-Policy vor Docker-Regeln durchsetzen
iptables -N DOCKER-USER 2>/dev/null || true
iptables -I DOCKER-USER -s 192.0.2.24 -j DROP
iptables -A DOCKER-USER -j RETURN
UFW-Feineinstellungen: Reihenfolge, Profile und Routed-Traffic
Wenn Präzision zählt, nutze ich “insert”, “numbered” und App-Profile. So halte ich die Regelreihenfolge sauber und nutze getestete Service-Definitionen.
# Reihenfolge steuern
sudo ufw insert 1 deny in from 198.51.100.0/24
# Nummerierte Ansicht und gezieltes Löschen
sudo ufw status numbered
sudo ufw delete 3
# App-Profile (z. B. Nginx Full)
sudo ufw app list
sudo ufw app info "Nginx Full"
sudo ufw allow "Nginx Full"
# Routed Traffic standardmäßig blockieren (Forwarding)
sudo ufw default deny routed
Komplexere Ausnahmen hinterlege ich in before.rules bzw. after.rules. Dort kann ich ICMP-Feintuning oder NAT exakt platzieren, ohne die Lesbarkeit der Standardregeln zu verlieren.
Persistente Regeln: Speichern und Wiederherstellen
Mit UFW sind Regeln persistent und überstehen Reboots automatisch. Das vereinfacht Administration deutlich auf Debian/Ubuntu-Hosts. Bei iptables speichere ich die Regeln nach Änderungen und spiele sie beim Start wieder ein. Dafür nutze ich iptables-save/restore oder netfilter-persistent. Ohne diese Schritte verliere ich sonst Änderungen nach einem Neustart [5].
Ich teste Persistenz planvoll: Reboot einplanen, danach Status prüfen. Stimmen Zähler und Chains, ist die Konfiguration solide. Fehlen Regeln, korrigiere ich den Ladepfad im Init- bzw. Systemd-Kontext. Diese Routine verhindert Überraschungen während Wartungen. Dokumentation und Backup der Regeldateien runden das Vorgehen ab.
# Debian/Ubuntu: Persistenz für iptables
sudo apt-get install -y iptables-persistent
sudo netfilter-persistent save
# Manuell sichern
sudo iptables-save | sudo tee /etc/iptables/rules.v4
sudo ip6tables-save | sudo tee /etc/iptables/rules.v6
# Wiederherstellen (bei Bedarf)
sudo iptables-restore < /etc/iptables/rules.v4
sudo ip6tables-restore < /etc/iptables/rules.v6
Leistung und Schutz: Limits, Sets und Kernel-Tuning
Bei hoher Last reduziere ich Regelanzahl und nutze gezielte Rate-Limits. Für große Blocklisten arbeite ich mit ipset, um Nachschlagezeiten zu verkürzen. Zusätzlich setze ich systemnahe Schutzmechanismen:
# SYN-Flut eindämmen (Kernel)
sudo sysctl -w net.ipv4.tcp_syncookies=1
# HTTP-Verbindungsrate pro Quell-IP begrenzen (Beispiel)
iptables -A INPUT -p tcp --dport 80 -m conntrack --ctstate NEW
-m hashlimit --hashlimit-name http_rate --hashlimit-above 50/second
--hashlimit-burst 100 --hashlimit-mode srcip -j DROP
Ich behalte die Größe der Conntrack-Tabelle im Blick. Bei vielen gleichzeitigen Verbindungen erhöhe ich nf_conntrack_max, teste aber vorher die Auswirkungen.
Management, Tests und Fehlervermeidung
Ich lasse SSH immer zuerst zu, bevor ich “deny incoming” aktiviere. Danach teste ich aus einer zweiten Session, ob der Zugang stabil bleibt. Jede neue Regel prüfe ich mit “ufw status verbose” oder “iptables -L -v”. So erkenne ich Trefferzähler und sehe, ob Pakete in der erwarteten Chain landen. Backup der Firewall-Dateien setze ich vor größeren Umbauten an.
Für ganzheitliche Sicherheit kombiniere ich die Firewall mit Härtungsschritten am System. Dazu zählen sichere SSH-Settings, Patch-Management und minimale Dienste. Einen praxisnahen Leitfaden nutze ich gerne als Checkliste: Server-Hardening für Linux. Ich wiederhole diese Prüfungen regelmäßig und halte mich an feste Wartungsfenster. Das hält meine Server zuverlässig in Form.
Erweiterte Tests und Beobachtung
Ich prüfe die Außenwirkung mit Port-Scans aus einem externen Netz und verifiziere intern offene Sockets. Logs beobachte ich anfangs engmaschig, um Fehlschlüsse früh zu erkennen.
# Offenliegende Sockets
ss -lntup
# iptables-Übersicht kompakt
sudo iptables -S
sudo iptables -L -v -n
# UFW: ausführlicher Status und Logs
sudo ufw status verbose
journalctl -k | grep -i ufw
# Externe Prüfung (aus anderem Host/Netz)
nmap -Pn -p 22,80,443 <HOST>
Für risikoreiche Änderungen plane ich eine Rückfallebene ein. Ich arbeite im Screen/Tmux und setze falls nötig einen zeitgesteuerten Rückbau, falls ich mich aussperre. Nach erfolgreichem Test hebe ich die Rückfallaktion wieder auf.
# Beispiel: automatisches Deaktivieren als Notanker (vorsichtig verwenden)
echo "ufw disable" | at now + 2 minutes
# Nach erfolgreichem Test wieder löschen: atrm <job-id>
Vergleich Hosting-Anbieter: Firewall-Integration im Fokus
Bei Hosting setze ich auf Sicherheit nahe an der Plattform. Individuelle Policies, schnelle Regel-Deployments und gutes Monitoring zahlen sich aus. In aktuellen Vergleichen überzeugt webhoster.de mit sauber integrierten Firewall-Optionen und flotter Unterstützung. Wer Panel-Setups bevorzugt, profitiert von einer klaren Anleitung zur Plesk-Firewall. Die folgende Tabelle ordnet zentrale Kriterien ein.
| Anbieter | Firewall-Integration | Performance | Support | Platzierung |
|---|---|---|---|---|
| webhoster.de | individuell konfig. | sehr hoch | top | 1 |
| Anbieter B | Standard | hoch | gut | 2 |
| Anbieter C | Standard | gut | befriedigend | 3 |
Praxisbeispiele: Von Test zur Produktivregel
Ich beginne jedes Regel-Set im Screen oder in einer zweiten SSH-Session. So kann ich mich bei Fehlbedienung noch retten. Erst wenn ein Test-Host sauber läuft, übernehme ich Regeln in Produktion. Rückweg-Regeln und ein Rollback-Plan geben mir zusätzlich Sicherheit. Am Ende dokumentiere ich Änderungen knapp im Change-Log.
Für Webserver nutze ich wiederkehrende Bausteine: SSH erlauben, HTTP/S freigeben, interne Ports lokal binden, Logging an, ICMP begrenzen, überflüssige Protokolle blocken. Danach kümmere ich mich um IPv6-Spiegelregeln, damit keine Lücke bleibt. Docker-Ketten prüfe ich immer separat, weil sie eigene Pfade setzen. Abschließend validiere ich Zugriffe über externe Checks und Monitoring. So bleibt die Oberfläche sauber und nachvollziehbar [1][2].
Zusammenfassung für Admins
Mit klaren Regeln und wenigen Befehlen sichere ich Webserver verlässlich ab. Default-Deny eingehend, SSH zuerst, HTTP/S freigeben – das bildet die stabile Basis. Datenbankports nur lokal oder per Whitelist, Logging aktiv, IPv6 beachten, Docker-Ketten prüfen. Persistente Speicherung und regelmäßige Tests verhindern böse Überraschungen. Diese Routine hält Dienste erreichbar und reduziert Risiken spürbar.
Ob ich UFW nutze oder direkt iptables: Entscheidend ist eine klare, sparsame Policy. Ich dokumentiere kurz, verifiziere regelmäßig und halte Ausnahmen klein. Outbound-Restriktion stoppt unnötige Verbindungen und begrenzt Schaden bei Kompromittierung. Mit geübtem Blick auf Logs erkenne ich Anomalien schneller und reagiere passend. So bleibt der Webserver belastbar und die Angriffsfläche klein.


