Ruch Docker i VM za OpenVPN: routować czy przenumerować?
Kontenery i goście na hoście z klientem OpenVPN albo tracą drogę powrotną, albo kolidują z prefiksem tunelu. Trzy pytania wybierają rozwiązanie.
Host przegląda przez tunel, a kontener obok nie osiąga niczego. Publikowanie portu z kontenera, który uruchamia własnego klienta VPN, jest gorsze: żądanie przychodzi, a odpowiedź nigdy nie wychodzi. Oba są decyzjami routingu na hoście, a Docker jest tylko posłańcem.
Trzy pytania decydują, który z dwóch przypadków widzisz, i zajmują około minuty.
Dwie awarie, które wyglądają tak samo
Pierwsza to problem drogi powrotnej. Opublikowany port jest tłumaczony do kontenera, żądanie przychodzi, a odpowiedź jest routowana przez domyślną trasę hosta, którą klient OpenVPN skierował na tun0. Odpowiedź wychodzi z adresu, którego zdalny peer się nie spodziewa, i zostaje odrzucona. Druga to kolizja prefiksów: domyślny most Dockera posiada 172.17.0.0/16, a tunel firmowy wypycha ten sam zakres.
Jedną naprawia się tabelą routingu, drugą przenumerowaniem mostu. Edytowanie, zanim wiadomo, który to który, to sposób na zepsucie działającego kontenera.
Zdecyduj za pomocą trzech pytań
Pytanie pierwsze: czy awaria znika, gdy zatrzymasz VPN? Jeśli wszystko działa przy wyłączonym tunelu, podejrzewaj drogę powrotną. Pytanie drugie: czy ip route show table all wymienia ten sam prefiks dwa razy, raz na moście i raz na tun0? To właśnie kolizja. Pytanie trzecie: czy dotknięty adres to kontener, gość NAT czy gość w trybie mostu?
Trzecie pytanie ma znaczenie, bo każdy ma inną poprawną naprawę, a zastosowanie naprawy dla kontenera do gościa libvirt marnuje cały wieczór.
ip route
ip route show table all | grep -E '172\.1[7-9]|10\.8'
docker network inspect bridge --format '{{.IPAM.Config}}'Droga powrotna: tabela dla jednego adresu źródłowego
Gdy ruch kontenera musi wychodzić przez interfejs fizyczny, podczas gdy sam host korzysta z tunelu, routing oparty na źródle jest najmniejszą zmianą. Dodaj regułę pasującą do adresu kontenera i wyślij ją do osobnej tabeli. Nic więcej na hoście się nie rusza.
ip rule add from 172.22.0.3 lookup 10
ip route add default via 172.22.0.1 table 10Każdy pakiet, którego źródłem jest ten adres, najpierw zagląda do tabeli 10, a tabela 10 wysyła go do bramy mostu zamiast do tunelu.
Potwierdź za pomocą ip route get 1.1.1.1 from 172.22.0.3. Odpowiedź musi wskazać interfejs mostu, a nie tun0. Reguły dodane w ten sposób znikają po restarcie, więc umieść je w unicie lub haku sieciowym, do którego należy kontener.
Ten sam wzorzec działa dla kontenera WireGuard, gdzie droga powrotna również wymaga jawnej reguły.
Kolizja prefiksów: przenumeruj most
Tabela routingu wysyła jeden prefiks w jedno miejsce. Jeśli VPN wypycha 172.17.0.0/16, a most Dockera już tam jest, wygrywa niższa metryka: Docker instaluje swoją trasę połączoną z metryką 0, a wypchnięta trasa przychodzi z 50. Wtedy tunel wygląda na zepsuty, podczas gdy Docker dalej działa, albo odwrotnie, zależnie od kolejności instalacji. Przenumeruj most, zamiast walczyć z metryką.
{
"bip": "10.200.0.1/24",
"default-address-pools": [{"base": "10.201.0.0/16", "size": 24}]
}Po zmianie zrestartuj Dockera i odtwórz sieci zbudowane ze starej puli. Zmiana metryki tylko przenosi konflikt na następny start.
Maszyny wirtualne potrzebują znowu innej odpowiedzi
Gość libvirt w domyślnej sieci NAT dziedziczy routing hosta i przestaje docierać na zewnątrz, gdy tunel hosta przejmie domyślną trasę. Przełączenie gościa na urządzenie w trybie mostu lub macvtap daje mu własną drogę w sieci fizycznej. W virt-managerze dodaj sprzęt, wybierz Network, ustaw źródło na urządzenie hosta macvtap, pozostaw virtio jako model, a następnie dezaktywuj stary interfejs NAT.
Gość macvtap zwykle nie może dotrzeć do własnego hosta. To oczekiwane zachowanie, a nie usterka, i należy do notatki, którą zostawiasz.
Dla gościa, któremu wystarczy LAN hosta, klient dostawcy może mieć brakujący przełącznik: z Mullvadem mullvad lan set allow przywraca lokalną osiągalność bez dotykania libvirt. A gdy kontener ma być osiągalny tylko przez tunel, przypisz opublikowany port do adresu tunelu, -p 10.8.0.5:8080:8080, zamiast publikować go na każdym adresie.