UFW Firewall einrichten: IPv4, IPv6 und Docker
Wenn du die UFW Firewall einrichten möchtest, geht es um eine einfache Frage: Welche Verbindungen darf dein Linux-Server annehmen? UFW hilft dir, den Netzwerkzugriff mit übersichtlichen Regeln zu begrenzen. Ich zeige dir die Einrichtung, die wichtigsten Befehle und die Konfiguration für IPv4 und IPv6. Und weil Docker bei der Paketweiterleitung gerne einen anderen Weg nimmt, schauen wir uns veröffentlichte Container-Ports mit einem praktischen Test an.
Eine Firewall ist schnell aktiviert. Sich dabei selbst auszusperren leider auch! Deshalb sichern wir zuerst die Konfiguration und den SSH-Zugang. Die Anleitung richtet sich an Debian und Ubuntu; meine praktischen Tests liefen auf Ubuntu 24.04.4 LTS mit UFW 0.36.2 und Docker Engine 29.3.0.
🧱 Was ist UFW und was schützt die Firewall?
UFW steht für Uncomplicated Firewall. Du verwaltest damit Paketfilterregeln des Linux-Kernels über gut lesbare Befehle. Statt dich für jede Freigabe durch lange iptables-Aufrufe zu arbeiten, kannst du beispielsweise sudo ufw allow 443/tcp verwenden. UFW ist dabei eine Verwaltungsschicht; die eigentliche Filterung übernimmt Netfilter im Kernel.
Ich unterscheide drei Richtungen: incoming betrifft Verbindungen zum Host selbst, outgoing vom Host ausgehenden Verkehr und routed durch den Rechner weitergeleitete Pakete. Genau diese dritte Richtung wird bei Docker wichtig. Ein Container auf einer Bridge ist ein eigenes Netzwerkziel.
UFW berücksichtigt Verbindungszustände. Antworten auf erlaubte Verbindungen dürfen mit den üblichen Standardregeln zurückkommen, ohne dass du für jeden dynamischen Rückkanal eine neue Freigabe brauchst. Die Firewall ersetzt trotzdem weder Updates noch sichere Passwörter oder eine saubere Dienstkonfiguration. Ein erlaubter Port bleibt ein erlaubter Port.
🧭 Vor der Einrichtung: Zugang und Ist-Zustand prüfen
Du brauchst sudo-Rechte und solltest einen unabhängigen Konsolenzugang haben, etwa die Konsole deines VPS-Anbieters oder Hypervisors. Lass die bestehende SSH-Sitzung offen und teste später eine neue Verbindung. Eine noch funktionierende alte Sitzung beweist nicht, dass du dich erneut anmelden kannst.
cat /etc/os-release ip -brief address ip route ip -6 route sudo ss -lntup sudo ufw status verbose sudo ufw show added
Prüfe den tatsächlichen SSH-Port und alle benötigten Dienste. Port 22 ist nur der Standard. Bei einem anderen Port passt du sämtliche SSH-Beispiele an. Status: inactive bedeutet außerdem nicht, dass keine Regeln gespeichert sind. Auf meinem Testhost lagen bereits zahlreiche ältere Regeln vor, obwohl UFW ausgeschaltet war.
📦 UFW installieren und den bisherigen Stand sichern
sudo apt update sudo apt install ufw ufw --version
Ubuntu bringt UFW häufig bereits mit, aktiviert es aber nicht automatisch. Vor Änderungen sichere ich die Konfigurationsdateien. Die zusätzlichen Regeldumps helfen bei der Diagnose; bei einem laufenden Docker-Host solltest du sie später nicht unbesehen komplett zurückspielen, weil Container und ihre Regeln inzwischen anders aussehen können.
backup="$HOME/ufw-backups/$(date +%Y%m%d_%H%M%S)" install -d -m 700 "$backup" sudo tar -czpf "$backup/ufw-config.tar.gz" /etc/ufw /etc/default/ufw sudo iptables-save > "$backup/iptables.txt" sudo ip6tables-save > "$backup/ip6tables.txt" sudo ufw status verbose > "$backup/status.txt" chmod 600 "$backup"/*
🔐 UFW Firewall einrichten, ohne SSH zu verlieren
Die folgenden Grundregeln passen zu einem neuen, normalen Server: eingehende Verbindungen standardmäßig sperren, ausgehende erlauben und benötigte Dienste ausdrücklich freigeben. Bei einem bestehenden Rechner kontrollierst du vorher seine gespeicherten Regeln. Die Änderung einer Standardrichtlinie löscht keine vorhandene Freigabe.
Zuerst IPv6 berücksichtigen
sudo nano /etc/default/ufw
Kontrolliere in dieser Datei den folgenden Wert. Du änderst gezielt die vorhandene Zeile und ersetzt nicht die komplette Datei:
IPV6=yes
Damit verwaltet UFW auch IPv6-Regeln. Bei bisher ausgeschaltetem UFW genügt für unseren Ablauf das anschließende Aktivieren. Änderst du diesen Wert bei einer bereits aktiven Firewall, verlangt das Framework ein kontrolliertes ufw disable und ufw enable. Dabei entsteht eine Schutzlücke. Plane das mit Konsolenzugang und prüfe vorher die SSH-Freigaben beider Adressfamilien; ein einfacher Reload ist dafür kein Ersatz.
SSH und benötigte Dienste erlauben
sudo ufw allow 22/tcp comment 'SSH' sudo ufw default deny incoming sudo ufw default allow outgoing # Für einen gewöhnlichen Host ohne Router-Aufgabe: sudo ufw default deny routed # Nur wenn tatsächlich ein Webserver auf dem Host läuft: sudo ufw allow 80/tcp comment 'HTTP' sudo ufw allow 443/tcp comment 'HTTPS' sudo ufw show added
Öffne nur Ports, die du benötigst. Ein Datenbankserver muss deshalb nicht automatisch aus dem Internet erreichbar sein. Für Router, VPN-Gateways und Docker-Hosts prüfst du die Weiterleitung gesondert. deny routed ist kein Versprechen, dass veröffentlichte Docker-Ports geschlossen sind.
Zeitliche Rückfallmöglichkeit und Aktivierung
Auf einem bisher inaktiven Testhost mit systemd kannst du vor dem Aktivieren eine automatische Abschaltung nach fünf Minuten vorbereiten. Dieser kleine Rettungsanker nimmt nur die UFW-Aktivierung zurück. Er restauriert keine Dateien und entfernt keine selbst gesetzten Docker-Regeln. Auf einem bereits geschützten Produktivserver brauchst du stattdessen einen Rückfallplan, der den vorherigen Schutz wiederherstellt.
sudo systemd-run --unit=ufw-safety --on-active=5m /usr/sbin/ufw disable sudo systemctl status ufw-safety.timer --no-pager sudo ufw enable sudo ufw status verbose
UFW warnt beim Aktivieren über SSH. Bestätige erst, wenn dein echter SSH-Port erlaubt und die Rückfallmöglichkeit eingerichtet ist. Öffne anschließend in einem zweiten Terminal eine neue SSH-Verbindung. Prüfe auch die benötigten Anwendungen von einem anderen Rechner aus. Erst wenn alles funktioniert, stoppst du den Timer vor seinem Ablauf:
sudo systemctl stop ufw-safety.timer sudo systemctl is-active ufw-safety.timer
Die letzte Abfrage soll inactive melden. Ist die Rücknahme bereits gelaufen, prüfst du UFW erneut. ufw enable aktiviert den Schutz auch für den nächsten Systemstart; normale Regeländerungen werden bei aktiver Firewall unmittelbar angewendet.
🛠️ Regeln im Alltag: freigeben, begrenzen und löschen
Die folgenden Beispiele sind Alternativen für verschiedene Aufgaben. Führe sie nicht einfach gesammelt aus. Die Dokumentationsadressen 192.0.2.10 und 2001:db8::10 sind Platzhalter; ersetze sie durch deine tatsächliche Verwaltungsadresse. Auch eth0 kann bei dir anders heißen.
# Einzelne IPv4-Adresse für SSH erlauben: sudo ufw allow from 192.0.2.10 to any port 22 proto tcp comment 'SSH Verwaltung' # Einzelne IPv6-Adresse: sudo ufw allow from 2001:db8::10 to any port 22 proto tcp comment 'SSH Verwaltung IPv6' # Nur aus dem eigenen LAN, am passenden Interface: sudo ufw allow in on eth0 from 192.168.50.0/24 to any port 443 proto tcp # UDP ausdrücklich benennen, hier WireGuard: sudo ufw allow 51820/udp # TCP-Portbereich: sudo ufw allow 8000:8010/tcp # Einen Host-Dienst sperren: sudo ufw deny 8080/tcp
Eine IPv4-Quelladresse erzeugt nur eine IPv4-Regel. Sie erlaubt nicht automatisch die IPv6-Adresse desselben Rechners. Möchtest du eine anfangs breite SSH-Freigabe auf einzelne Quellen beschränken, füge zuerst die eingeschränkten Regeln hinzu, teste eine neue Anmeldung darüber und entferne erst dann die allgemeine Freigabe.
Reihenfolge und Rate-Limit
Innerhalb der benutzerdefinierten Regeln zählt die Reihenfolge: Eine frühere passende Freigabe kann eine spätere Sperre wirkungslos machen. insert fügt an einer bestimmten Position ein. UFW hat zusätzlich vorgeschaltete Regeln, etwa für Loopback, bestehende Verbindungen und bestimmte ICMP-Pakete. Die nummerierte Liste zeigt deshalb nicht die gesamte Kernel-Firewall.
sudo ufw status numbered # Beispiel: Sperre vor späteren allgemeinen Freigaben einfügen sudo ufw insert 1 deny from 192.0.2.123 to any port 443 proto tcp # Alternative zur allgemeinen SSH-Freigabe: sudo ufw limit 22/tcp comment 'SSH mit Rate-Limit'
Für limit musst du eine zuvor vorhandene breite allow 22/tcp-Regel passend ersetzen; beide nebeneinander sind keine verlässliche Verschärfung. Das Limit begrenzt neue Verbindungsversuche pro Quelladresse. Es ersetzt keine SSH-Schlüssel, keinen Passwortschutz und keine gesonderte Überwachung.
Regeln entfernen und Profile ansehen
sudo ufw status numbered # Beispielnummer erst nach Kontrolle der aktuellen Liste verwenden: sudo ufw delete 7 # Oder eine bekannte Regel über ihren Inhalt löschen: sudo ufw delete allow 51820/udp sudo ufw app list sudo ufw app info OpenSSH
Nach dem Löschen verschieben sich die Nummern. Lies die Liste vor der nächsten Löschung erneut. IPv4- und IPv6-Einträge haben eigene Nummern; beim Löschen über eine Nummer verschwindet nur dieser Eintrag. Anwendungsprofile sind Portbeschreibungen aus /etc/ufw/applications.d/. Sie erkennen nicht automatisch, ob du den SSH-Port geändert hast.
deny verwirft passende Pakete ohne Antwort; der Client wartet häufig bis zum Timeout. reject weist sie ausdrücklich zurück. Und ufw reset ist kein Reload: Es deaktiviert UFW und setzt seine Konfiguration zurück. Das gehört nicht in eine beiläufige Fehlerbehebung über SSH.
🌍 IPv4 und IPv6 sauber auseinanderhalten
Ein Server kann über IPv4 gut geschützt und über IPv6 trotzdem unerwartet erreichbar sein. Prüfe deshalb beide Wege. Eine globale IPv6-Adresse benötigt nicht dieselbe IPv4-Portweiterleitung deines Routers; ob sie von außen erreichbar ist, hängt unter anderem von Routing und den vorgeschalteten IPv6-Firewallregeln ab. NAT ist ohnehin kein Ersatz für Zugriffskontrolle.
grep "^IPV6=" /etc/default/ufw ip -6 address show scope global ip -6 route sudo ufw status verbose sudo ip6tables -S
Bei IPV6=yes erzeugt eine allgemeine Regel wie ufw allow 443/tcp normalerweise passende Einträge für beide Familien; die IPv6-Zeile erkennst du an (v6). IPV6=no schaltet IPv6 im Betriebssystem nicht aus! Laut UFW-Framework werden bei aktiver Firewall dann IPv6-Pakete außerhalb von Loopback verworfen. Wer IPv6 nutzen möchte, sollte es gezielt konfigurieren und testen.
Blockiere ICMPv6 nicht pauschal. IPv6 braucht unter anderem Neighbor Discovery und Meldungen wie Packet Too Big, damit Nachbarn und geeignete Paketgrößen gefunden werden. Die mitgelieferten before6.rules berücksichtigen wichtige Kontrollpakete. Auch Router Advertisements und die sysctl-Einstellungen verdienen bei besonderen Routing-Aufbauten einen Blick.
Von einem anderen Rechner mit funktionierender IPv6-Anbindung prüfst du beispielsweise einen Webserver so. Bei einer direkten IPv6-Adresse sind die eckigen Klammern in der URL Pflicht:
curl -4 -I --connect-timeout 5 https://server.example.net curl -6 -I --connect-timeout 5 https://server.example.net curl -g -6 -I --connect-timeout 5 "http://[2001:db8::20]:8080/"
Ein fehlender AAAA-Eintrag oder eine fehlende IPv6-Route ist kein Beweis für eine funktionierende Sperre. Zuerst muss das Ziel grundsätzlich über die jeweilige Adressfamilie erreichbar sein.
⚙️ Konfigurationsdateien und Fehlersuche
Für normale Freigaben nutze ich die UFW-Befehle. Die dahinterliegenden Dateien helfen beim Verstehen: /etc/default/ufw enthält grundlegende Einstellungen wie IPv6 und Standardrichtlinien. /etc/ufw/ufw.conf enthält unter anderem den Aktivierungsstand. Die Paare before.rules/before6.rules, user.rules/user6.rules und after.rules/after6.rules werden in dieser Reihenfolge ausgewertet. /etc/ufw/sysctl.conf steuert zugehörige Kernelparameter.
Die user-Dateien verwaltet UFW selbst. Änderungen daran würde ich nicht parallel zu CLI-Aufrufen vornehmen. Für spezielle manuelle Regeln brauchst du die richtige iptables-restore-Syntax einschließlich Tabellenabschnitten und COMMIT. Nach Dateiänderungen ist normalerweise ein Reload nötig; die erwähnte Umstellung der IPv6-Unterstützung benötigt dagegen den Disable/Enable-Zyklus.
sudo ufw logging low sudo ufw status verbose sudo ufw show raw sudo journalctl -k --since "10 minutes ago" --no-pager | grep "UFW" # Falls rsyslog diese Datei bereitstellt: sudo tail -n 50 /var/log/ufw.log # Nach passenden manuellen Dateiänderungen: sudo ufw reload
In einem Logeintrag zeigen SRC, DST, PROTO und DPT die Quelle, das Ziel, das Protokoll und den Zielport. Dauerhaft sehr ausführliches Logging kann viel Speicher und I/O kosten. Ein offener UFW-Port startet übrigens keinen Dienst. Wenn nichts lauscht, kommt auch mit Freigabe keine Webseite zurück.
🐳 Warum Docker veröffentlichte Ports an UFW vorbeileitet
Bei einem gewöhnlichen Host-Dienst geht der eingehende Verkehr durch den INPUT-Pfad. Bei einem veröffentlichten Port eines Containers im Bridge-Netzwerk übersetzt Docker die Zieladresse normalerweise zunächst auf Container-IP und Container-Port. Anschließend werden die Pakete über FORWARD weitergeleitet. Eine UFW-Regel für eingehenden Host-Verkehr sitzt damit am falschen Prüfpunkt.
Docker weist ausdrücklich auf diese Besonderheit hin. ufw deny 18080/tcp ist deshalb kein ausreichender Schutz für ein Docker-Portmapping wie 18080:80. Auch ein ordentliches ufw status zeigt dir nicht automatisch alle Docker-Regeln. Die Aussage betrifft hier den getesteten rootful Bridge-Aufbau; Host-Netzwerk, Rootless Docker, Swarm und andere Netzwerkmodi brauchen eine eigene Betrachtung.
🧪 Praktischer Docker-Test mit fester Container-IP
Für den Test verwende ich einen kleinen Nginx-Container. Falls Docker und Compose noch fehlen, findest du hier meine Installationsanleitung:
Nutze dafür einen Testhost! Die IP 10.0.1.92 ist die LAN-Adresse meines Hosts und muss bei dir angepasst werden. Der Port 18080 war dort frei. Ich habe außerdem die bestehenden Docker-Netze und Host-Routen geprüft, bevor ich 172.29.183.0/24 gewählt habe.
docker network ls docker network inspect $(docker network ls -q) ip route sudo ss -lntup sudo mkdir -p /home/docker-projekte/ufw-demo/html /home/docker-projekte/ufw-demo/backups sudo chown -R "$USER":"$(id -gn)" /home/docker-projekte/ufw-demo cd /home/docker-projekte/ufw-demo nano docker-compose.yaml
Der folgende Pfad ist für ein neu angelegtes Projekt gedacht. Passe Eigentümer und Rechte an dein Betriebskonzept an; ändere keine fremden bestehenden Projektverzeichnisse pauschal. Lege diese Compose-Datei an:
services:
ufw-demo:
image: nginx:1.28-alpine
container_name: ufw-demo
restart: unless-stopped
ports:
- "10.0.1.92:18080:80"
volumes:
- ./html:/usr/share/nginx/html:ro
networks:
ufw_demo_net:
ipv4_address: 172.29.183.10
networks:
ufw_demo_net:
name: ufw-demo.dockernetwork.local
driver: bridge
ipam:
config:
- subnet: 172.29.183.0/24
gateway: 172.29.183.1
ip_range: 172.29.183.128/25
Docker hat keinen eigenen exklusiven privaten IPv4-Bereich. Geeignet sind konfliktfreie RFC1918-Netze, zum Beispiel aus 172.16.0.0/12, 10.0.0.0/8 oder 192.168.0.0/16. Zufällig bedeutet hier nicht ungeprüft: Dein LAN, VPN, andere Container-Netze und entfernte erreichbare Netze dürfen sich nicht überschneiden. Die feste .10 liegt innerhalb des Subnetzes, aber außerhalb des Pools für dynamisch vergebene Adressen.
printf '%s\n' '<!doctype html><html lang="de"><meta charset="utf-8"><title>UFW Docker-Test</title><h1>Der Docker-Testcontainer läuft!</h1></html>' > html/index.html
docker compose config -q
docker compose up -d
docker compose ps
docker compose logs --tail=30
docker inspect ufw-demo --format '{{json .NetworkSettings.Networks}}'
Die Portfreigabe ist bewusst an die IPv4-LAN-Adresse gebunden. Sie öffnet keinen IPv6-Host-Port. Das verhindert keine LAN-Zugriffe über IPv4; dafür brauchen wir den nächsten Test. Der Container läuft mit restart: unless-stopped weiter.
Host-Port und Container-Port vergleichen
Für den Vergleich startest du auf dem Host einen einfachen HTTP-Dienst auf Port 18081. Er läuft im Vordergrund in einem eigenen Terminal. Python 3 muss installiert sein. Erlaube vorher deinen tatsächlichen SSH-Port und sichere UFW, wie oben beschrieben.
cd /home/docker-projekte/ufw-demo/html python3 -m http.server 18081 --bind 0.0.0.0
Im anderen Server-Terminal setzt du die Sperren und aktivierst UFW nur mit dem vorbereiteten Rückfallplan:
sudo ufw deny 18080/tcp sudo ufw deny 18081/tcp sudo ufw enable sudo ufw status verbose
Die eigentliche Prüfung führst du von einem anderen Rechner im LAN aus. localhost auf dem Docker-Host nimmt einen anderen Weg und eignet sich dafür nicht:
curl --noproxy "*" --connect-timeout 3 -I http://10.0.1.92:18081/ curl --noproxy "*" --connect-timeout 3 -I http://10.0.1.92:18080/
Bei meinem Test kam auf dem Host-Port 18081 keine HTTP-Antwort; der Verbindungsaufbau lief in den Timeout. Auf dem Docker-Port 18080 kam dagegen HTTP 200 zurück, obwohl beide UFW-Sperren vorhanden waren. Ich habe währenddessen eine neue SSH-Sitzung geöffnet und den Zugang bestätigt.
Host-Dienst :18081 mit UFW deny → Timeout, HTTP 000 Docker-Port :18080 mit UFW deny → HTTP 200 Neue SSH-Verbindung → erfolgreich
HTTP 000 ist der Messwert von curl für eine ausgebliebene HTTP-Antwort, kein Statuscode eines Webservers. Der Test zeigt das Verhalten dieses konkreten Setups, nicht eine Garantie für alle Docker-Netzwerkmodi.
🛡️ Docker-Zugriff gezielt filtern: DOCKER-USER
Beim iptables-Firewall-Backend von Docker ist DOCKER-USER der vorgesehene Einstieg für eigene Regeln vor Dockers Freigaben. Prüfe ihn zuerst. Die Ausgabe iptables ... (nf_tables) bedeutet dabei nur, dass das iptables-Werkzeug die nftables-Kompatibilitätsschicht verwendet. Sie beweist nicht, dass Docker sein natives nftables-Backend nutzt.
sudo iptables --version sudo iptables -S DOCKER-USER sudo iptables -S FORWARD
Für unseren Test blockiere ich ausschließlich neue TCP-Verbindungen, die am LAN-Interface eth0 zur Demo-IP und ihrem Container-Port 80 ankommen. Andere Container bleiben von dieser Regel unberührt. Bestehende Verbindungen werden durch diese NEW-Regel nicht beendet.
sudo iptables -I DOCKER-USER 1 -i eth0 -p tcp \ -d 172.29.183.10 --dport 80 \ -m conntrack --ctstate NEW -j DROP sudo iptables -vnL DOCKER-USER
Wiederhole den curl-Aufruf zum Port 18080 vom anderen Rechner. Bei meinem Test folgte jetzt ebenfalls ein Timeout. Der DROP-Zähler stieg auf zwei Pakete mit zusammen 104 Byte. Die Sperre hatte also tatsächlich den Docker-Verkehr getroffen.
Chain DOCKER-USER (1 references) pkts bytes target prot in destination 2 104 DROP tcp eth0 172.29.183.10 tcp dpt:80 ctstate NEW
Willst du stattdessen nur eine Quelle zulassen, kannst du die Sperre für diesen Dienst beispielsweise mit ! -s 192.0.2.10 auf alle anderen Quellen beschränken. Das ist eine alternative Regel, die vorhandene Testsperre musst du dafür zuerst entfernen. Kontrolliere auch weitere Eingangsinterfaces, VPNs und direkte Routingpfade. Eine Regel nur für eth0 deckt diese nicht automatisch mit ab.
In DOCKER-USER ist die Zielübersetzung bereits erfolgt. Deshalb verwenden wir hier 172.29.183.10:80, nicht 10.0.1.92:18080. Für eine Prüfung des ursprünglichen Host-Ziels kannst du die conntrack-Optionen --ctorigdst und --ctorigdstport verwenden. Das ist besonders bei mehreren Portmappings hilfreich, kann aber zusätzliche Verarbeitungskosten verursachen.
Regel wieder entfernen und Persistenz planen
sudo iptables -D DOCKER-USER -i eth0 -p tcp \ -d 172.29.183.10 --dport 80 \ -m conntrack --ctstate NEW -j DROP
Die manuelle iptables-Regel ist eine Testregel im laufenden Kernel, keine fertige dauerhafte Konfiguration. Für den Betrieb brauchst du eine idempotente Anwendung nach Docker-Start, etwa über dein zentrales Firewall-Regelwerk oder einen passenden systemd-Dienst. Prüfe dabei mit iptables -C, ob die Regel bereits existiert, und teste zusätzlich Docker-Neustart, UFW-Reload und einen Host-Neustart. Speichere nicht einfach einen kompletten Docker-Regeldump als statisches Dauerregelwerk.
IPv6 und natives nftables-Backend
Die gezeigte Regel filtert ausschließlich IPv4. Hat dein Container einen veröffentlichten IPv6-Zugang, brauchst du dafür ebenfalls passende Filter. Eine IPv6-Bridge mit Docker-ip6tables-Regeln kann über ip6tables gefiltert werden. Ein IPv6-Host-Port, der per docker-proxy auf einen IPv4-Container weiterreicht, kann dagegen im Host-INPUT-Pfad landen. Prüfe die konkrete Veröffentlichung, den Listener und den Paketweg; eine IPv4-Regel in DOCKER-USER schützt diese Variante nicht automatisch.
Docker bietet außerdem seit Version 29 ein natives nftables-Backend, das die aktuelle Dokumentation als experimentell bezeichnet. Dort gibt es keine DOCKER-USER-Kette. Eigene Filter gehören in separate nftables-Tabellen mit passenden Forward-Hooks und Prioritäten. Ändere nicht Dockers automatisch verwaltete Tabellen. Die iptables-Beispiele dieses Beitrags gelten für den auf meinem Testhost verwendeten Backend-Aufbau.
🏠 Oft einfacher: Ports gar nicht öffentlich veröffentlichen
Für Dienste hinter einem Reverse Proxy brauchst du häufig keine Portfreigabe am Host. Sind Proxy und Anwendung im passenden gemeinsamen Docker-Netz erreichbar, kann der Proxy direkt mit dem Container sprechen. Dafür entfernst du den ports-Abschnitt der Anwendung. Ein internes Docker-Netz ersetzt trotzdem keine saubere Trennung und Zugriffskontrolle.
Ein Dienst, den ausschließlich Programme auf dem Host erreichen sollen, lässt sich alternativ an Loopback binden. Ersetze dafür in unserem Beispiel die Portzeile durch:
ports: - "127.0.0.1:18080:80"
Anschließend wendest du die Änderung mit docker compose up -d an. Ein anderer Container erreicht diesen Port nicht über sein eigenes localhost. Bei älteren Docker-Versionen vor 28 gab es zudem eine dokumentierte Einschränkung, bei der localhost-Veröffentlichungen aus demselben Layer-2-Netz erreichbar sein konnten. Verwende eine aktuelle Version und prüfe die Erreichbarkeit von außen. Eine vorgeschaltete Provider- oder Router-Firewall ist eine weitere Schutzschicht.
Deaktiviere Dockers Regelverwaltung nicht pauschal mit "iptables": false oder "ip6tables": false. Ohne einen vollständigen Ersatz können NAT, Weiterleitung und Container-Netzwerke kaputtgehen. Das ist keine kleine UFW-Reparatur.
🧹 Test beenden und den Demo-Stack betreiben
Beende den Python-HTTP-Server mit Strg+C. Entferne nur deine temporären UFW-Testregeln. War UFW vorher inaktiv, kannst du es nach dem Test wieder deaktivieren und die unmittelbar zuvor gesicherten Konfigurationsdateien restaurieren. War es vorher aktiv, stellst du stattdessen seine bisherigen Regeln und den aktiven Zustand wieder her. Eine vorher aktive Firewall darf nach dem Test nicht einfach ausgeschaltet bleiben.
sudo ufw delete deny 18080/tcp sudo ufw delete deny 18081/tcp # NUR wenn UFW vor deinem Test bereits ausgeschaltet war: sudo ufw disable # backup muss auf die unmittelbar vor dem Test erstellte Sicherung zeigen: sudo tar -xzpf "$backup/ufw-config.tar.gz" -C / sudo ufw status verbose cd /home/docker-projekte/ufw-demo docker compose ps docker compose logs --tail=50
Ich habe auf meinem Testhost den vorherigen UFW-Stand wiederhergestellt und die DOCKER-USER-Testsperre entfernt. Nginx läuft weiterhin für Screenshots. Das ist ein LAN-Demodienst, kein fertig abgesicherter Produktionsdienst. IPv6-Regelerzeugung und SSH über ::1 habe ich ebenfalls geprüft. Ein externer IPv6-End-to-End-Test war dort mangels globaler IPv6-Adresse nicht möglich.
Für spätere Änderungen sicherst du zuerst das Projekt. Mit diesen Befehlen kannst du die Demo warten oder nach deinen Screenshots entfernen:
cd /home/docker-projekte/ufw-demo stamp=$(date +%Y%m%d_%H%M%S) tar -czpf "backups/project-$stamp.tar.gz" docker-compose.yaml html # Update innerhalb des gewählten Image-Tags: docker compose pull docker compose up -d docker compose ps docker compose logs --tail=50 # Erst wenn die Demo nicht mehr gebraucht wird: docker compose down
Der Image-Tag nginx:1.28-alpine ist die getestete Grundlage, aber kein unveränderlicher Digest und keine automatische Empfehlung für zukünftige Versionen. Prüfe vor einem produktiven Einsatz den aktuellen Upstream-Stand. Die Demo verwendet keine Zugangsdaten und benötigt keine .env-Datei.
✅ Mein Fazit
UFW macht die Host-Firewall angenehm übersichtlich. Für einen gewöhnlichen Linux-Server reichen oft eine klare Standardrichtlinie und wenige bewusst gesetzte Freigaben. Ich würde immer mit dem SSH-Zugang anfangen, IPv4 und IPv6 getrennt prüfen und von einem zweiten Rechner testen.
Bei Docker musst du zusätzlich verstehen, wohin die Pakete tatsächlich gehen. Eine Sperre für den Host schützt nicht automatisch einen weitergeleiteten Container-Port. Genau das hat der praktische Test gezeigt. Lieber einen Port weniger veröffentlichen und den Zugriff gezielt kontrollieren, als sich von einer hübschen Statusausgabe beruhigen lassen. Die Pakete lesen schließlich keine guten Absichten!
📚 Quellen und weiterführende Informationen
Ubuntu Server: Firewall · UFW-Handbuch · UFW-Framework und Konfigurationsdateien. Lokal findest du die passenden Handbücher mit man ufw und man ufw-framework.
Docker: Packet filtering and firewalls · Docker mit iptables · Docker mit nftables · Port-Veröffentlichungen · Offizielles Nginx-Image · Nginx-Docker-Repository. Recherche und Praxistest: 6. Oktober 2026.
👥 Techniverse Community
Lust auf Austausch rund um Matrix, Selfhosting und andere smarte IT-Lösungen?
In der Techniverse Community triffst du Gleichgesinnte, kannst Fragen stellen oder einfach nerdigen Talk genießen. 🚀
👉 Jetzt der Gruppe auf Matrix beitreten
~ Direkte Raumadresse: #community:techniverse.net
👉 Für lockere Gespräche abseits der Kernthemen komm in den Talkraum
~ Direkte Raumadresse: #talk:techniverse.net
Wir freuen uns, wenn du dabei bist!



