Dwa rekordy A, jeden adres, jeden demon: niezależnie od tego, jaką nazwę wpisze użytkownik, przychodzący pakiet jest identyczny, a proces związany z portem 1194 nie ma czym odróżnić obu. To nie jest błędna konfiguracja czekająca na naprawę; to granica tego, co może wyrazić wyszukiwanie nazwy. Wszystko, co wybiera między dwiema instancjami OpenVPN, musi być wartością, którą klient wkłada w połączenie i którą frontend potrafi odczytać z powrotem, a standardowy klient OpenVPN nie umieszcza takiej wartości otwarcie. Dalej jest o tym, gdzie to prowadzi i które układy naprawdę rozdzielają ruch.

Zacznij od tego, co zwraca resolver

Klient pyta o vpn-a.example i otrzymuje adres; ten adres plus port to wszystko, z czym się łączy. Nazwa jest zużywana lokalnie, podczas wyszukiwania, i nigdy nie staje się polem na drucie, więc odbiorca po drugiej stronie nie widzi, jaka etykieta została użyta. Skieruj vpn-a.example i vpn-b.example na ten sam host, a oba wyszukiwania dadzą ten sam cel, dlatego demon wchłania oba połączenia w jedną instancję, nie rejestrując różnicy. Profile oparte na adresie zachowują się dokładnie jak te oparte na nazwie, z tego samego powodu.

To zamyka także rodzinę kuszących obejść: dodatkowe aliasy, wybór certyfikatu dla każdej nazwy na jednym gnieździe albo reguła zapory powiązana z odpytywaną nazwą. Żadne z nich nie może działać na informacji, która została już odrzucona, zanim pierwszy pakiet opuścił klienta. Jeśli dwa serwery mają pozostać rozdzielone, podział musi podróżować z samym połączeniem, czyli adres, port lub pole ładunku zdefiniowane przez protokół.

Jedyny liczący się selektor jedzie wewnątrz pakietu

HTTP rozwiązuje to nagłówkiem Host, a TLS rozszerzeniem nazwy serwera w ClientHello, i tak jeden adres obsługuje wiele witryn za jednym odbiorcą. Oba działają, ponieważ protokół na drucie niesie żądanie konkretnej usługi. Istotny fakt o OpenVPN jest taki, że żadne z tych pól nie występuje w warstwie, którą bada pośredniczące pudełko.

W standardowym układzie uzgadnianie TLS chroniące kanał sterujący odbywa się po ustanowieniu sesji zewnętrznej i jest przenoszone w jej wnętrzu, więc odbiorca, który tej sesji nie zakończył, widzi nieprzejrzysty strumień. Nadal może stwierdzić, że to OpenVPN, bo opkod i ramkowanie w pierwszych bajtach są rozpoznawalne, ale nazwa serwera, jeśli istnieje, siedzi w zaszyfrowanej części. Projekt, który oczekuje odczytania wirtualnego hosta z pakietu zewnętrznego, czyta pole, którego protokół nigdy tam nie zapisuje. To wynika z tego, jak zbudowany jest tunel, a nie z luki, którą mogłoby domknąć sprytniejsze dopasowanie wzorca.

Gdzie rozdzielacz protokołów się zatrzymuje

Multiplekser tego rodzaju, który dokumentuje projekt sslh, nasłuchuje na jednym porcie, bada pierwsze bajty i przekazuje połączenie temu backendowi, który mówi rozpoznanym protokołem. To realna wartość, gdy jeden port musi obsłużyć SSH, HTTPS i OpenVPN jednocześnie.

sslh trzyma osobną sondę dla każdego protokołu, a jego sonda TLS potrafi wyciągnąć nazwę serwera lub wartość ALPN z prawdziwego ClientHello. Rozpoznanie standardowego OpenVPN to inna klasyfikacja niż czytanie rozszerzenia TLS, i te dwie się nie łączą, bo klient, który nigdy nie wysyła ClientHello do gniazda nasłuchującego, zostawia sondę bez niczego do czytania. Dlatego reguła pasująca do OpenVPN odpowiada „to klient OpenVPN” i nie idzie dalej. Dwa backendy OpenVPN za tą regułą są nierozróżnialne, frontend nie ma podstawy do wyboru, a każda sesja ląduje w tym samym miejscu. Multipleksowanie dzieli jeden port między różne usługi; nie robi hostingu wirtualnego w obrębie jednej usługi.

Układy, które naprawdę rozdzielają dwa serwery

Najtańszy uczciwy podział to drugi publiczny adres, po jednym na instancję, a istniejące profile już noszą to, co potrzebne, bo wskazują na różne adresy. Następny jest inny port na tym samym adresie, co nie kosztuje nic poza liczbą w każdym profilu. TCP i UDP prowadzą osobne tablice portów, więc 1194/tcp i 1194/udp to niepowiązane gniazda i po jednym z każdych może współistnieć na jednym hoście; to rozdzielenie transportu, a nie routing po nazwie, i często się to z nim myli. Obie opcje przechodzą przegląd zapory, bo reguła pasuje do czegoś, co pakiet naprawdę zawiera.

Jeśli potrzebna granica biegnie między grupami użytkowników, a nie między dwiema implementacjami, może ją pokryć jedna instancja z odrębnymi konfiguracjami klientów, własną obsługą certyfikatów i jawną polityką dostępu. To kupuje politykę wewnątrz serwera, a nie izolację między serwerami, i to są różne wymagania. Izolacja, która musi się utrzymać pod kompromitacją, nadal potrzebuje adresu lub krawędzi zapory.

Terminator TLS postawiony przed OpenVPN może ujawnić prawdziwą nazwę serwera zdolnemu frontendowi, ale tylko wtedy, gdy strona klienta jest również skonfigurowana pod tę dodatkową warstwę. Istniejące profile nie zyskują tego zachowania przez samo wskazanie tego samego adresu.

Udowodnij selektor, zanim zbudujesz

Nazwij pole, które będzie identyfikować backend, a potem potwierdź, że klient naprawdę je wysyła; jeśli odpowiedzią jest adres, plan się trzyma, a jeśli to nazwa serwera w uzgadnianiu, które nigdy nie dociera do odbiorcy, żadna praca z DNS tego nie uratuje. Trzy polecenia szybko rozstrzygają stronę nasłuchującą.

ss -lntup | grep -E ':443|:1194'
openssl s_client -connect vpn-a.example:443 -servername vpn-a.example </dev/null | head -n 20
journalctl -u openvpn-server@server-a -n 20 --no-pager

Linia openssl daje certyfikat tylko tam, gdzie coś pod tym adresem kończy TLS; wobec standardowego odbiorcy OpenVPN zatrzymuje się lub zawodzi, i to samo w sobie jest odpowiedzią na to, co ujawnia warstwa zewnętrzna. Linia journal, odczytana na każdym backendzie, pokazuje, która instancja przyjęła sesję. Logi frontendu zapisują to, co zostało sklasyfikowane, a nigdy to, co było zamierzone, więc backend pozostaje jedynym autorytetem co do tego, gdzie wylądował użytkownik.

Zakończ dwoma profilami przeznaczonymi do różnych serwerów, połącz je po kolei i zanotuj log backendu dla każdego. Jeśli obie sesje pojawią się w tym samym journalu, selektora brakuje, a nie jest źle skonfigurowany, i poprawką jest adres lub port, który klient może obrać za cel. Edycja reguły routingu nie pomoże, bo regule nigdy nie dano niczego, po czym routować. Gdy podział stanie się rzeczywisty, zapisz go obok profili, żeby następna osoba nie dodała drugiego aliasu i nie oczekiwała, że wykona on pracę.

Powiązane artykuły