Zum Inhalt
Bytearium

Keine Werbung und keine Partnerlinks aktiv

IT-Sicherheit

Docker auf dem VPS: Firewall und Absicherung

Docker auf dem VPS umgeht ufw bei veröffentlichten Ports. So sicherst du ihn mit Anbieter-Firewall, Loopback-Bindung und DOCKER-USER ab.

Du hast ufw eingeschaltet, alles verboten und ruhig geschlafen. Währenddessen hat deine Datenbank einem Fremden auf Port 5432 geantwortet. Docker ist bei vielem höflich, nur bei deiner Firewall nicht.

Kurz gesagt

Schützt ufw Docker-Container auf einem VPS? Nein. Ports, die du mit Docker veröffentlichst, per -p 8080:80 oder einer ports:-Zeile in Compose, umgehen die ufw-Regeln, weil Docker diesen Verkehr in der nat-Tabelle abzweigt, bevor er die Ketten erreicht, die ufw nutzt [1]. Schütze den Server stattdessen in Schichten: eine Firewall beim VPS-Anbieter, veröffentlichte Ports an 127.0.0.1 gebunden mit einem Reverse Proxy davor und DOCKER-USER-Regeln für alles, was öffentlich bleiben muss, aber eingeschränkt sein soll. Mach vor jeder Änderung einen Snapshot.

So funktioniert’s

  1. Was Docker mit deiner Firewall macht. Unter Linux legt Docker für Bridge-Netzwerke eigene Firewall-Regeln an: für Isolation, Portveröffentlichung und NAT [1]. Standardmäßig sperrt der Daemon den Zugriff auf Ports, die nicht veröffentlicht wurden. Ports von Containern in einem Bridge-Netzwerk sind vom Host und von anderen Containern im selben Netzwerk erreichbar, nicht von außen [2]. Das Veröffentlichen mit -p 8080:80 bildet Port 8080 auf jeder Adresse des Hosts auf Port 80 im Container ab [2]. Die Docker-Dokumentation sagt selbst: „Publishing container ports is insecure by default.“ Der Port ist dann auch für die Außenwelt verfügbar [2].
  2. Warum ufw das nicht sieht. ufw ist ein Frontend für die Ketten INPUT und OUTPUT. Docker leitet Container-Verkehr in der nat-Tabelle, sodass Pakete abgezweigt werden, bevor sie diese Ketten erreichen. Die Docker-Dokumentation sagt, die Pakete würden geroutet, „before the firewall rules can be applied, effectively ignoring your firewall configuration“ [1]. Ein Docker-Maintainer erklärte den Hintergrund: ufw unterstützt nur Regeln für den eigenen Host, Pakete an einen Container werden aber geroutet und laufen deshalb nie durch die Kette, in der die ufw-Regeln liegen [7]. Das ist dokumentiertes Verhalten und kein Fehler in deinem Setup.
  3. Compose macht dasselbe. Eine Zeile wie - "8080:80" ohne Host-IP bindet an alle Schnittstellen, und die Compose-Referenz warnt, dass das den Container direkt ins Internet stellen kann, wenn der Host eine öffentliche IP hat, und Firewall-Regeln des Hosts umgeht [5]. Docker veröffentlicht außerdem standardmäßig auch über IPv6: Ohne Host-Adresse wird ein Port auf 0.0.0.0 und [::] veröffentlicht [2].
  4. Schicht 1: die Firewall des Anbieters. Firewalls beim VPS-Anbieter filtern den Verkehr, bevor er deine Instanz erreicht. Contabo beschreibt seine Firewall als Filter auf Netzwerkebene, bei der aller eingehender Verkehr ab der Aktivierung verworfen wird [9]. Hostinger sagt, die Paketfilterung geschehe, bevor eingehender Verkehr den Server erreicht, ein leeres Regelwerk blockiere alles, und Änderungen griffen in zwei Minuten oder weniger [10]. Die Cloud Firewalls von Hetzner sind zustandsbehaftet und verwerfen neuen eingehenden Verkehr, solange keine Regel ihn erlaubt [11]. Weil diese Firewalls außerhalb deiner virtuellen Maschine sitzen, können Dockers Regeln sie nicht umgehen (unsere Einschätzung). Erlaube nur, was du bedienst: SSH (am besten nur von deiner eigenen IP-Adresse), Port 80 und Port 443. Kenne die Grenzen: Die Firewall von Contabo erfasst nur eingehenden Verkehr [9], und Hostinger sagt, seine Firewall ersetze keinen DDoS-Schutz [10].
  5. Schicht 2: Veröffentlichte Ports an 127.0.0.1 binden. Setze die Host-Adresse in jede Portzuordnung: -p 127.0.0.1:8080:80 auf der Kommandozeile oder "127.0.0.1:8080:80" in Compose. Dann erreicht nur der Docker-Host den Port [2][5]. Ein Reverse Proxy auf dem Host leitet den öffentlichen Verkehr dorthin weiter. Ein Versionshinweis: In Versionen vor 28.0.0 erreichen Hosts im selben Layer-2-Segment Ports, die an localhost veröffentlicht sind, prüfe also docker version [2]. Container, die nur miteinander sprechen, etwa eine Datenbank, bekommen gar keine ports:-Zeile [2]. Als Sicherheitsnetz kannst du die Standardbindung in der Daemon-Konfiguration ändern, das zeigt das Beispiel unten [2]. Eine ausdrückliche Host-IP in einer Zuordnung gilt weiterhin vorrangig.
  6. Schicht 3: die Kette DOCKER-USER. Docker legt eine Kette namens DOCKER-USER an, die vor Dockers eigenen Regeln abgearbeitet wird. Willst du Pakete für veröffentlichte Ports filtern, füge dort Regeln ein. Regeln, die an die Kette FORWARD angehängt werden, laufen nach Dockers Regeln [3]. Zwei Haken: Die Pakete haben bei der Ankunft die Adressumsetzung schon hinter sich, gewöhnliche Optionen treffen also auf die interne Adresse und den internen Port des Containers, und für den ursprünglichen Port braucht es die Erweiterung conntrack, die laut Dokumentation Leistung kosten kann [3][8]. Der zweite Haken: Von Hand angelegte Regeln bleiben nicht erhalten, iptables-Regeln werden beim Neustart geleert [17]. Die Docker-Dokumentation behandelt Persistenz nicht. Das Speichern des gesamten Regelwerks würde auch Dockers eigene Regeln sichern, halte deine DOCKER-USER-Zeilen deshalb in einem kleinen Skript, das nach dem Start von Docker läuft (unsere Einschätzung, bei Bytearium nicht getestet).
  7. Was du in Ruhe lassen solltest. Setze "iptables": false nicht in der Daemon-Konfiguration. Es bricht wahrscheinlich die Container-Netzwerke, und die Ports deiner Container werden für Hosts im lokalen Netz erreichbar [1]. Ändere Dockers eigene Regeln nicht [1]. Das mit Docker 29.0.0 eingeführte nftables-Backend ist als experimentell markiert, hat keine Kette DOCKER-USER, und deine alten DOCKER-USER-Regeln werden ignoriert, solange kein Sprung in die Kette mehr existiert [4]. Die Installationsseite für Ubuntu sagt weiterhin, Docker sei nur mit iptables-nft und iptables-legacy kompatibel, und mit nft angelegte Regeln würden nicht unterstützt (Stand 25.10.2026) [6].
  8. Host-Networking ist anders. Für host-, ipvlan- und macvlan-Netzwerke legt Docker keine Firewall-Regeln an [1]. Unsere Einschätzung: Ein Container im Host-Netzwerk verhält sich wie ein Prozess auf dem Host, sodass ufw für ihn gilt. Teste das, statt uns zu glauben.

Ein Praxisbeispiel

Ein VPS-Setup von Bytearium haben wir nicht erfasst (Stand 25.10.2026), deshalb ist das ein allgemeiner Ablauf für deinen eigenen Server und kein Bericht über unseren. Beginne mit einem Snapshot im Panel deines Anbieters. Ein Snapshot ist ein Zwischenstand, kein Backup. Contabo löscht Snapshots nach 30 Tagen und begrenzt ihre Zahl je nach Tarif [12]. Hostinger hält einen Snapshot, der nach 1 Tag verfällt und verschwindet, wenn du das Betriebssystem neu installierst [13]. Öffne dann die Anbieter-Firewall: TCP 22 von deiner eigenen IP-Adresse, TCP 80 und TCP 443 erlauben, sonst nichts. Lass deine aktuelle SSH-Sitzung offen und öffne eine zweite, um zu beweisen, dass du dich noch anmelden kannst. Sperrt dich eine Regel aus, bietet Hostinger im Dashboard ein Zurücksetzen der Firewall an [21].

Als Nächstes ufw für die Dienste des Hosts selbst. Das Aktivieren von ufw schließt alle Ports einschließlich SSH, erlaube SSH also zuerst [14][15]:

sudo ufw allow 22/tcp
sudo ufw enable
sudo ufw status verbose

Sieh nun nach, was Docker schon veröffentlicht hat. Lauschen heißt nicht erreichbar, denn ein Port kann LISTEN zeigen, obwohl eine Firewall ihn blockiert [15]:

docker ps
sudo ss -tlnp

Einträge wie 0.0.0.0:5432 oder [::]:5432 sind die, die du ändern musst. Eine sichere Compose-Datei veröffentlicht nur den Webdienst, nur auf der Loopback-Adresse, und gibt der Datenbank keine Ports. Die Image-Tags unten waren am 25.10.2026 auf Docker Hub gelistet [19][20]; die Volumes sind weggelassen, damit das Beispiel kurz bleibt, und DB_PASSWORD stammt aus einer .env-Datei neben der Compose-Datei, die nicht ins Git gehört:

services:
  web:
    image: nginx:1.30.6
    ports:
      - "127.0.0.1:8080:80"
  db:
    image: postgres:18.6
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD}

Dein Reverse Proxy auf dem Host lauscht auf 80 und 443 und leitet an 127.0.0.1:8080 weiter. Das optionale Sicherheitsnetz gehört in /etc/docker/daemon.json; starte den Daemon danach neu. Es gilt für benutzerdefinierte Bridge-Netzwerke, die Compose anlegt, und betrifft keine Swarm-Dienste [2]:

{
  "default-network-opts": {
    "bridge": {
      "com.docker.network.bridge.host_binding_ipv4": "127.0.0.1"
    }
  }
}

Muss ein veröffentlichter Port öffentlich bleiben, aber nur eine Adresse darf ihn nutzen, füge die Regeln hinzu, die die Docker-Dokumentation zum Einschränken externer Verbindungen zeigt. Füge zuerst die DROP-Regel ein und danach die Regel für bestehende Verbindungen, denn die zweite liegt dann oben in der Kette, vor dem DROP [3]. Ersetze eth0 durch deine öffentliche Schnittstelle und die Adresse durch deine. Beachte die Wirkung: Nur diese Adresse erreicht dann irgendeinen veröffentlichten Container-Port, nutze das also nur auf einem Server, der über Docker nichts Öffentliches anbietet:

sudo iptables -I DOCKER-USER -i eth0 ! -s 203.0.113.10 -j DROP
sudo iptables -I DOCKER-USER -m state --state RELATED,ESTABLISHED -j ACCEPT

Dockers Regeln betreffen auch IPv6, aber wir haben hier nicht geprüft, wie sich die Kette DOCKER-USER bei ip6tables in deiner Docker-Version verhält. Das ist ein weiterer Grund, die Bindung an Loopback und die Anbieter-Firewall zu bevorzugen, die IPv4 und IPv6 filtert [10]. Teste zum Schluss von außen, von einem anderen Rechner oder einem Handy-Hotspot: nc -zv <VPS-Adresse> 8080 sollte mit Zeitüberschreitung oder Ablehnung scheitern [15]. Die Nmap-Dokumentation erklärt den Unterschied: closed heißt, das Paket kam an und nichts lauscht, filtered heißt, eine Firewall hat es verworfen [18].

Zuletzt die SSH-Grundlagen. Lege deinen öffentlichen Schlüssel auf dem Server ab und teste die Anmeldung per Schlüssel zuerst in einer zweiten Sitzung. Setze dann diese Optionen in /etc/ssh/sshd_config und lade den SSH-Dienst so neu, wie deine Distribution es beschreibt [16]:

PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no

Die Handbuchseite rät, bei aktivem PAM entweder PasswordAuthentication oder KbdInteractiveAuthentication abzuschalten. Beide abzuschalten deckt es ab [16]. Fixiere deine Image-Tags wie oben und plane regelmäßige Updates ein, auch für Docker selbst, denn der Localhost-Fix kam erst mit 28.0.0 [2]. Ein Snapshot ersetzt kein Backup, denk deshalb an die 3-2-1-Regel.

Prüfliste

  • Mache einen Snapshot im Anbieter-Panel, bevor du eine Firewall-Einstellung änderst.
  • Lege eine Anbieter-Firewall an, die nur SSH, Port 80 und Port 443 erlaubt, und halte beim Testen eine zweite SSH-Sitzung offen.
  • Erlaube SSH in ufw, bevor du es aktivierst, und lies das Ergebnis von sudo ufw status verbose.
  • Liste mit docker ps und sudo ss -tlnp auf, was veröffentlicht ist, und korrigiere jeden Eintrag mit 0.0.0.0 oder [::], den du nicht beabsichtigt hast.
  • Binde jeden veröffentlichten Port an 127.0.0.1, außer er muss ins Internet.
  • Entferne die ports:-Zeile bei Containern, die nur mit anderen Containern sprechen.
  • Stelle Webdiensten einen Reverse Proxy voran und lass ihn das Einzige auf den Ports 80 und 443 sein.
  • Ergänze DOCKER-USER-Regeln nur für Ports, die öffentlich, aber eingeschränkt bleiben müssen, und halte sie in einem Skript, das nach dem Docker-Start läuft.
  • Teste jede Änderung von außerhalb deines Netzes und prüfe mit docker version, ob du 28.0.0 oder neuer hast.
  • Fixiere Image-Tags und schalte Root- und Passwort-Anmeldung für SSH ab, sobald die Anmeldung per Schlüssel läuft.

Primärquellen

Diese Quellen sind die Grundlage der Seite. Das heißt nicht, dass jede Aussage einzeln nachgeprüft wurde; ob ein Prüfbeleg vorliegt, steht oben unter „Stand“. Einordnung und Prüfliste sind unsere eigene Arbeit.

Für diese Fassung liegt noch kein dokumentierter Prüfbeleg vor. Der Text stützt sich auf die genannten Quellen.

Entstehung: Bytearium erstellt diese Seiten mit Unterstützung von KI und verantwortet die Veröffentlichung. KI kann sich irren, deshalb nennen wir Quellen und Prüfstand offen.

  1. DockerPacket filtering and firewalls
  2. DockerPort publishing and mapping
  3. DockerDocker with iptables
  4. DockerDocker with nftables
  5. DockerDefine services in Docker Compose, ports
  6. DockerInstall Docker Engine on Ubuntu
  7. Docker for Linux projectDocker bypasses ufw firewall rules
  8. Moby projectSummary of the issues about custom iptables rules
  9. ContaboFirewall
  10. HostingerHow to use a managed VPS firewall at Hostinger
  11. HetznerFirewalls FAQ
  12. ContaboHow do I create a snapshot of my server?
  13. HostingerHow to back up or restore a Hostinger VPS
  14. UbuntuFirewall
  15. MicrosoftTroubleshoot connectivity issues by using Uncomplicated Firewall (UFW) in Azure Ubuntu VMs
  16. Linux man-pagessshd_config(5)
  17. Debian Wikiiptables
  18. Nmap projectPort Scanning Basics
  19. Docker Hubnginx, Official Image
  20. Docker Hubpostgres, Official Image
  21. HostingerHow to set up a firewall at Hostinger VPS

Auf dieser Seite gibt es keine Werbung und keine Partnerlinks.