VPN klijent se povezuje, preglednik radi, a ona jedna naredba koja vam je zaista važna i dalje izlazi kroz vašu kućnu vezu. Ili se dogodi suprotno: tunel preuzme zadanu rutu i lokalni datotečni server nestane s radne površine. Na to sam naišao s agentom za izgradnju koji je morao dosegnuti interni artefaktni mirror dok je ostatak hosta ostao na uredskom LAN-u, a klijent je nudio točno dvije opcije: sav saobraćaj ili ništa. Zahtjev je u oba smjera isti, ova aplikacija a ne cijela mašina, i većina klijenata nema kvačicu za to, jer tablica rutiranja nikada nije znala šta je aplikacija.

Zašto tablica rutiranja ne može vidjeti vašu aplikaciju

Kada proces pošalje paket, stek traži odredišnu adresu u tablici rutiranja. Ni ime izvršne datoteke ni port ne ulaze u to traženje. Domenska imena padaju iz istog razloga: tunel nosi saobraćaj do adrese, a dok se ime razriješi u adresu, rutiranje je jedina poluga koju jezgro još ima. Zato „VPN samo za ovu stranicu“ na kraju znači proxy, a „VPN samo za ovu aplikaciju“ znači jedan od malog skupa mehanizama.

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

Ništa od ovoga nije ruta po procesu. Svako premješta odluku na sloj koji vidi aplikaciju, korisnika ili vlastitu konfiguraciju aplikacije.

Jedno odredište je i dalje ruta, a ne pravilo aplikacije. Na macOS-u uobičajeni recept je skinuti kvačicu „Šalji sav saobraćaj preko VPN-a“ i dodati rutu do hosta: sudo route add -host internal-tuner.example -interface ppp0, koja se opet uklanja s sudo route delete -host internal-tuner.example -interface ppp0. Prvo pročitajte ime interfejsa iz ifconfig, jer je ppp0 samo primjer. Džokeri ne rade u ruti do hosta, pa usluga koja razgovara s nekoliko imena treba jedan zapis po imenu, a pravilo pokriva svaki proces na mašini, ne onaj koji ste imali na umu.

Vežite socket na adresu tunela

Kada kontrolirate klijent, vezivanje je najjeftiniji selektor. Socket vezan na lokalnu adresu tunela izlazi kroz taj interfejs sve dok tamo postoji ruta do odredišta, a sve ostalo na mašini zadržava svoj uobičajeni put. Suptilnost je u tome što vezivanje bira izvornu adresu dok tablica rutiranja još bira izlaz. Samo vezivanje ne premješta saobraćaj koji nema rutu u tunel, i upravo je to greška iza većine prijava „vezao sam ga a i dalje izlazi na pogrešnu stranu“.

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

Alati višeg nivoa nude istu ideju: curl --interface 10.8.0.2, CURLOPT_INTERFACE u PHP-u i argument source_address u Python HTTP klijentima. Adresa mora pripadati tunelu, a greška u kucanju ovdje pada kao obična greška veze, ne kao pritužba na rutiranje. Vezivanje na ime uređaja umjesto na adresu je Linux privilegija i ne prenosi se na Windows ili macOS, zato prenosive skripte prosljeđuju adresu.

Linux: uparite vlasnika, ne ime aplikacije

Jezgro može upariti korisnika vlasnika socketa, pa posvećeni račun postaje selektor. Stavite tunel u vlastitu tablicu rutiranja i tamo pošaljite samo saobraćaj tog računa:

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

Ovo radi samo ako tunel već nije zadana ruta. Pokrenite klijent s --route-nopull, ili filtrirajte gurnuti preusmjerivač s --pull-filter ignore "redirect-gateway" na OpenVPN 2.4 i novijem, pa sami dodajte zapis u tablicu. Inače postoje obje zadane rute i pobjeđuje ona s nižim prioritetom. Namjerna verzija toga je usmjeriti zadanu rutu na gateway tunela ili to učiniti iz skripte za odspajanje: drži saobraćaj van interneta kada tunel padne, ali to je prekidač, a ne selektor, i ostaje dok ga neko ne ukloni.

Jača verzija je mrežni namespace. Stvorite ga, pokrenite klijent unutra i pokrenite aplikaciju s ip netns exec. Ništa izvan namespacea ne vidi interfejs tunela, pa aplikacija ne može slučajno pasti natrag na rutu hosta. Cijena je stvarna: namespace treba veth par za dosezanje fizičke mreže i vlastitu datoteku resolvera, jer mount namespace ne dijeli postavke razrješavanja hosta.

Dva načina kvara objašnjavaju većinu curenja. Razrješavanje imena i dalje se događa u resolveru hosta, pa upit izlazi izvan tunela čak i kada TCP veza ulazi unutra. A IPv6 treba vlastito ip -6 rule, jer pravilo v4 ga ne pokriva.

Windows i macOS: ograničite firewall ili prepustite proxy

Windows također nema selektor rute po aplikaciji, ali njegova firewall pravila mogu se ograničiti na profil, a VPN adapter se može klasificirati kao „javni“ dok kućni LAN ostaje „privatni“. Napravite izlazna pravila blokiranja za program na profilima „privatni“ i „domen“, ostavite „javni“ otvoren, i aplikacija može dosegnuti mrežu samo kroz tunel. Prvo postavite mrežnu kategoriju veze, zatim potvrdite da se pravilo poklopilo u nadzornoj ploči firewall-a. To je blok, ne ruta: pada zatvoreno kada tunel padne i ne može zaustaviti aplikaciju koja ignorira firewall.

macOS slijedi istu logiku s drugim alatima. SOCKS slušalac dosegnut preko SSH-a najmanje je invazivna opcija, jer ssh -D 1080 user@host stvara jedan lokalni port i samo aplikacije konfigurirane da ga koriste idu tim putem. Pravilo pf može blokirati program po korisniku ili grupi, ali još ne može odabrati tunel samo za tu aplikaciju. Tunnelblick uopće ne može filtrirati po domeni, jer rute rade s adresama; korisnici OpenConnecta posežu za vpn-slice kada server gurne preširok popis prefiksa.

Dva zaobilazna rješenja pokrivaju ono što platformski alati promaše. Proširenja preglednika koja mijenjaju proxy po domeni praktičan su odgovor za „samo ova stranica“, jer djeluju na sloju aplikacije gdje imena još postoje; pokrivaju jedan preglednik i ništa drugo. Grubo rješenje je pokrenuti klijent dobavljača u virtualnoj mašini tako da gost posjeduje promjenu rute dok host zadržava svoj put, uz cijenu dva mrežna steka za održavanje.

SOCKS skok za alate bez opcije vezivanja

Većina aplikacija može se usmjeriti na HTTP ili SOCKS proxy čak i kada ne znaju ništa o interfejsima. SSH klijent nudi jedan bez promjene na strani servera:

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

Sve konfigurirano da koristi taj port izlazi na bastionu dok ostatak radne površine zadržava uobičajenu vezu. Lokalna instanca 3proxy vezana na adresu tunela radi isto za jedan port bez SSH-a, a stunnel pokriva jedan port umotan u TLS. Ograničenja su praktična: proxy nosi samo protokole koji ga znaju koristiti, UDP treba SOCKS5 UDP asocijaciju koju postavka „SOCKS5“ mnogih klijenata ne uključuje, a DNS se mora razriješiti kroz proxy (socks5h u curl-u), inače ime iscuri lokalnom resolveru.

Držite slušalac na loopbacku osim ako ga druga mašina treba.

Provjerite put snimkom, a ne statusnom porukom

Prozori statusa aplikacije opisuju postavku, ne paket. Snimite na oba interfejsa i uporedite:

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

Ako se veza pojavi na fizičkom interfejsu bez odgovarajućeg zapisa na tunelu, selektor ne djeluje, šta god klijent izvijestio. Provjerite IPv6 i resolver odvojeno, i provjerite postoji li drugi put poput pomoćnog procesa ili naslijeđenog sistemskog proxyja.

Zatim odaberite granicu koja odgovara zahtjevu: proxy je dovoljan za jednu stranicu, UID pravilo je dovoljno za jedan račun, firewall profil je dovoljan kada veza mora pasti zatvoreno, a namespace je jedina opcija koja skriva tunel od svega izvan njega.

Povezani članci