Docker umgeht UFW: 4 getestete Lösungen für Ihren VPS

10 Min. Lesezeit·Matthieu|

Docker manipuliert iptables direkt und ignoriert UFW-Regeln. Ihre Container-Ports sind trotz aktivem ufw deny aus dem Internet erreichbar. Hier sind vier Lösungen mit Vor- und Nachteilen, jeweils durch einen Scan von einem externen Host verifiziert.

Ihre UFW-Firewall belügt Sie. Wenn Sie Docker auf einem VPS mit aktiviertem UFW betreiben, ist jeder veröffentlichte Container-Port offen im Internet. ufw deny 8080 bewirkt nichts. Docker umgeht UFW vollständig.

Dieses Tutorial zeigt das Problem in Aktion und führt durch vier Lösungen mit unterschiedlichen Kompromissen. Jede Lösung enthält einen Verifizierungsschritt: Den Port von einem externen Host scannen, um zu bestätigen, dass er tatsächlich blockiert ist. Nicht nur ufw status prüfen.

Diese Anleitung funktioniert auf Ubuntu 24.04 und Debian 12. Beide verwenden standardmäßig die iptables-Kompatibilitätsschicht, daher gelten dieselben Lösungen. Debian 12 nutzt nftables als Standard-Backend, aber UFW und Docker interagieren über die iptables-Schnittstelle, die transparent auf nftables abbildet.

Voraussetzungen: Ein VPS mit installiertem Docker, aktiviertem UFW und SSH-Zugang. Ein zweiter Rechner (Ihr Laptop oder ein anderer Server) für externe Port-Scans.

Warum umgeht Docker die UFW-Firewall-Regeln?

Docker schreibt iptables-Regeln in die nat- und filter-Tabellen, um Datenverkehr zu Containern zu leiten. UFW verwaltet nur die INPUT-Kette. Wenn ein Paket für einen veröffentlichten Container-Port ankommt, leiten Dockers NAT-Regeln es über die FORWARD-Kette um, bevor UFWs INPUT-Regeln es sehen können. Das Paket erreicht UFW nie. Das bedeutet, dass ufw deny keinerlei Wirkung auf von Docker veröffentlichte Ports hat.

Hier ist der Paketfluss für eine Anfrage an Port 8080 auf Ihrem VPS, bei dem ein Container mit -p 8080:80 veröffentlicht ist:

  1. Das Paket kommt an der Netzwerkschnittstelle an
  2. Es gelangt in die PREROUTING-Kette der nat-Tabelle
  3. Dockers DNAT-Regel schreibt das Ziel auf die Container-IP um (z.B. 172.17.0.2:80)
  4. Das Paket wechselt in die FORWARD-Kette der filter-Tabelle (nicht INPUT)
  5. Dockers DOCKER-Kette akzeptiert das weitergeleitete Paket
  6. Das Paket erreicht den Container

UFW sieht es nie, weil UFW nur die INPUT-Kette überwacht. Das Paket nimmt den FORWARD-Pfad.

Das ist kein Bug. Docker braucht die iptables-Kontrolle, damit Container-Networking funktioniert. Aber es entsteht eine ernste Sicherheitslücke, wenn Sie davon ausgegangen sind, dass UFW Ihren Server schützt.

Wie verifiziere ich, dass Docker meine UFW-Firewall umgeht?

Bevor Sie einen Fix anwenden, sehen Sie das Problem selbst. Starten Sie einen Test-Container, der HTTP auf Port 8080 bereitstellt:

docker run -d --name ufw-test -p 8080:80 nginx:alpine

Prüfen Sie, dass UFW aktiv ist und eingehenden Verkehr standardmäßig ablehnt:

sudo ufw status verbose

Sie sollten Default: deny (incoming) in der Ausgabe sehen. Verweigern Sie jetzt explizit Port 8080:

sudo ufw deny 8080

Prüfen Sie, dass UFW den Port als blockiert anzeigt:

sudo ufw status | grep 8080
8080                       DENY        Anywhere
8080 (v6)                  DENY        Anywhere (v6)

UFW meldet den Port als verweigert. Testen Sie jetzt von Ihrem lokalen Rechner (nicht vom Server):

curl -s -o /dev/null -w "%{http_code}" http://YOUR_SERVER_IP:8080
200

Die Antwort ist 200. Der Container ist trotz UFW-Deny-Regel vollständig aus dem Internet erreichbar. Falls Sie nmap auf Ihrem lokalen Rechner installiert haben:

nmap -p 8080 YOUR_SERVER_IP
PORT     STATE SERVICE
8080/tcp open  http-proxy

Der Port ist offen. UFW schützt ihn nicht.

Entfernen Sie die Deny-Regel (Sie werden als Nächstes einen echten Fix anwenden):

sudo ufw delete deny 8080

Lassen Sie den Test-Container laufen. Sie verwenden ihn zur Verifizierung jeder Lösung.

Welchen Docker-UFW-Fix sollte ich verwenden?

Es gibt vier Lösungen. Jede hat unterschiedliche Kompromisse. Wählen Sie diejenige, die zu Ihrem Setup passt.

Lösung Netzwerk-Auswirkung Überlebt Neustart Compose-kompatibel Komplexität Geeignet für
DOCKER-USER-Kette Keine Ja Ja Mittel Volle Kontrolle, mehrere öffentliche Ports
ufw-docker-Tool Keine Ja Ja Niedrig Automatisierte Verwaltung, dynamische Container
iptables=false Unterbricht Inter-Container-Networking, kein Internet aus Containern Ja Ja Niedrig Isolierte Einzelcontainer (selten)
Bind auf 127.0.0.1 Keine Ja Ja Niedrig Dienste hinter einem Reverse Proxy

Schnelle Entscheidung:

  1. Sind alle Ihre Container hinter einem Reverse Proxy (Nginx, Traefik, Caddy)? Verwenden Sie 127.0.0.1-Binding. Das ist am einfachsten.
  2. Brauchen Sie Container, die auf bestimmten Ports öffentlich erreichbar sind? Verwenden Sie die DOCKER-USER-Kette für manuelle Kontrolle oder ufw-docker für automatisierte Verwaltung.
  3. Betreiben Sie Container, die niemals Netzwerkzugriff haben dürfen? Verwenden Sie iptables=false, aber verstehen Sie die Kompromisse.

Wie behebe ich das Umgehen von UFW durch Docker mit der DOCKER-USER-Kette?

Die DOCKER-USER-Kette ist ein Platzhalter, den Docker für Administratoren leer lässt. Regeln, die Sie hier einfügen, werden vor Dockers eigenen Weiterleitungsregeln ausgewertet. Durch das Einfügen von UFW-Integrationsregeln in diese Kette über /etc/ufw/after.rules lassen Sie Docker-Verkehr durch UFW laufen.

Diese Methode gibt Ihnen volle Kontrolle. Sie funktioniert sowohl mit docker run als auch mit Docker Compose. Sie überlebt Neustarts, da die Regeln beim UFW-Start geladen werden.

Öffnen Sie /etc/ufw/after.rules:

sudo cp /etc/ufw/after.rules /etc/ufw/after.rules.bak
sudo nano /etc/ufw/after.rules

Fügen Sie den folgenden Block am Ende der Datei hinzu, nach der bestehenden COMMIT-Zeile:

# BEGIN UFW AND DOCKER
*filter
:ufw-user-forward - [0:0]
:ufw-docker-logging-deny - [0:0]
:DOCKER-USER - [0:0]
-A DOCKER-USER -j ufw-user-forward

-A DOCKER-USER -m conntrack --ctstate RELATED,ESTABLISHED -j RETURN
-A DOCKER-USER -m conntrack --ctstate INVALID -j DROP
-A DOCKER-USER -i docker0 -o docker0 -j ACCEPT

-A DOCKER-USER -j RETURN -s 10.0.0.0/8
-A DOCKER-USER -j RETURN -s 172.16.0.0/12
-A DOCKER-USER -j RETURN -s 192.168.0.0/16
-A DOCKER-USER -j ufw-docker-logging-deny -m conntrack --ctstate NEW -d 10.0.0.0/8
-A DOCKER-USER -j ufw-docker-logging-deny -m conntrack --ctstate NEW -d 172.16.0.0/12
-A DOCKER-USER -j ufw-docker-logging-deny -m conntrack --ctstate NEW -d 192.168.0.0/16

-A DOCKER-USER -j RETURN

-A ufw-docker-logging-deny -m limit --limit 3/min --limit-burst 10 -j LOG --log-prefix "[UFW DOCKER BLOCK] "
-A ufw-docker-logging-deny -j DROP

COMMIT
# END UFW AND DOCKER

Was dieser Block bewirkt:

  • -A DOCKER-USER -j ufw-user-forward: leitet Docker-Verkehr zuerst durch UFWs Weiterleitungsregeln
  • conntrack --ctstate RELATED,ESTABLISHED: erlaubt Rückverkehr bestehender Verbindungen (hält bestehende Verbindungen nach Regeländerungen aufrecht)
  • conntrack --ctstate INVALID: verwirft fehlerhafte Pakete
  • -i docker0 -o docker0: erlaubt Container-zu-Container-Kommunikation im selben Bridge-Netzwerk
  • Die -j RETURN -s-Zeilen erlauben Verkehr aus privaten Subnetzen
  • Neue Verbindungen (ctstate NEW) zu Docker-Subnetzen von außen werden protokolliert und verworfen
  • Die Logging-Regel begrenzt auf 3 Nachrichten pro Minute, um Log-Überflutung zu vermeiden

Laden Sie UFW neu, um die neuen Regeln anzuwenden:

sudo ufw reload

Verifizieren Sie, dass die Regeln geladen sind:

sudo iptables -L DOCKER-USER -n -v

Sie sollten Ihre neuen Regeln in der Kettenausgabe sehen. Wenn die Kette nur eine Standard-RETURN-Regel zeigt, wurde der after.rules-Block nicht korrekt geladen. Prüfen Sie die Dateisyntax.

Einen bestimmten Container-Port über UFW freigeben

Mit aktiver DOCKER-USER-Kette sind alle Container-Ports standardmäßig von außen blockiert. Um einen bestimmten Port freizugeben, müssen Sie die Container-IP und den Container-Port referenzieren, nicht den Host-Port. Der Grund: Dockers DNAT-Regel in der PREROUTING-Kette schreibt das Ziel um, bevor das Paket die FORWARD-Kette erreicht. Zum Zeitpunkt der Regelauswertung ist das Ziel die interne Container-Adresse.

Ermitteln Sie zuerst die Container-IP:

docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' ufw-test
172.17.0.2

Dann erlauben Sie Verkehr zu diesem Container:

sudo ufw route allow proto tcp from any to 172.17.0.2 port 80

Verifizieren Sie, dass UFW die Route-Regel anzeigt:

sudo ufw status | grep "ALLOW FWD"
172.17.0.2 80/tcp          ALLOW FWD   Anywhere

Hinweis: Da die Regel eine Container-IP referenziert, die sich beim Neustart ändern kann, verwenden Sie das ufw-docker-Tool (nächster Abschnitt) für automatische IP-Verfolgung. Der manuelle DOCKER-USER-Ansatz eignet sich besser, wenn Sie die Container-IPs selbst verwalten (statische IPs in Docker Compose oder wenn Sie Regelaktualisierungen skripten).

Verifizierung von einem externen Host

Testen Sie von Ihrem lokalen Rechner den freigegebenen Port:

curl -s -o /dev/null -w "%{http_code}" http://YOUR_SERVER_IP:8080
200

Der Port ist erreichbar, weil Sie die Weiterleitung zum Container explizit erlaubt haben. Testen Sie jetzt einen nicht freigegebenen Port (zum Beispiel, wenn Sie einen Container auf Port 9090 hätten):

nmap -p 9090 YOUR_SERVER_IP
PORT     STATE    SERVICE
9090/tcp filtered unknown

filtered bedeutet, dass die Firewall Pakete stillschweigend verwirft. Die DOCKER-USER-Kette funktioniert.

Wie verwende ich das ufw-docker-Tool zur Verwaltung von Container-Firewall-Regeln?

Das Tool chaifeng/ufw-docker automatisiert die DOCKER-USER-Ketten-Konfiguration und bietet Befehle zum Freigeben oder Sperren von Container-Ports. Es ändert /etc/ufw/after.rules für Sie und fügt ein CLI zur Verwaltung von Regeln pro Container hinzu.

Installation:

sudo wget -O /usr/local/bin/ufw-docker \
  https://github.com/chaifeng/ufw-docker/raw/master/ufw-docker
sudo chmod +x /usr/local/bin/ufw-docker

Prüfen Sie das Skript vor der Ausführung. Es ist ein Shell-Skript, das Sie mit cat /usr/local/bin/ufw-docker lesen können.

Führen Sie die Installation aus, um /etc/ufw/after.rules zu patchen:

sudo ufw-docker install

Dies fügt DOCKER-USER-Ketten-Regeln ähnlich dem vorherigen Abschnitt hinzu, einschließlich conntrack-basierter Filterung und Logging. Es patcht auch after6.rules für IPv6. Laden Sie UFW neu:

sudo ufw reload

Verifizieren Sie die Installation:

sudo ufw-docker check

Container-Ports verwalten

Externen Zugriff auf Port 80 eines Containers namens web erlauben:

sudo ufw-docker allow web 80/tcp

Regeln für einen Container auflisten:

sudo ufw-docker list web

Eine Regel entfernen:

sudo ufw-docker delete allow web 80/tcp

Alle Docker-Firewall-Regeln anzeigen:

sudo ufw-docker status

Docker-Compose-Beispiel mit ufw-docker

services:
  web:
    image: nginx:alpine
    container_name: web
    ports:
      - "8080:80"
    restart: unless-stopped

Starten Sie den Stack und geben Sie dann den Port frei:

docker compose up -d
sudo ufw-docker allow web 80/tcp

Hinweis: Der ufw-docker-Befehl referenziert den Container-Port (80), nicht den Host-Port (8080). Das Tool löst das Mapping automatisch auf.

Alternative: ufw-docker-automated

Das Projekt shinebayar-g/ufw-docker-automated verfolgt einen anderen Ansatz. Es läuft selbst als Docker-Container, hört auf Docker-API-Events und erstellt automatisch UFW-Regeln, wenn Container starten oder stoppen. Das löst eine Einschränkung des ufw-docker-Tools: Wenn ein Container neu startet und eine neue IP erhält, wird die alte Regel ungültig.

Mit ufw-docker-automated fügen Sie Labels zu Ihren Containern hinzu:

services:
  web:
    image: nginx:alpine
    ports:
      - "8080:80"
    labels:
      - "UFW_MANAGED=TRUE"
    restart: unless-stopped

Das Tool überwacht Container mit dem Label UFW_MANAGED=TRUE und erstellt automatisch passende UFW-Regeln. Es unterstützt auch UFW_ALLOW_FROM, um den Zugriff auf bestimmte IPs oder CIDR-Bereiche zu beschränken.

Verifizierung von einem externen Host

Nach der Portfreigabe mit ufw-docker bestätigen Sie von Ihrem lokalen Rechner:

curl -s -o /dev/null -w "%{http_code}" http://YOUR_SERVER_IP:8080
200

Testen Sie einen nicht gelisteten Port, um die Blockierung zu bestätigen:

nmap -p 9090 YOUR_SERVER_IP
PORT     STATE    SERVICE
9090/tcp filtered unknown

Was passiert, wenn ich Dockers iptables-Verwaltung deaktiviere?

"iptables": false in der Docker-Daemon-Konfiguration stoppt Docker daran, iptables-Regeln zu erstellen. Das ist der einfachste Fix, hat aber die schwerwiegendsten Nebenwirkungen.

Was nicht mehr funktioniert:

  • Container können nicht auf das Internet zugreifen (keine Masquerading-Regeln)
  • Container-zu-Container-Kommunikation über verschiedene Netzwerke funktioniert nicht mehr
  • Port-Publishing (-p) funktioniert nicht mehr. Sie müssen alle Weiterleitungsregeln selbst verwalten.

Dieser Ansatz ist nur geeignet, wenn Sie isolierte Container betreiben, die keinen Internetzugang benötigen, und Sie das gesamte Networking manuell handhaben.

Bearbeiten oder erstellen Sie die Docker-Daemon-Konfiguration:

sudo nano /etc/docker/daemon.json
{
  "iptables": false
}

Wenn die Datei bereits Inhalt hat, fügen Sie den Schlüssel "iptables": false zum bestehenden JSON-Objekt hinzu. Erstellen Sie kein zweites JSON-Objekt.

Starten Sie Docker neu:

sudo systemctl restart docker

Verifizieren Sie, dass Docker keine iptables-Regeln erstellt:

sudo iptables -L DOCKER -n 2>/dev/null

Mit Docker 28.x kann die DOCKER-Kette noch existieren, sollte aber nur eine DROP-Regel statt der üblichen Weiterleitungsregeln enthalten. Keine ACCEPT-Regeln bedeutet, dass Docker keinen Verkehr zu Containern leitet.

Verifizierung von einem externen Host

Starten Sie den Test-Container erneut (er wurde beim Docker-Neustart gestoppt):

docker start ufw-test

Von Ihrem lokalen Rechner:

nmap -p 8080 YOUR_SERVER_IP
PORT     STATE  SERVICE
8080/tcp closed http-proxy

closed (nicht filtered), weil Docker die NAT-Regeln zur Verkehrsweiterleitung nicht mehr erstellt. Der Port hört aus externer Sicht nicht.

Zum Rückgängigmachen entfernen Sie "iptables": false aus daemon.json und starten Docker neu:

sudo systemctl restart docker

Wie binde ich Docker-Compose-Ports nur an Localhost?

Für Container hinter einem Reverse Proxy (Nginx, Traefik, Caddy) ist die einfachste Lösung, Ports gar nicht erst auf 0.0.0.0 zu veröffentlichen. Binden Sie sie stattdessen an 127.0.0.1. Der Reverse Proxy verbindet sich über Localhost mit dem Container. Externer Verkehr erreicht den Reverse Proxy auf den Ports 80/443, die UFW normal kontrolliert.

Das erfordert keine iptables-Änderungen, keine zusätzlichen Tools und funktioniert auf jedem Betriebssystem.

Ändern Sie in Ihrer docker-compose.yml das Port-Binding:

services:
  app:
    image: your-app:latest
    ports:
      - "127.0.0.1:8080:80"
    restart: unless-stopped

  nginx:
    image: nginx:alpine
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
    restart: unless-stopped

Der app-Dienst bindet nur an 127.0.0.1:8080. Er ist von außerhalb des Servers nicht erreichbar. Der nginx-Dienst bindet an 0.0.0.0:80 und 0.0.0.0:443 (Standard, wenn keine IP angegeben), die Sie mit UFW kontrollieren.

Für docker run die äquivalente Syntax:

docker run -d --name app -p 127.0.0.1:8080:80 your-app:latest

Binding verifizieren

Auf dem Server bestätigen Sie, dass der Port nur an Localhost gebunden ist:

ss -tlnp | grep 8080
LISTEN 0      4096      127.0.0.1:8080      0.0.0.0:*    users:(("docker-proxy",pid=12345,fd=4))

Genau hinsehen: Die Lausch-Adresse ist 127.0.0.1:8080, nicht 0.0.0.0:8080. Das bedeutet, nur lokale Verbindungen werden akzeptiert.

Verifizierung von einem externen Host

Von Ihrem lokalen Rechner:

nmap -p 8080 YOUR_SERVER_IP
PORT     STATE  SERVICE
8080/tcp closed http-proxy

Der Port ist für externen Verkehr geschlossen. Ihr Reverse Proxy steuert den öffentlichen Zugriff auf den Ports 80 und 443, die UFW normal schützt.

Das ist der empfohlene Ansatz für die meisten VPS-Setups, bei denen Sie Webanwendungen hinter einem Reverse Proxy betreiben.

Fehlerbehebung

Regeln verschwinden nach Docker-Neustart

Wenn Sie den DOCKER-USER-Ketten-Ansatz verwendet haben und Regeln beim Docker-Neustart verschwinden, prüfen Sie, dass Ihre Regeln in /etc/ufw/after.rules stehen und nicht manuell mit iptables-Befehlen hinzugefügt wurden. Manuelle iptables-Regeln überleben keine Service-Neustarts. Die after.rules-Datei wird bei jedem UFW-Start geladen.

sudo ufw reload
sudo iptables -L DOCKER-USER -n -v

Container kann DNS nicht auflösen nach Anwendung der DOCKER-USER-Regeln

Der after.rules-Block enthält eine conntrack --ctstate RELATED,ESTABLISHED-Regel, die DNS-Antwortverkehr zurück zu Containern erlaubt. Wenn die DNS-Auflösung in Containern fehlschlägt, prüfen Sie, ob die conntrack-Regel geladen ist:

sudo iptables -L DOCKER-USER -n -v | grep "RELATED,ESTABLISHED"

Falls sie fehlt, prüfen Sie die Syntax Ihres after.rules-Blocks und laden Sie UFW neu.

ufw-docker-Befehle schlagen mit „container not found" fehl

Der Befehl ufw-docker allow erfordert, dass der Container läuft. Er löst den Containernamen in eine IP-Adresse auf. Wenn der Container gestoppt ist, schlägt der Befehl fehl. Starten Sie den Container zuerst, dann fügen Sie die Regel hinzu.

Debian 12 nftables-Kompatibilität

Debian 12 nutzt nftables als Firewall-Backend, aber UFW und Docker verwenden die iptables-Kompatibilitätsschicht (iptables-nft). Die Lösungen in dieser Anleitung funktionieren auf Debian 12 und Ubuntu 24.04 identisch. Prüfen Sie, dass Sie das nft-Backend verwenden:

sudo iptables --version
iptables v1.8.10 (nf_tables)

Das Suffix (nf_tables) bestätigt, dass die iptables-nft-Kompatibilitätsschicht aktiv ist. Alle DOCKER-USER-Ketten-Regeln funktionieren über diese Schicht.

Logs auf blockierten Verkehr prüfen

Wenn eine Verbindung nach Anwendung des DOCKER-USER-Ketten-Fixes unerwartet blockiert wird, prüfen Sie die UFW-Docker-Logs:

sudo journalctl -k | grep "UFW DOCKER BLOCK"

Die Log-Einträge zeigen die Quell-IP und den Zielport der blockierten Pakete.

Testressourcen aufräumen

Entfernen Sie den Test-Container, wenn Sie fertig sind:

docker rm -f ufw-test

Wenn Sie während des Tests eine UFW-Deny-Regel hinzugefügt haben, entfernen Sie sie:

sudo ufw delete deny 8080

Copyright 2026 Virtua.Cloud. Alle Rechte vorbehalten. Dieser Inhalt ist ein Originalwerk des Virtua.Cloud-Teams. Vervielfältigung, Wiederveröffentlichung oder Weiterverbreitung ohne schriftliche Genehmigung ist untersagt.

Bereit, es selbst auszuprobieren?

Stellen Sie Ihren eigenen Server in Sekunden bereit. Linux, Windows oder FreeBSD.

VPS-Angebote ansehen