Dlaczego twoja reguła iptables nigdy nie widzi ruchu VPN przez most
Ramki przechodzące przez most mogą omijać łańcuch IP, który obserwujesz. Porównaj natywną rodzinę bridge ze starym br_netfilter, zanim dodasz kolejną regułę.
Dwie maszyny wirtualne na tym samym hoście docierają do LAN przez most, a reguła umieszczona w łańcuchu ip forward pokazuje licznik, który nigdy się nie porusza. Ramki są prawdziwe, ruch przechodzi, a reguła po prostu nie leży na jego drodze. Porównanie dwóch sposobów, w jakie Linux udostępnia ruch przez most, wyjaśnia tę ciszę i wyjaśnia też, dlaczego wczytanie jednego starego modułu jest gorszą odpowiedzią, niż się wydaje.
Dwie drogi przez jeden host
Pakiet może opuścić ten host na dwa bardzo różne sposoby, a różnica decyduje o tym, które hooki go zobaczą. Jeśli cel znajduje się za portem fizycznym i ramka jest przełączana między dwoma portami mostu, host przekazuje ramkę Ethernet; kod routingu nie jest pytany i nie następuje żaden routowany skok. Jeśli celem jest sam host albo sieć osiągana przez interfejs tunelu, pakiet jest routowany i hooki warstwy IP rzeczywiście działają. Miej to rozgałęzienie w pamięci, bo każde późniejsze pytanie o łańcuchy i rodziny wynika z tego, którą z dwóch dróg wybierze ramka.
Ta różnica to cała historia reguły, która niczego nie liczy. Ramka przez most z veth9a1 do enp3s0 przecina most, a nie stos IP, więc łańcuch ip forward nie widzi niczego do dopasowania. Nazwy interfejsów w starszym przykładzie nie rozstrzygają, którą drogę masz, dlatego pierwszym krokiem jest patrzenie na topologię, a nie na zestaw reguł.
Tunel dodaje trzeci przypadek, który zasługuje na nazwę. Urządzenie TUN dostarcza pakiety IP do hosta, więc są one routowane i rzeczywiście przechodzą przez łańcuchy IP, natomiast urządzenie TAP podłączone do mostu dostarcza ramki i zachowuje się jak przypadek mostu. Decyduje typ urządzenia, a nie nazwa produktu.
Porównaj dwie rodziny, które widzą ramkę przez most
Linux obsługuje natywne filtrowanie mostu poprzez rodzinę bridge w nftables i nie potrzebuje żadnego starego modułu, aby sprawdzać i odrzucać ramki w locie. Dokumentacja mostu samego jądra podaje to zalecenie wprost, co warto wiedzieć, zanim wynik wyszukiwania każe ci wczytać moduł.
| Observation point | What it sees | What it costs |
|---|---|---|
nftables bridge family chains | Ethernet frames forwarded between bridge ports, in both directions | Rules must be written in bridge syntax with link-layer matches |
Legacy br_netfilter plus an ip chain | Bridged IPv4 and IPv6 packets, re-exposed through IP-layer hooks | Existing host rules may begin matching traffic they never saw before |
inet chain for routed traffic | Packets the host routes, including traffic arriving from a TUN device | No visibility into purely switched frames |
Użycie polecenia nft zamiast iptables samo w sobie nie umieszcza reguły w właściwej rodzinie. Łańcuch zadeklarowany w rodzinie bridge widzi ramki; łańcuch zadeklarowany w ip, ip6 lub inet ich nie widzi, niezależnie od tego, który plik binarny go utworzył. Rodzina jest właściwością łańcucha, a nie narzędzia, które go wpisało.
Stara droga działa inaczej i właśnie dlatego warto porównywać, zamiast wybierać stronę z przyzwyczajenia. Wczytanie br_netfilter i włączenie ustawień bridge-nf-call ponownie udostępnia ruch IPv4 i IPv6 przez most poprzez hooki warstwy IP, dzięki czemu zwykła reguła iptables może w końcu go dopasować. Ramki się nie zmieniają; zmienia się zbiór hooków, przez które przechodzą, co oznacza, że każda istniejąca reguła hosta ma teraz nową publiczność.
Przeczytaj rzeczywistą drogę, zanim dodasz regułę
Trzy polecenia ustalają, gdzie jesteś: do czego podłączony jest most, czy stary moduł jest aktywny i jakie łańcuchy już istnieją w rodzinie bridge. Uruchom je, zanim cokolwiek napiszesz, ponieważ odpowiedź decyduje, który z dwóch projektów rozszerzasz. Pominięcie tego kroku to sposób, w jaki poprawna reguła trafia do łańcucha, przez który nic nie przechodzi.
bridge link show
lsmod | grep br_netfilter
nft list ruleset | grep -A4 "chain forward"
Wyjście czysto rozdziela przypadki. Porty wymienione pod mostem mówią, które interfejsy są przełączane; pusty wynik lsmod oznacza brak jakiejkolwiek widoczności na warstwie IP ramek przez most; istniejący łańcuch forward rodziny bridge oznacza, że właściciel polityki już istnieje i twoja reguła należy tam, a nie do nowego własnego łańcucha.
Następnie dodaj jedną łatwą do odczytania regułę, z licznikiem i bez zmiany werdyktu, i przepuść przez most jeden reprezentatywny strumień. Poruszający się licznik dowodzi, że ramka dotarła do reguły. To wszystko, czego dowodzi, więc podążaj też odrzuconą drogą, bo polityka, która akceptuje wszystko, również daje poruszający się licznik.
nft add rule bridge filter forward iifname "veth9a1" oifname "enp3s0" counter acceptGdzie stary moduł zmienia zachowanie
Włączenie starej drogi na działającym moście nie jest neutralnym aktem dodania widoczności. Reguły wcześniej nieosiągalne dla ruchu przez most stają się osiągalne, w tym reguły hosta pisane dla innego interfejsu, a domyślna polityka drop w łańcuchu ip forward może nagle zacząć obowiązywać ramki, które wcześniej przechodziły nietknięte. Na hoście zarządzanym przez ten most wynikiem jest zablokowanie bez konsoli. To nie jest hipotetyczny tryb awarii; to zwykła konsekwencja rozszerzenia zbioru hooków na maszynie, do której docierasz przez właśnie rozszerzoną drogę.
Bezpieczniejsza kolejność to zdecydować, która rodzina posiada politykę, tam zapisać reguły i zachować niezależną drogę odzyskiwania, zanim zmienisz jakikolwiek sysctl. Jeśli dwie rodziny mają reguły, udokumentuj, która jest nadrzędna; dwie nakładające się polityki to sposób, w jaki pakiet jest akceptowany w jednym miejscu i odrzucany w drugim bez ani jednego pliku do przeczytania.
Izolacja sama w sobie nadal wymaga pełnego projektu. Zezwolenie jednemu źródłu na jeden cel dowodzi tylko, że jeden strumień działa, podczas gdy wymaganiem zwykle jest to, że VM nie może dotrzeć do sąsiadów ani do hosta. Liczniki na regułach odmowy są ważniejsze niż licznik na regule akceptacji, ponieważ licznik akceptacji to ten, którego ruchu już się spodziewałeś.
Na koniec trzymaj przypadek wejścia hosta osobno. Ruch adresowany do własnego IP mostu trafia na input, a nie na forward, i reguła w forward nigdy go nie zobaczy, niezależnie od wybranej rodziny. Trzy przypadki, dwie rodziny, jedna lekcja: droga decyduje o hooku, a hook decyduje, czy twoja reguła w ogóle istnieje.