Klient VPN se připojí, prohlížeč funguje a ten jediný příkaz, na kterém vám skutečně záleží, pořád odchází přes vaše domácí připojení. Nebo se stane opak: tunel si vezme výchozí trasu a místní souborový server zmizí z plochy. Narazil jsem na to u build agenta, který musel dosáhnout na interní zrcadlo artefaktů, zatímco zbytek hostitele zůstal na kancelářské LAN, a klient nabízel přesně dvě možnosti: všechen provoz, nebo nic. Požadavek je v obou směrech stejný, tato aplikace a ne celý počítač, a většina klientů na něj nemá zaškrtávací políčko, protože směrovací tabulka nikdy nevěděla, co je to aplikace.

Proč směrovací tabulka nevidí vaši aplikaci

Když proces odešle paket, zásobník hledá cílovou adresu ve směrovací tabulce. Ani název spustitelného souboru, ani port do tohoto hledání nevstupují. Doménová jména selhávají ze stejného důvodu: tunel nese provoz na adresu a než se jméno přeloží na adresu, je směrování jediná páka, kterou jádro ještě má. Proto „VPN jen pro tento web“ nakonec znamená proxy a „VPN jen pro tuto aplikaci“ znamená jeden z malé sady mechanismů.

MechanismWhat selectsCoversOn tunnel drop
Socket bindsource addressa client you controlconnections fail
UID rule (Linux)owning userevery process of one accountconnections fail
Firewall profile (Windows)adapter profileone programfails closed
Network namespacenamespace membershipeverything started insideconnections fail
SOCKS hopapp proxy settingone app or one browser profileconnections fail

Nic z toho není trasa pro jednotlivý proces. Každý přesouvá rozhodnutí do vrstvy, která vidí aplikaci, uživatele nebo vlastní konfiguraci aplikace.

Jediný cíl je pořád trasa, ne pravidlo aplikace. Na macOS je obvyklý recept odškrtnout „Posílat všechen provoz přes VPN“ a přidat trasu k hostiteli: sudo route add -host internal-tuner.example -interface ppp0, která se zase odstraní pomocí sudo route delete -host internal-tuner.example -interface ppp0. Nejprve si přečtěte název rozhraní z ifconfig, protože ppp0 je jen příklad. Zástupné znaky v trase k hostiteli nefungují, takže služba, která mluví s několika jmény, potřebuje jeden záznam na jméno, a pravidlo pokrývá každý proces na počítači, ne ten, který jste měli na mysli.

Připoutejte socket k adrese tunelu

Když klienta ovládáte, je připoutání nejlevnější volič. Socket připoutaný k místní adrese tunelu vychází přes toto rozhraní, dokud tam existuje trasa k cíli, a všechno ostatní na počítači si drží obvyklou cestu. Jemnost je v tom, že připoutání volí zdrojovou adresu, zatímco směrovací tabulka pořád volí výstup. Samotné připoutání nepřesouvá provoz, který nemá do tunelu trasu, a přesně to je chyba za většinou hlášení „připoutal jsem to a pořád to odchází špatnou cestou“.

import socket
s = socket.socket()
s.bind(("10.8.0.2", 0))
s.connect(("build.internal.example", 443))

Nástroje vyšší úrovně nabízejí stejnou myšlenku: curl --interface 10.8.0.2, CURLOPT_INTERFACE v PHP a argument source_address v HTTP klientech Pythonu. Adresa musí patřit tunelu, a překlep zde selže jako běžná chyba spojení, ne jako stížnost na směrování. Připoutání k názvu zařízení místo adresy je privilegium Linuxu a nepřenáší se na Windows ani macOS, proto přenosné skripty předávají adresu.

Linux: porovnávejte vlastníka, ne název aplikace

Jádro umí porovnat vlastníka socketu, takže vyhrazený účet se stává voličem. Dejte tunel do vlastní směrovací tabulky a posílejte tam jen provoz tohoto účtu:

ip rule add uidrange 1001-1001 lookup 100
ip route add default dev tun0 table 100

Tohle funguje jen tehdy, když tunel už není výchozí trasou. Spusťte klienta s --route-nopull, nebo odfiltrujte vynucené přesměrování pomocí --pull-filter ignore "redirect-gateway" na OpenVPN 2.4 a novějším, a pak záznam v tabulce přidejte sami. Jinak existují obě výchozí trasy a vyhraje ta s nižší prioritou. Záměrná verze toho je nasměrovat výchozí trasu na bránu tunelu, nebo to udělat ze skriptu při odpojení: drží provoz mimo internet, když tunel spadne, ale to je vypínač, ne volič, a zůstane, dokud ho někdo neodstraní.

Silnější verze je síťový jmenný prostor. Vytvořte ho, spusťte klienta uvnitř a spusťte aplikaci s ip netns exec. Nic mimo jmenný prostor nevidí rozhraní tunelu, takže aplikace nemůže omylem spadnout zpět na trasu hostitele. Cena je reálná: jmenný prostor potřebuje dvojici veth pro dosažení fyzické sítě a vlastní soubor resolveru, protože přípojný jmenný prostor nesdílí nastavení překladu hostitele.

Dva režimy selhání vysvětlují většinu úniků. Překlad jmen pořád probíhá v resolveru hostitele, takže dotaz vychází mimo tunel, i když TCP spojení jde dovnitř. A IPv6 potřebuje vlastní ip -6 rule, protože pravidlo v4 ho nepokrývá.

Windows a macOS: ohraničte firewall nebo předejte proxy

Windows také nemá volič trasy pro jednotlivou aplikaci, ale jeho pravidla firewallu lze omezit na profil a adaptér VPN lze klasifikovat jako „veřejný“, zatímco domácí LAN zůstává „soukromá“. Vytvořte odchozí pravidla blokování pro program v profilech „soukromý“ a „doména“, nechte „veřejný“ otevřený a aplikace se dostane na síť jen tunelem. Nejprve nastavte síťovou kategorii připojení, pak potvrďte, že pravidlo v monitorovacím panelu firewallu odpovídá. Je to blok, ne trasa: selhává zavřeně, když tunel spadne, a nezastaví aplikaci, která firewall ignoruje.

macOS sleduje stejnou logiku jinými nástroji. SOCKS posluchač dostupný přes SSH je nejméně invazivní možnost, protože ssh -D 1080 user@host vytvoří jeden místní port a touto cestou jdou jen aplikace nakonfigurované k jeho použití. Pravidlo pf umí zablokovat program podle uživatele nebo skupiny, ale pořád neumí vybrat tunel jen pro tuto aplikaci. Tunnelblick neumí filtrovat podle domény vůbec, protože trasy pracují s adresami; uživatelé OpenConnect sahají po vpn-slice, když server vynutí příliš široký seznam prefixů.

Dvě obejití pokrývají to, co nástroje platformy minou. Rozšíření prohlížeče, která přepínají proxy podle domény, jsou praktickou odpovědí na „jen tento web“, protože působí ve vrstvě aplikace, kde jména ještě existují; pokrývají jeden prohlížeč a nic jiného. Hrubé řešení je spustit klientský software dodavatele ve virtuálním počítači, aby host ovládal změnu trasy a hostitel si držel svou cestu, za cenu dvou síťových zásobníků k údržbě.

SOCKS skok pro nástroje bez možnosti připoutání

Většinu aplikací lze nasměrovat na HTTP nebo SOCKS proxy, i když o rozhraních nevědí nic. SSH klient jeden poskytne beze změny na straně serveru:

ssh -N -D 127.0.0.1:9999 [email protected]

Vše, co je nastaveno na tento port, vychází na bastionu, zatímco zbytek plochy si drží obvyklé připojení. Místní instance 3proxy připoutaná k adrese tunelu dělá totéž pro jeden port bez SSH a stunnel pokrývá jediný port obalený TLS. Omezení jsou praktická: proxy nese jen protokoly, které ho umějí použít, UDP potřebuje asociaci SOCKS5 UDP, kterou nastavení „SOCKS5“ mnoha klientů nezapíná, a DNS se musí překládat přes proxy (socks5h v curl), jinak jméno unikne místnímu resolveru.

Nechte posluchač na loopbacku, pokud ho nepotřebuje jiný počítač.

Ověřte cestu zachycením, ne stavovou zprávou

Stavová okna aplikace popisují nastavení, ne paket. Zachyťte na obou rozhraních a porovnejte:

tcpdump -i any -n port 443
ss -tnp | grep 443

Pokud se spojení objeví na fyzickém rozhraní bez odpovídajícího záznamu na tunelu, volič neúčinkuje, ať klient hlásí cokoli. Zkontrolujte zvlášť IPv6 a resolver a ověřte, zda neexistuje druhá cesta, například pomocný proces nebo zděděná systémová proxy.

Pak zvolte hranici, která odpovídá požadavku: proxy stačí na jeden web, pravidlo UID stačí na jeden účet, profil firewallu stačí, když spojení musí selhat zavřeně, a jmenný prostor je jediná možnost, která tunel skryje před vším mimo něj.

Související články