„Czy VPN jest włączony?” to tak naprawdę trzy pytania w jednym zdaniu. Którego z nich potrzebuje wasz kod, jest pierwszą decyzją, i właśnie ją pomija większość implementacji. Czy na tym urządzeniu istnieje tunel, czy ruch do tej trasy wychodzi przez niego, czy drugi koniec odpowiada tożsamością, której oczekujecie. Wybierzcie właściwość, potem sygnał, który ją mierzy, i na końcu napiszcie w logu, który z nich faktycznie zmierzyliście.

Trzy właściwości, trzy sygnały

Ściśnięte do jednej linii każda, trzy pytania mają trzy różne odpowiedzi. Tabela poniżej jest samą decyzją, a nie jej streszczeniem.

PropertySignal that answers itWhat it still leaves open
ExistenceThe platform's own connection state: RAS on Windows, NetworkManager on D-Bus on Linux, scutil on macOSTunnels of a type the platform does not manage, which includes most third-party clients
PathA route lookup for the one destination, or a scoped proxy or interface key that names the tunnelWhether the far end is the peer you trust
IdentityAn authenticated response from the far end, or a source-address policy applied on the serverWhich local interface carried the packets

Przeczytajcie ostatni wiersz powoli. Prywatny host, który odpowiada, dowodzi, że coś odpowiedziało, a nie że odpowiedź przyszła przez tunel, i nic nie mówi o tym, kto jest po drugiej stronie. Jeśli właściwością, której potrzebujecie, jest ścieżka, mierzcie ścieżkę.

Istnienie: własny stan platformy

Windows odpowiada na to dla profili natywnych przez Remote Access Service API. RasEnumConnections wymienia, co jest połączone, RasGetConnectStatus zgłasza stan jednego wpisu, a RasGetLinkStatistics dodaje prędkość, liczniki bajtów i wiek połączenia. Granice są tu sednem: OpenVPN, WireGuard i klienci producentów nigdy nie rejestrują połączenia RAS, więc zdrowy tunel tego typu pokazuje pustą listę, a naiwny kod zgłasza go jako wyłączony.

Pulpity Linuksa udostępniają tę samą właściwość przez NetworkManager na D-Bus, gdzie PropertiesChanged na obiekcie połączenia VPN zamienia kontrolę w zdarzenie, a właściwość State zgłasza, kiedy łącze osiąga stan połączony. Z powłoki nmcli connection show --active czyta te same informacje, i to jest to, czego chcecie podczas debugowania. macOS trzyma tę listę w frameworku System Configuration, a scutil jest jego widokiem z wiersza poleceń. Wszystkie trzy opisują połączenie zarządzane przez platformę, i wszystkie trzy milkną o tunelu, którego nie zarządza.

Ścieżka: dokąd pakiet naprawdę trafia

Dopasowanie nazw to skrót, po który wszyscy sięgają najpierw: wyliczcie interfejsy, sprawdźcie ciąg wobec tun, tap, ppp lub ipsec i potraktujcie trafienie jako dowód. To heurystyka, a zbiór interfejsów może wyglądać tak samo niezależnie od tego, czy coś przez niego płynie. Starsze wersje iOS odpowiadały w ten sposób, a późniejsze przestały, bo widoczne interfejsy nie rozróżniały już tych dwóch stanów.

Tym, co to tam zastąpiło, jest słownik serwera proxy systemu. Dopóki VPN jest połączony, wywołanie ustawień proxy zwraca klucze ograniczone do interfejsów powiązanych z tunelem, a gdy nic nie jest połączone, słownik wraca pusty. To twierdzenie słabsze, niż brzmi, ponieważ zapisuje przekonanie systemu, że tunel istnieje, a nie to, która trasa go używa.

Android ma własną wersję tej pułapki: rozgłoszenie vpn.connectivity dociera do odbiorników zadeklarowanych w manifeście w niektórych kompilacjach, a w innych do odbiorników zarejestrowanych w czasie działania. Traktujcie je jako najlepszy wysiłek i trzymajcie za nim bezpośrednią sondę.

Kiedy liczy się trasa, pytajcie tablicę routingu, a nie listę adapterów. Heurystyki bramy domyślnej psują się na maszynach z wieloma sieciami i na tunelach, które instalują własną bramę.

Tożsamość: zbadaj drugi koniec

Pętla wysyłająca echo ICMP na drugi koniec co kilka sekund to najtańsza kontrola tego rodzaju, i zawodzi w użytecznym kierunku. Kierujcie ją na adres, który istnieje tylko wewnątrz sieci prywatnej, bo publiczny odpowiada przez dowolne działające łącze w górę i niczego nie dowodzi. Tam, gdzie ICMP jest filtrowany, krótkie połączenie TCP z portem usługi robi to samo.

Kontrola po stronie serwera ma dokładnie jeden sygnał: adres źródłowy na połączeniu. Utrzymywanie inwentarza zakresów to praca konserwacyjna, i nie potrafi oddzielić tunelu od dowolnej innej sieci, która przypadkiem używa tego samego bloku.

Zgrubna polityka może z tym żyć. Tożsamość nie może, więc uwierzytelniajcie klienta, zamiast ufać temu, skąd pozornie przychodzą jego pakiety.

Zapisujcie kategorię, nie wyrok

Trzymajcie kategorie awarii osobno w logu: brak jakiegokolwiek interfejsu, interfejs jest, ale trasa nieosiągalna, trasa osiągalna, ale uwierzytelnianie odrzucone. To trzej różni właściciele, i każdy otwiera inne śledztwo.

Zlepienie ich w jedno „VPN padł” wysyła raport do zespołu, który nie może tego naprawić. Kategoria jest użytecznym wynikiem; wyrok jest tym, co czyni go błędnym.

Powiązane artykuły