SSD Nodes Learn Hosting plans →
Anleitungen Matt ConnorVon Matt Connor

UFW und IPv6: Warum Ihr VPS trotzdem offen ist

Ihre IPv4-Regeln greifen nicht automatisch für IPv6. Erfahren Sie, warum Dienste auf Ubuntu 24.04 offen sind und wie Sie die Lücke auf Ihrem VPS schließen.

Die IPv6-Firewall-Falle in einem Satz

Ihre Firewall schützt IPv4. Ihr VPS hat mit hoher Wahrscheinlichkeit zusätzlich eine öffentliche IPv6-Adresse, und viele Dienste lauschen standardmäßig darauf. Wenn Ihre Firewall nur IPv4 abdeckt oder Sie sich auf eine Cloud-Firewall verlassen, die nur IPv4 filtert, ist jeder dieser Dienste über IPv6 aus dem gesamten Internet erreichbar, während Ihre IPv4-Seite gesichert wirkt. Sie testen einen Port mit curl, sehen eine abgelehnte Verbindung und fühlen sich sicher. Ein Angreifer verbindet sich über IPv6 mit demselben Port und erhält Zugriff.

Dieser Leitfaden zeigt, wo diese Lücke auf einem normalen Ubuntu-24.04-VPS entsteht, wie Sie genau ermitteln, was Sie erreichbar machen, und wie Sie die Lücke schließen. UFW ist hier nicht der Verursacher. Bei einer modernen Ubuntu-Installation unterstützt UFW IPv6 bereits. Die Erreichbarkeit entsteht durch die umgebenden Ebenen und durch Dienste, von denen Sie nicht wussten, dass sie lauschen.

Warum Ihr VPS überhaupt IPv6 verwendet

Nahezu jeder VPS verfügt heute neben seiner IPv4-Adresse über eine öffentliche IPv6-Adresse, häufig sogar über ein ganzes /64. Prüfen Sie Ihre Adresse:

ip -6 addr show scope global
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> ...
    inet6 2001:db8:2a::1/64 scope global

Das 2001:db8:2a::1 in dieser Anleitung ist ein Platzhalter aus 2001:db8::/32. Es handelt sich um ein für Dokumentationszwecke reserviertes Präfix, das niemals über das Internet geroutet wird. Die echte Adresse, die Ihr Server in dieser Zeile ausgibt, ist aus dem gesamten Internet erreichbar, genau wie Ihre IPv4-Adresse. Sehen Sie sich nun an, welche Dienste auf Verbindungen warten:

sudo ss -tlnp
State   Recv-Q  Local Address:Port   Process
LISTEN  0       0.0.0.0:22           sshd
LISTEN  0       [::]:22              sshd
LISTEN  0       127.0.0.1:5432       postgres
LISTEN  0       [::]:8080            docker-proxy

Lesen Sie die Spalte Local Address genau. 0.0.0.0:22 bedeutet „auf jeder IPv4-Adresse auf Verbindungen warten“. [::]:22 bedeutet „auf jeder IPv6-Adresse auf Verbindungen warten“. 127.0.0.1:5432 ist an die Loopback-Schnittstelle gebunden und daher überhaupt nicht öffentlich erreichbar. Die Postgres-Zeile ist deshalb sicher. Die beiden [::]-Zeilen nehmen Verbindungen aus dem gesamten Internet über IPv6 an. Bei docker-proxy handelt es sich um die Art von Dienst, dessen Start man leicht vergisst.

Die meisten Daemons binden standardmäßig an ::, weil ein ::-Socket unter Linux normalerweise auch IPv4-Verbindungen annimmt. Die Standardeinstellung eines neuen Servers lautet daher: „Auf beiden Protokollstapeln und überall antworten.“ Ihre Firewall ist das Einzige, was davorsteht. Deshalb ist eine Firewall, die nur einen Protokollstapel berücksichtigt, ein ernstes Problem.

Woher die IPv6-Lücke tatsächlich kommt

Dafür gibt es drei häufige Ursachen. Auf einem bestimmten Server kann eine davon oder mehrere gleichzeitig zutreffen.

1. Eine Cloud-Firewall, die nur IPv4 filtert. Viele Provider-Firewalls und Security-Group-Produkte wurden ursprünglich für IPv4 entwickelt. Sie ignorieren IPv6 entweder oder benötigen separate IPv6-Regeln, die Sie manuell hinzufügen müssen. Wenn Ihre einzige Firewall die im Provider-Dashboard ist und IPv6 nicht abdeckt, sind Ihre [::]-Dienste unabhängig von der Anzeige für Port 22 unter IPv4 offen. Lesen Sie die Dokumentation Ihres Providers zur Firewall und suchen Sie gezielt nach dem Begriff IPv6.

2. Manuell erstellte iptables-Regeln ohne ip6tables. Der Befehl iptables bearbeitet nur die IPv4-Tabellen. Für IPv6 gibt es den vollständig separaten Befehl ip6tables mit eigenen Regeln. Wenn Ihr Firewall-Skript zahlreiche iptables -A INPUT ...-Zeilen enthält, aber keine entsprechenden ip6tables-Regeln, ist Ihre IPv6-Firewall leer. Eine leere INPUT-Kette mit der Standardrichtlinie ACCEPT lässt sämtlichen Datenverkehr zu:

sudo ip6tables -L INPUT -n
Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Diese Ausgabe zeigt die gesamte Falle auf einem Bildschirm. IPv4 wird gefiltert, IPv6 akzeptiert den gesamten Datenverkehr.

3. Docker veröffentlicht Ports direkt an Ihrer Firewall vorbei. Wenn Sie docker run -p 8080:80 ausführen, fügt Docker seine eigenen Regeln vor den UFW-Regeln ein. Deshalb bleibt ein veröffentlichter Port erreichbar, selbst wenn ufw status diesen Port als verweigert anzeigt. Aktuelle Docker-Versionen verwalten standardmäßig ip6tables-Regeln. Wenn IPv6 im Netzwerk des Containers aktiviert ist, gilt dies auch für IPv6. Warum Docker UFW umgeht und wie Sie Container-Ports korrekt filtern erklärt den Mechanismus und die möglichen Lösungen. Unter Die Grundlagen von Docker Compose auf einem VPS erfahren Sie, wie diese veröffentlichten Ports definiert werden.

Hinweis zum IPv6-Schalter von UFW. UFW unterstützt IPv6. Der Schalter befindet sich in einer Datei. Prüfen Sie ihn:

grep IPV6 /etc/default/ufw

Aktuelle Ubuntu-Versionen enthalten IPV6=yes. Daher wendet UFW jede Regel auf beide Protokollstacks an. Wenn Sie IPV6=no sehen, stammt dies möglicherweise von einem alten Image oder einer alten Anleitung. IPv6 bleibt dadurch nicht ungeschützt. Bei dieser Einstellung verwirft ein aktives UFW sämtlichen IPv6-Datenverkehr mit Ausnahme der Loopback-Schnittstelle. Das Problem tritt dann umgekehrt auf: Alles, was Sie über IPv6 bereitstellen wollten, antwortet nicht mehr. Setzen Sie den Wert auf yes, wenn UFW IPv6 anhand Ihrer Regeln filtern soll, statt IPv6 vollständig zu blockieren.

Sehen Sie genau, was Sie erreichbar machen

Raten Sie nicht. Messen Sie es von außen. Listen Sie zuerst Ihre Listener auf und notieren Sie jeden, der an :: gebunden ist:

sudo ss -tlnp | grep '::'

Stellen Sie anschließend von einem anderen Rechner eine Verbindung zur öffentlichen IPv6-Adresse des Servers her und testen Sie einen Port, den Sie für geschlossen halten:

curl -6 -v http://[2001:db8:2a::1]:8080/

Wenn dabei eine Seite oder ein Banner zurückgegeben wird, ist der Port über IPv6 offen. Ein geschlossener Port liefert Connection refused oder einen Timeout. Diese beiden Fehler sind nicht dasselbe Signal. Der Unterschied zwischen „refused“ und „timed out“ zeigt, ob der Host geantwortet und die Verbindung abgewiesen hat oder ob eine Firewall Ihr Paket still verworfen hat. Für ein vollständiges Bild scannen Sie die IPv6-Adresse mit nmap von außerhalb des Servers:

nmap -6 2001:db8:2a::1

Jeder Port, den nmap über IPv6 als offen meldet, ist für das gesamte Internet erreichbar, unabhängig davon, was Ihr IPv4-Scan gezeigt hat. Vergleichen Sie die IPv4- und IPv6-Scans nebeneinander. So finden Sie die Lücke am schnellsten: Alles, was auf -6 offen, auf IPv4 jedoch geschlossen ist, ist ein Dienst, den Ihre Firewall nicht berücksichtigt.

Die Lücke schließen

Lassen Sie UFW beide Protokollstacks abdecken und verweigern Sie standardmäßig den Zugriff. Bestätigen Sie die Umschaltung. Legen Sie dann eine standardmäßige Deny-Regel für eingehenden Verkehr fest und erlauben Sie nur, was Sie benötigen:

sudo sed -i 's/^IPV6=no/IPV6=yes/' /etc/default/ufw
sudo ufw default deny incoming
sudo ufw allow 22/tcp
sudo ufw enable
sudo ufw status verbose

Wenn UFW beim Umschalten von IPV6=yes bereits aktiv war, wird die Änderung erst wirksam, nachdem Sie sudo ufw reload ausgeführt haben.

ufw status listet jede Regel zweimal auf: einmal ohne Zusatz und einmal mit dem Suffix (v6). Wenn die Zeilen mit (v6) angezeigt werden, filtert UFW IPv6:

22/tcp                     ALLOW IN    Anywhere
22/tcp (v6)                ALLOW IN    Anywhere (v6)

Wenn Sie iptables manuell verwalten, müssen Sie jede Regel in ip6tables übernehmen oder auf nftables umsteigen. Dessen inet-Tabellen decken IPv4 und IPv6 zentral ab und vermeiden diese Fehlerklasse vollständig. Eine einzelne inet-Filtertabelle in nftables ist die sauberste Lösung, wenn Sie die Regeln selbst schreiben. Wenn Ihr VPS statt Ubuntu Rocky oder AlmaLinux verwendet, gibt es kein UFW zu konfigurieren. In diesem Fall verwalten Sie stattdessen firewalld als Frontend. Es wendet seine Zonenregeln gleichzeitig auf beide Protokollstacks an.

Binden Sie Dienste, die nicht öffentlich erreichbar sein sollen, an die Loopback-Schnittstelle. Eine Datenbank, ein Administrationspanel oder ein Metrik-Endpunkt benötigt nur selten überhaupt eine öffentliche Adresse. Binden Sie den Dienst an 127.0.0.1 und ::1, damit er von vornherein nicht an einer routbaren Adresse lauscht. Setzen Sie bei Postgres listen_addresses = 'localhost'. Binden Sie einen Anwendungsserver an 127.0.0.1 und schalten Sie einen Reverse Proxy davor. Den Listener zu schließen ist sicherer als ihn per Firewall zu schützen, weil dann keine erreichbare Schnittstelle vorhanden ist.

Verlassen Sie sich nicht darauf, dass UFW die veröffentlichten Docker-Ports schützt. Veröffentlichen Sie Container-Ports an eine bestimmte Adresse statt an jeder Schnittstelle, zum Beispiel -p 127.0.0.1:8080:80. Dadurch ist der Port nur vom Host und von Diensten erreichbar, an die Sie den Zugriff bewusst per Proxy weiterleiten. Wenn ein Container tatsächlich öffentlich erreichbar sein muss, stellen Sie einen Traefik-Reverse-Proxy davor und veröffentlichen Sie nur den Proxy, nicht jede einzelne Anwendung.

Fügen Sie Ihrem Provider-Firewallregeln für IPv6 hinzu oder akzeptieren Sie, dass diese Firewall IPv6 nicht schützt. Lassen Sie diese Aufgabe stattdessen von UFW oder nftables auf dem Host übernehmen.

Prüfen Sie, ob der Port tatsächlich geschlossen ist

Führen Sie den externen Test nach Ihren Änderungen erneut aus:

curl -6 -v http://[2001:db8:2a::1]:8080/
nmap -6 2001:db8:2a::1

Der Port, der zuvor geantwortet hat, sollte nun Verbindungen ablehnen oder in einen Timeout laufen. nmap sollte ihn als gefiltert oder geschlossen melden. Wenn ein Port weiterhin offen ist, prüfen Sie die drei oben genannten Quellen erneut: Ein Dienst ist noch an :: gebunden, ohne dass eine Regel davor greift; eine Docker-Regel steht vor UFW; oder eine Provider-Firewall hat IPv6 überhaupt nicht berücksichtigt.

Noch sicherer ist es, sensible Dienste vollständig vom öffentlichen Internet fernzuhalten. Platzieren Sie SSH und Administrationsoberflächen hinter einem WireGuard-VPN und begrenzen Sie ihre Ports mit der Firewall so, dass sie nur über den Tunnel antworten. Damit stellt sich die Frage nach der IPv6-Exponierung für diese Dienste nicht mehr. Um Brute-Force-Scans zu verlangsamen, die weiterhin öffentlich erreichbare Dienste treffen, setzen Sie Fail2ban vor SSH zusätzlich zu einer standardmäßig alles ablehnenden Firewall ein.

Wenn Ports für Sie noch neu sind, lesen Sie zuerst was Ports sind und wie Dienste auf Verbindungen warten.

FAQ

Blockiert UFW IPv6 standardmäßig?

Bei einer aktuellen Ubuntu-24.04-Installation: ja. UFW liest IPV6=yes aus /etc/default/ufw und wendet jede Regel auf IPv4 und IPv6 an. ufw status zeigt die IPv6-Regeln mit dem Suffix (v6) an. Problematisch wird es, wenn Sie sich auf eine Provider-Firewall verlassen, die nur IPv4 filtert, wenn selbst geschriebene iptables-Regeln kein Gegenstück für ip6tables haben oder wenn Docker einen Port an UFW vorbei veröffentlicht. IPV6=no (aus einem alten Image oder einer alten Anleitung) aktiviert IPv6 nicht. Bei einem aktiven UFW verwirft es sämtlichen IPv6-Verkehr außer auf dem Loopback-Interface. Prüfen Sie den Schalter mit grep IPV6 /etc/default/ufw.

Wie prüfe ich, welche Dienste mein VPS über IPv6 erreichbar macht?

Führen Sie sudo ss -tlnp aus. Achten Sie auf jeden Listener, dessen lokale Adresse mit [::] beginnt. Das bedeutet, dass er auf allen IPv6-Interfaces antwortet. Testen Sie anschließend von einem anderen Rechner aus die öffentliche IPv6-Adresse des Servers direkt mit curl -6 -v http://[YOUR:IPV6::ADDR]:PORT/. Alternativ können Sie sie mit nmap -6 YOUR:IPV6::ADDR scannen. Jeder Port, der beim IPv6-Scan offen, bei IPv4 aber geschlossen ist, stellt eine Sicherheitslücke in der Konfiguration dar.

Warum erreiche ich den Port meines Docker-Containers, obwohl UFW ihn als blockiert anzeigt?

Docker fügt beim Veröffentlichen eines Ports mit -p eigene Firewall-Regeln vor den UFW-Regeln ein. Daher bleibt der veröffentlichte Port erreichbar, obwohl ufw status ihn als verweigert aufführt. Das geschieht bei IPv4 und auch bei IPv6, wenn die IPv6-Unterstützung von Docker aktiviert ist. Veröffentlichen Sie den Port an eine bestimmte Adresse wie -p 127.0.0.1:8080:80. Alternativ können Sie den Container hinter einem Reverse Proxy betreiben und nur den Proxy veröffentlichen.

Benötige ich weiterhin eine IPv6-Firewall, wenn meine IPv4-Firewall sicher konfiguriert ist?

Ja. IPv4 und IPv6 sind getrennte Netzwerkstacks mit eigenen Firewall-Regeln. Ein vollständiger Satz von IPv4-Regeln hat keine Wirkung auf IPv6-Verkehr. Wenn Ihr VPS eine öffentliche IPv6-Adresse besitzt, was bei fast allen VPS der Fall ist, bleibt jeder Dienst, der auf :: lauscht, über IPv6 erreichbar, bis eine IPv6-Firewall-Regel oder eine Bindung an das Loopback-Interface den Zugriff verhindert.

Wie lasse ich einen Dienst nur auf IPv4 oder nur auf localhost lauschen?

Legen Sie die Bind-Adresse des Dienstes in dessen eigener Konfiguration fest. Binden Sie ihn an 127.0.0.1, wenn er nur auf dem IPv4-Loopback-Interface lauschen soll, oder an 0.0.0.0, wenn er auf allen IPv4-Adressen, aber nicht auf IPv6 lauschen soll. Postgres verwendet listen_addresses, SSH verwendet ListenAddress. Die meisten Anwendungsserver bieten dafür eine Host- oder Bind-Option. Prüfen Sie das Ergebnis mit sudo ss -tlnp. Kontrollieren Sie außerdem, dass Local Address nicht mehr [::] anzeigt.