Container antworten im Büro-LAN und hören in dem Moment auf zu antworten, in dem sich die Firmen-VPN verbindet. Im Container hat sich nichts geändert: kein Neustart, kein Image-Update, keine Konfigurationsänderung. Geändert hat sich die Routing-Tabelle des Hosts, in der eine vom Tunnel hinzugefügte Route nun die von Docker vergebene Adresse abdeckt. Bevor Sie ein Subnetz bearbeiten oder eine VPN-Ausnahme beantragen, legen Sie beide Adresspläne nebeneinander, denn die Reparaturen kosten sehr unterschiedlich viel Zeit.

Lesen Sie die Route, bevor Sie die Konfiguration ändern

Beginnen Sie mit dem Netzwerk, das Docker tatsächlich gebaut hat. Der IPAM-Block ist der Adressplan, und das Subnetz ist die Zeile, auf die es ankommt:

docker network inspect project_default --format '{{json .IPAM.Config}}'
# [{"Subnet":"172.18.0.0/16","Gateway":"172.18.0.1"}]

Compose vergibt standardmäßig ein /16 pro neuem Projekt, sodass zwei Dienste in einem Bereich von 65,536 Adressen liegen können. Genau diese Breite macht Überschneidungen häufig: Ein zufällig gewähltes /16 ist groß genug, um einen Standort zu verschlucken, den Sie über den Tunnel erreichen, und niemand merkt es, bis der Tunnel hochkommt.

Vergleichen Sie dann die Routenauswahl für eine Containeradresse, einmal mit abgebautem und einmal mit aufgebautem Tunnel:

ip route get 172.18.0.7
# 172.18.0.7 dev br-3f2a9c src 172.18.0.1

Zwei Antwortformen führen an verschiedene Orte. Wenn die Ausgabe tun0, wg0 oder ein anderes Tunnelgerät nennt, ist ein VPN-Präfix mindestens so spezifisch wie die Bridge-Route, und das Paket erreicht den Container nie. Wenn sie weiterhin die Bridge nennt, kann der Host den Container erreichen, und Ihr Fehler ist Filterung, DNS oder ein Rückweg, der nur in eine Richtung scheitert.

Warum eine /16-Kollision leicht zu treffen ist

Docker zieht Bridge-Netze aus den privaten Pools, und der Standard-Pool stammt aus dem Block 172.16.0.0/12 in /16-Stücken. Firmennetze nutzen häufig denselben privaten Raum, und eine Fernzugriffs-VPN schiebt diese Präfixe an jeden Client, der sich verbindet. Wenn beide Seiten glauben, sie besäßen 172.18.0.0/16, hält eine Routing-Tabelle zwei widersprüchliche Ansprüche.

Die Präfixlänge entscheidet, nicht die Reihenfolge, in der Routen erscheinen. Ein Tunnel, der 10.0.0.0/8 schiebt, schlägt keine Bridge-Route 172.18.0.0/16, weil /8 weniger spezifisch ist. Ein Tunnel, der 172.18.0.0/16, oder 172.18.5.0/24 darin schiebt, schon. Deshalb funktioniert ein Laptop im Büronetz und scheitert im Hotel-WLAN mit derselben VPN: Der Client erhält unterschiedliche Präfixe.

Dieselbe Tabelle regelt die andere Richtung. Ein Container, der einen internen Host unter 10.8.4.20 braucht, schickt das Paket an sein Bridge-Gateway, und der Host routet es dann in den Tunnel. Dieser Weg funktioniert normalerweise, weil der Host das NAT übernimmt, er scheitert aber, wenn die Firmenseite nur die VPN-Adresse des Laptops akzeptiert oder wenn Reverse-Path-Filtering die asymmetrische Antwort verwirft.

Vergleichen Sie die drei Reparaturen

Es gibt nur drei Auswege, und sie unterscheiden sich darin, wer sie genehmigen muss und was bricht, während Sie einen anwenden.

RepairWho owns itWhat it costs
Renumber the container networkYouRecreates the network, restarts services, invalidates literal addresses in configs and scripts
Exclude your range from the tunnelNetwork teamChanges what every laptop sees, needs a change request and a client profile update
Reach the service through a published portYou and the clientOne port only, no direct container-to-container routing across the tunnel

Umnummerieren ist die einzige Reparatur, die Sie ohne Ticket abschließen können, und deshalb hören die meisten Anleitungen dort auf. Sie ist in einem jungen Projekt billig und teuer, sobald ein Migrationstool oder ein gespeicherter Verbindungsstring auf eine literale Adresse zeigt. Zählen Sie diese Referenzen, bevor Sie YAML bearbeiten, nicht danach.

Eine Ausnahme vom Tunnel ist die sauberste Antwort, wenn die Überschneidung in einem Bereich liegt, den Ihre Firma wirklich besitzt. Sie ist auch die langsamste und für einen Auftragnehmer nutzlos, der das Client-Profil nicht ändern kann. Ein veröffentlichter Port oder ein kleiner Reverse-Proxy vermeidet die Diskussion: Der Client spricht eine Adresse an, die der Tunnel bereits korrekt routet, und nur dieser Port muss erreichbar sein.

Fixieren Sie einen Bereich mit Absicht

Für ein einzelnes Projekt deklarieren Sie das Subnetz in der Compose-Datei, damit das Projekt nicht mehr aus dem gemeinsamen Pool zieht:

networks:
  default:
    ipam:
      config:
        - subnet: 10.201.7.0/24

Nehmen Sie diesen Bereich aus einem Register, das Ihr Team führt, nicht aus einem Beispiel, das Sie bei der Suche gefunden haben. Prüfen Sie ihn gegen die Präfixe, die die VPN schiebt, die Heim-LANs, von denen Menschen arbeiten, und die anderen Containernetzwerke auf demselben Host. Planen Sie dann die Neuerstellung, denn ein bestehendes Netzwerk behält sein altes Subnetz: docker compose down, dann up -d. docker network rm verweigert, solange Endpunkte angehängt sind, und jeder ipv4_address Eintrag in der Datei muss in den neuen Bereich fallen.

Für Netzwerke, die Docker selbst anlegt, setzen Sie einen Daemon-Pool in /etc/docker/daemon.json:

{
  "default-address-pools": [
    { "base": "10.201.0.0/16", "size": 24 },
    { "base": "10.202.0.0/16", "size": 24 }
  ]
}

Der Pool gilt für Netzwerke, die nach dem Neustart des Daemons erstellt werden, und Bridges, die bereits existieren, behalten ihre Adressen. Das ist bequem und leicht zu vergessen, also behandeln Sie ein altes Projekt als weiterhin überlappend, bis jemand sein Netzwerk neu erstellt. Dockers Dokumentation zum Engine-Netzwerk beschreibt die Zuteilungsregeln, falls Sie den Pool im Review verteidigen müssen.

Linux-Bridge und Docker Desktop scheitern unterschiedlich

Auf einem Linux-Host sitzen Container auf Bridges im Namespace des Hosts. Eine Tabelle entscheidet alles, also ist der obige Vergleich die ganze Geschichte: Lesen Sie die Bridge-Route, lesen Sie die Tunnelroute, und sehen Sie, welche für eine echte Containeradresse gewinnt.

Docker Desktop stellt Container hinter eine VM mit eigenem NAT. Der Host hat überhaupt keine Route zur Containeradresse; er erreicht den Dienst über einen veröffentlichten Port, meist gebunden an 127.0.0.1. Ein VPN-Client kann den Dienst daher ohne jede Subnetz-Überschneidung kaputt machen, indem er eine Route für den internen Bereich der VM installiert oder Loopback-Verkehr blockiert, solange der Tunnel verbunden ist. Wenn ip route get auf dem Host keine Route für die Containeradresse meldet, ist das zu erwarten und nicht der Fehler.

Die praktische Konsequenz ist, wo Sie zuerst hinsehen. Auf Linux beginnen Sie bei der Routing-Tabelle. Auf Desktop beginnen Sie beim veröffentlichten Port und den Firewall-Regeln des VPN-Clients und prüfen dann, ob das interne Subnetz der VM mit etwas kollidiert, das der Tunnel schiebt.

Prüfen Sie mit aufgebautem Tunnel

Verbinden Sie die VPN zweimal neu, bevor Sie es als behoben bezeichnen, denn ein Profil, das beim Verbinden Routen schiebt und beim Trennen zurückzieht, kann nach dem ersten Versuch korrekt aussehen. Führen Sie dann die Prüfungen aus, die der Client tatsächlich ausführt:

  1. ip route get vom Host aus, mit der echten Adresse des Containers.
  2. Eine Anfrage aus einem anderen Container, um zu beweisen, dass der Dienst selbst gesund ist.
  3. curl -sv http://127.0.0.1:8080/ vom Client bei aufgebautem Tunnel, dann traceroute -n zur Containeradresse von einem Rechner im LAN.
  4. sysctl net.ipv4.conf.all.rp_filter wenn die Anfrage den Host erreicht, die Antwort aber verschwindet.

Schreiben Sie den gewählten Bereich schließlich in dasselbe Register, aus dem Sie ihn gewählt haben, mit Datum und dem Projekt, dem er gehört. Die nächste Überschneidung kommt von einem Projekt, das niemand dokumentiert hat, und die einzige Verteidigung ist, dass der Bereich absichtlich reserviert und nicht zufällig gezogen wurde.

Ähnliche Artikel