Kontener Dockera niedostępny przez VPN? Porównaj podsieci
Docker przydziela podsieć, do której rości sobie prawo także VPN. Porównaj oba plany adresacji i wybierz naprawę, która przetrwa ponowne połączenie.
Kontenery odpowiadają w sieci LAN biura, a potem przestają odpowiadać w chwili, gdy łączy się firmowy VPN. W kontenerze nic się nie zmieniło: brak restartu, brak aktualizacji obrazu, brak edycji konfiguracji. Zmieniła się tablica routingu hosta, w której trasa dodana przez tunel obejmuje teraz adres przydzielony przez Dockera. Zanim edytujesz podsieć albo poprosisz o wyjątek w VPN, połóż oba plany adresacji obok siebie, bo naprawy kosztują bardzo różne ilości czasu.
Przeczytaj trasę, zanim zmienisz konfigurację
Zacznij od sieci, którą Docker faktycznie zbudował. Blok IPAM to plan adresacji, a podsieć to ta linia, która ma znaczenie:
docker network inspect project_default --format '{{json .IPAM.Config}}'
# [{"Subnet":"172.18.0.0/16","Gateway":"172.18.0.1"}]Compose domyślnie przydziela /16 każdemu nowemu projektowi, więc dwie usługi mogą zmieścić się w zakresie 65,536 adresów. Ta szerokość jest powodem, dla którego nakładanie się zakresów jest częste: losowo wybrane /16 jest dość duże, by pochłonąć witrynę, do której docierasz przez tunel, i nikt tego nie zauważa, dopóki tunel się nie podniesie.
Następnie porównaj wybór trasy dla jednego adresu kontenera, raz z tunelem wyłączonym, a raz z włączonym:
ip route get 172.18.0.7
# 172.18.0.7 dev br-3f2a9c src 172.18.0.1Dwa kształty odpowiedzi prowadzą w różne miejsca. Jeśli wynik wymienia tun0, wg0 albo inne urządzenie tunelu, prefiks VPN jest co najmniej tak samo szczegółowy jak trasa mostu i pakiet nigdy nie dociera do kontenera. Jeśli nadal wymienia most, host może dotrzeć do kontenera, a twoim błędem jest filtrowanie, DNS albo ścieżka powrotna, która zawodzi tylko w jednym kierunku.
Dlaczego kolizja /16 jest łatwa do trafienia
Docker czerpie sieci mostowe z pul prywatnych, a pula domyślna pochodzi z bloku 172.16.0.0/12 w kawałkach /16. Sieci korporacyjne często używają tej samej przestrzeni prywatnej, a VPN dostępu zdalnego wypycha te prefiksy do każdego klienta, który się łączy. Gdy obie strony uważają, że posiadają 172.18.0.0/16, jedna tablica routingu zawiera dwa sprzeczne roszczenia.
O zwycięzcy decyduje długość prefiksu, nie kolejność tras. Tunel wypychający 10.0.0.0/8 nie pokonuje trasy mostu 172.18.0.0/16, bo /8 jest mniej szczegółowy. Tunel wypychający 172.18.0.0/16 albo 172.18.5.0/24 wewnątrz niego już tak. Dlatego jeden laptop działa w sieci biurowej, a zawodzi w Wi-Fi hotelu z tym samym VPN: klient otrzymuje różne prefiksy.
Ta sama tablica rządzi drugim kierunkiem. Kontener, który potrzebuje wewnętrznego hosta pod 10.8.4.20, wysyła pakiet do swojej bramy mostu, a host kieruje go potem do tunelu. Ta ścieżka zwykle działa, bo host wykonuje NAT, ale zawodzi, gdy strona korporacyjna akceptuje tylko adres VPN laptopa albo gdy filtrowanie ścieżki zwrotnej odrzuca asymetryczną odpowiedź.
Porównaj trzy naprawy
Są tylko trzy wyjścia i różnią się tym, kto musi je zatwierdzić oraz co się psuje, gdy jedną stosujesz.
| Repair | Who owns it | What it costs |
|---|---|---|
| Renumber the container network | You | Recreates the network, restarts services, invalidates literal addresses in configs and scripts |
| Exclude your range from the tunnel | Network team | Changes what every laptop sees, needs a change request and a client profile update |
| Reach the service through a published port | You and the client | One port only, no direct container-to-container routing across the tunnel |
Ponowne numerowanie to jedyna naprawa, którą można dokończyć bez zgłoszenia, dlatego większość opracowań kończy się w tym miejscu. Jest tanie w młodym projekcie i drogie, gdy narzędzie migracji albo zapisany ciąg połączenia wskazuje dosłowny adres. Policz te odwołania przed edycją YAML, a nie po niej.
Wyjątek w tunelu to najczystsza odpowiedź, gdy nakładanie się zakresów leży w zakresie, który twoja firma naprawdę posiada. Jest też najwolniejszy i bezużyteczny dla wykonawcy, który nie może zmienić profilu klienta. Publikowany port albo mały proxy odwrotne omija spór: klient rozmawia z adresem, który tunel już kieruje poprawnie, i tylko ten port musi być osiągalny.
Przypnij zakres świadomie
Dla jednego projektu zadeklaruj podsieć w pliku compose, żeby projekt przestał czerpać ze wspólnej puli:
networks:
default:
ipam:
config:
- subnet: 10.201.7.0/24Weź ten zakres z rejestru, który prowadzi twój zespół, a nie z przykładu znalezionego podczas szukania. Sprawdź go wobec prefiksów wypychanych przez VPN, sieci domowych, z których ludzie pracują, i innych sieci kontenerów na tym samym hoście. Potem zaplanuj odtworzenie, bo istniejąca sieć zachowuje starą podsieć: docker compose down, a następnie up -d. docker network rm odmawia, dopóki dołączone są punkty końcowe, a każdy wpis ipv4_address w pliku musi mieścić się w nowym zakresie.
Dla sieci, które Docker tworzy sam, ustaw pulę demona w /etc/docker/daemon.json:
{
"default-address-pools": [
{ "base": "10.201.0.0/16", "size": 24 },
{ "base": "10.202.0.0/16", "size": 24 }
]
}Pula dotyczy sieci tworzonych po restarcie demona, a mosty, które już istnieją, zachowują swoje adresy. To wygodne i łatwe do zapomnienia, więc traktuj stary projekt jako nadal kolidujący, dopóki ktoś nie odtworzy jego sieci. dokumentacja sieci silnika Dockera opisuje zasady przydziału, jeśli musisz obronić pulę podczas przeglądu.
Most Linuksa i Docker Desktop zawodzą inaczej
Na hoście linuksowym kontenery siedzą na mostach w przestrzeni nazw hosta. Wszystko rozstrzyga jedna tablica, więc powyższe porównanie to cała historia: odczytaj trasę mostu, odczytaj trasę tunelu i sprawdź, która wygrywa dla rzeczywistego adresu kontenera.
Docker Desktop umieszcza kontenery za maszyną wirtualną z własnym NAT. Host nie ma żadnej trasy do adresu kontenera; dociera do usługi przez publikowany port, zwykle przypięty do 127.0.0.1. Klient VPN może więc zepsuć usługę bez żadnego nakładania się podsieci: instalując trasę dla wewnętrznego zakresu maszyny wirtualnej albo blokując ruch loopback, gdy tunel jest połączony. Jeśli ip route get na hoście zgłasza brak trasy do adresu kontenera, to jest oczekiwane, a nie błąd.
Praktyczny skutek dotyczy tego, gdzie patrzysz najpierw. W Linuksie zaczynaj od tablicy routingu. W Desktopie zaczynaj od publikowanego portu i reguł zapory klienta VPN, a potem sprawdź, czy wewnętrzna podsieć maszyny wirtualnej nie koliduje z czymś, co wypycha tunel.
Zweryfikuj z włączonym tunelem
Połącz VPN dwa razy, zanim uznasz to za naprawione, bo profil, który przy połączeniu wypycha trasy, a przy rozłączeniu je wycofuje, może po pierwszej próbie wyglądać poprawnie. Potem wykonaj sprawdzenia, które klient faktycznie wykonuje:
ip route getz hosta, z użyciem rzeczywistego adresu kontenera.- Żądanie z innego kontenera, żeby dowieść, że sama usługa jest zdrowa.
curl -sv http://127.0.0.1:8080/z klienta przy włączonym tunelu, a potemtraceroute -ndo adresu kontenera z maszyny w sieci LAN.sysctl net.ipv4.conf.all.rp_filter, jeśli żądanie dociera do hosta, ale odpowiedź znika.
Na koniec zapisz wybrany zakres w tym samym rejestrze, z którego go wybrałeś, z datą i projektem, który go posiada. Następne nakładanie się przyjdzie z projektu, którego nikt nie udokumentował, a jedyną obroną jest to, że zakres zarezerwowano świadomie, a nie wylosowano.