Zagnieżdżony WireGuard: środkowy przeskok nie potrafi odpowiedzieć na własny port
Gdy środkowy przeskok wysyła wszystko do jednego tunelu, odpowiedzi do jego usługi wejściowej wychodzą złą drogą. Reguła portu źródłowego na interfejsie to naprawia.
Trzy przeskoki WireGuard docierają do sieci bez publicznego adresu, a potem środkowa maszyna przestaje odpowiadać na porcie, na którym miała nasłuchiwać. Tunel dalej nadal działa, zdalny host nadal odpowiada, i tylko sam środkowy serwer zamilkł z zewnątrz. Ta kombinacja jest myląca właśnie dlatego, że jest samoistna: środkowy przeskok słucha własnej tablicy routingu, a tablica wysyła jego odpowiedzi w złą stronę.
Łańcuch, który działa, dopóki nie porozmawiasz ze środkowym pudełkiem
Weźmy przykładowy łańcuch: klient A dociera do serwera B przez UDP 500, a B przechodzi dalej do serwera C przez UDP 500. C przyjmuje ruch tylko z adresu swojego jedynego peera, co czyni go liściem i utrzymuje jego zachowanie prostym. B nie jest liściem, i wszystko ciekawe w tej awarii dzieje się właśnie tam.
Ta różnica ma znaczenie, bo role liścia i środka ciągną w przeciwne strony. Liść może wysłać wszystko do jednego tunelu bez konsekwencji, ponieważ nic innego od niego nie zależy. Środkowy przeskok jest jednocześnie punktem końcowym dla klientów i klientem wobec dalszej strony, a te dwa zadania chcą innego routingu.
Dlaczego odpowiedź wychodzi przez tunel
Na B interfejs skierowany do C niesie AllowedIPs = 0.0.0.0/0 wraz z pasującą trasą domyślną. Dlatego każdy pakiet, który B utworzy, trafia do tego tunelu, chyba że coś bardziej konkretnego ma pierwszeństwo.
Pakiet od A adresowany do B przychodzi i jest akceptowany, ponieważ B jest celem, a dostarczenie lokalne następuje pierwsze. Odpowiedź to całkiem nowy, lokalnie utworzony pakiet skierowany na publiczny adres A, i ten cel pasuje do trasy domyślnej do tunelu. C go odbiera, nie posiada tego adresu i odrzuca go.
Objaw czyta się jak problem z zaporą ogniową albo niezgodność kluczy, i żadne z nich nie jest tu prawdą. Przechwycenie na tunelu w stronę C pokazuje, że odpowiedzi idą w złą stronę. To jedno spostrzeżenie oszczędza więcej czasu niż jakakolwiek ilość czytania konfiguracji, więc zapisz je, zanim zaczniesz zmieniać klucze. Kto nie widział przechwycenia, uzna, że sam tunel jest zepsuty.
Wąska naprawa: reguły polityki pasujące do portu źródłowego
Odpowiedzi z gniazda nasłuchującego B wychodzą z portem źródłowym 500. Reguła polityki może to dopasować i skierować te pakiety z powrotem do zwykłej tablicy:
ip rule add sport 500 table main priority 100
ip rule add sport 8080 table main priority 101
Reguły polityki są sprawdzane w kolejności priorytetów, a tablica główna znajduje się na 32766, więc reguła na 100 jest pytana, zanim trasa domyślna tunelu ma coś do powiedzenia. Dodaj jeden wpis na każdy port, który musi pozostać osiągalny z zewnątrz, i nic więcej, aby przekazywany ruch nadal korzystał z tunelu, dla którego został zbudowany.
Znaj granicę tego triku. Selektor sport pasuje do pakietów utworzonych lokalnie, co obejmuje dokładnie przypadek odpowiedzi i celowo nie obejmuje ruchu, który B tylko przekazuje za kogoś innego. Usługa hostowana za B nadal potrzebuje własnej odpowiedzi, a udawanie, że jest inaczej, daje poprawkę, która działa dla pierwszej usługi i zawodzi dla drugiej.
Daj regule właściciela
Reguła wpisana w wierszu poleceń znika przy następnym przebudowaniu interfejsu. W konfiguracji WireGuard umieść ją tam, gdzie interfejs jest tworzony i usuwany:
PostUp = ip rule add sport 500 table main priority 100
PostDown = ip rule del sport 500 table main priority 100
Jeśli interfejsem zarządza NetworkManager, hook dystrybucji lub punkt wejścia kontenera, para należy do tego właściciela, a nie do pliku edytowanego ręcznie. Sprawdź obowiązujące reguły po ponownym połączeniu, nie tylko po pierwszym starcie, bo pierwszy start to przypadek, który zawsze działa.
Porównaj alternatywy uczciwie
Reguła portu źródłowego jest jednym z czterech wyjść i najwęższym z nich. Ma tę zaletę, że w ogóle nie dotyka polityki tunelu, dlatego zwykle przeżywa późniejsze zmiany. Wąskie poprawki również głośno zawodzą, gdy przestają obowiązywać, podczas gdy szeroki wyjątek nadal cicho działa długo po tym, jak powód zniknął. Ta różnica liczy się bardziej niż elegancja, gdy za dwa lata pudełko należy do kogoś innego.
| Approach | What it costs | Where it breaks |
|---|---|---|
| Source-port policy rule | One rule per exposed port | Anything B forwards rather than originates |
| Destination exception route | A route pinned to specific client addresses | Clients whose address changes |
| Stop full-tunnelling on B | Loses the catch-all path toward C | Designs that depend on it |
| Split into two tunnels | More interfaces and more policy | Nothing, at the cost of complexity |
Sprawdź z obu stron, gdy już wybierzesz. Z A rozpocznij połączenie do portu B i trzymaj je zajęte przez chwilę. Na B każ jądro uzasadnić ścieżkę odpowiedzi czymś w rodzaju ip route get <address-of-A> sport 500, i potwierdź, że wskazuje interfejs WAN, a nie tunel. Następnie sprawdź, że coś za C nadal odpowiada przez łańcuch, co dowodzi, że wyjątek pozostał wąski, a nie zamienił się w obejście. Na koniec zapisz porty w notatkach projektowych, inaczej następny operator odkryje to ponownie w innej usłudze.