Zum Hauptinhalt springen
  1. Artikel/

Zug-WLAN funktioniert nicht? Docker könnte schuld sein

Autor
Christian Schärf

Vor einiger Zeit war ich mit dem ICE der Deutschen Bahn unterwegs und wollte das Bord-WLAN nutzen. Das die Internetverbindung nicht funktioniert, kommt bekanntlich ab und zu vor. Ich konnte allerdings nicht einmal das Captive Portal1 erreichen, was schon einigermaßen selten ist.

Docker-Netzwerktypen

Wenn in diesem Artikel Docker-Netzwerken erwähnt werden, sind immer Netzwerke des Typs bridge gemeint.

Auflösung

Das Bord-WLAN im ICE hat den IP-Adressbereich 172.18.0.0/16. Dieser war schon durch ein zuvor erstelltes Docker-Netzwerk belegt. Dadurch wurden alle Pakete, die zum Bord-WLAN gehen sollten, in das Docker-Netzwerk geroutet.

Analyse
#

Zuvor hatte ich mit Docker gearbeitet und ein Netzwerk des Typs bridge erstellt. Bei diesem Typ bekommen mit dem Netzwerk verbundene Container sowie der Host IP-Adressen zugewiesen, unter denen sie erreichbar sind. Docker verwendet dafür standardmäßig für jedes Netzwerk einen /16-Block des IP-Adressbereich von 172.16.0.0/122, der für private Netzwerke reserviert und daher nicht Teil des globalen Internets ist.3

Das erste Adressblock 172.16.0.0/16 wird dabei nicht verwendet, da dieser häufig für lokale Netzwerke verwendet wird.4 Der nächste Block 172.17.0.0/16 ist der standardmäßige Adressbereich des Standard-Netzwerks bridge:

❯ docker network inspect bridge 
[
    {
        "IPAM": {
            "Config": [
                {
                    "Subnet": "172.17.0.0/16",
                    "IPRange": "",
                    "Gateway": "172.17.0.1"
                }
            ]
        },
    }
]

Alle weiteren Blöcke werden aufsteigend für Netzwerke verwendet, die mit docker network create erstellt werden. Das erste (hier demo genannt) erhält:

❯ docker network inspect demo 
[
    {
        "IPAM": {
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "IPRange": "",
                    "Gateway": "172.18.0.1"
                }
            ]
        },
    }
]

Wie bereits erwähnt, wird der Block 172.16.0.0/16 übersprungen, weil er häufig für lokale Netzwerke verwendet wird. Alle anderen Bereiche sind üblicherweise ungenutzt. Eine verhängnisvolle Ausnahme stellt das Bord-WLAN des ICE dar: Dieses verwendet den Bereich 172.18.0.0/16, denselben wie das erste nutzererstellte Docker-Netzwerk:

❯ ip a
2: wlp2s0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1440 qdisc noqueue state UP group default qlen 1000
    link/ether 01:23:45:67:89:ab brd ff:ff:ff:ff:ff:ff
    altname wlx74d83e3e31a4
    inet 172.18.0.1/16 brd 172.18.255.255 scope global dynamic noprefixroute wlp2s0
       valid_lft 598sec preferred_lft 598sec
    inet6 fe80::cdd8:684:4c94:dc3d/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever
12: br-d9ec15037321: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default 
    link/ether 76:8c:41:da:45:99 brd ff:ff:ff:ff:ff:ff
    inet 172.18.0.1/16 brd 172.18.255.255 scope global br-d9ec15037321
       valid_lft forever preferred_lft forever

In Folge werden alle Pakete, die eigentlich für das Bord-WLAN bestimmt sind, vergeblich ins Docker-Netzwerk geroutet und gehen verloren.

Abhilfe
#

Für schnelle Abhilfe reicht es aus, das problematische Docker-Netzwerk mit docker network rm zu löschen und mit docker network create neu zu erstellen. Die Container müssen danach gegebenenfalls neu verbunden werden.

Warnung

Vorher sollten alle (einschließlich nicht laufende) Container, welche mit dem Netzwerk verbunden sind, getrennt werden.

Dies funktioniert, weil Docker bei der (Neu-)Erstellung des Netzwerkes bereits benutzte IP-Adressbereiche überspringt. Nur kann Docker verständlicherweise bei der Erstellung nicht wissen, ob ein bestimmter IP-Adressbereich in der Zukunft vielleicht benötigt wird.

Lösung
#

Die von der Docker-Engine verwendeten IP-Adressbereiche können in /etc/docker/daemon.json5 konfiguriert werden. Dabei werden nutzbare Adressbreiche definiert und für jeden Bereich die Größe des Netzwerks (Anzahl Bits des Netzwerkpräfix) zugewiesen.

IPv4
#

Für IPv4 bietet es sich an, einen Teil des für private Netzwerke reservierten IP-Adressbereichs 10.0.0.0/83 zu verwenden, beispielsweise:

{
    "default-address-pools": [
        {
            "base": "10.128.0.0/9",
            "size": 16
        }
    ]
}

Hier wird der zweite Teil des oben genannten Bereichs verwendet, der bei 10.128.0.0 beginnt. Jedes Docker-Netzwerk hat ein Präfix von /16 Bit. Somit stehen 128 Netzwerke mit jeweils 2¹⁶ = 65536 Adressen zur Verfügung.

Allerdings sollte beachtet werden, dass auch dieser IP-Adressbereich mit anderen lokalen Netzwerken (vor allem öffentlichen WLANs und Firmennetzwerke) kollidieren kann.

IPv6
#

Seit Version 27 der Docker-Engine ist IPv6 nicht mehr experimentell.6 Werden keine IPv6-Adressbereiche angegeben (in /etc/docker/daemon.json oder über die Kommandozeilenoption --subnet), weist Docker einen Bereich aus den Unique Local Adresses zu.7

Bei der Erstellung eines Netzwerkes kann angegeben werden, dass ausschließlich IPv6 verwendet werden soll:

docker network create --ipv6 --ipv4=false ipv6only

Für Compose können diese Einstellungen ebenfalls vorgenommen werden:8

networks:
  ipv6only:
    enable_ipv4: false
    enable_ipv6: true

Leider bestehen derzeit noch einige Einschränkungen:

  • Weiterleitungen eines Host-IPv4-Ports an den Container ist anscheinend nicht möglich.9 Dies bedeutet, dass der Container von außerhalb nur über IPv6 erreichbar ist. Für den Betrieb eines Servers dürfte dies in den meisten Fällen ein Ausschlusskriterium sein, für die lokale Entwicklung sollte es in Ordnung sein.
Note

In der umgekehrten Richtung gibt es diese Einschränkung übrigens nicht: Ein Host-IPv6-Port lässt sich an einen Container, der nur eine IPv4-Adresse hat, weiterleiten.

  • Es gibt anscheinend keine Möglichkeit, ausschließliches IPv6 als Standard für neue Netzwerke einzustellen.
  • Ebenfalls ist es anscheinend nicht möglich, das vordefinierte Netzwerk bridge ausschließlich mit IPv6 zu konfigurieren.

Schlussbemerkungen
#

Keine der beteiligten Parteien hat hier einen Fehler begangen. Die Wahl des Adressbereichs 172.18.0.0/16 seitens des Bord-WLANs ist nicht zu beanstanden, dafür ist dieser schließlich vorgesehen. Auch Docker muss sich seine IP-Adressen irgendwo hernehmen, hierfür die für lokale Netzwerke vorgesehenen Bereiche heranzuziehen, ist eine sinnvolle Wahl. Auch im größeren Adressbereich 10.0.0.0/8 (der standardmäßig von Podman für diesen Zweck verwendet wird), sind Kollisionen prinzipiell möglich. Schließlich gibt es keine IPv4-Adressbereiche, die für „Host-lokale“ Netzwerke reserviert sind.

Nur der deutlich größere Adressbereich der Unique Local Addresses von IPv6 schafft eine wirkliche Lösung. Dieser wird von Docker aber nicht standardmäßig verwendet und ist immer noch mit Einschränkungen behaftet. Anscheinend ist IPv6 für Docker immer noch Neuland.


  1. Das Captive Portal ist die Seite, auf die öffentliche Netzwerke weiterleiten, um typischerweise die Zustimmung zu den Nutzungsbedingungen einzuholen. ↩︎

  2. Wir verwenden die CIDR-Notation. Dabei gibt die Zahl hinter dem Schrägstrich an, wie viele Bit der IP-Adresse zum Netzwerkpräfix gehören. Das Netzwerk 172.16.0.0/12 umfasst also die IP-Adressen von 172.16.0.0 bis 172.31.255.255, da für diese Adressen die ersten 12 Bit gleich sind. ↩︎

  3. https://en.wikipedia.org/wiki/List_of_reserved_IP_addresses#IPv4 ↩︎ ↩︎

  4. https://docs.docker.com/engine/network/#automatic-subnet-allocation ↩︎

  5. Unter der Annahmne, dass ein sinnvolles Betriebssystem verwendet wird. ↩︎

  6. https://docs.docker.com/engine/release-notes/27/#ipv6 ↩︎

  7. https://docs.docker.com/engine/daemon/ipv6/#dynamic-ipv6-subnet-allocation ↩︎

  8. https://docs.docker.com/reference/compose-file/networks/#enable_ipv4 ↩︎

  9. https://github.com/moby/moby/issues/49617 ↩︎