Cum direcționezi o singură aplicație prin VPN fără întregul computer
Niciun desktop neadministrat nu oferă un comutator VPN per proces. Alege stratul care selectează traficul: un socket legat, o regulă UID, un profil de firewall sau un salt SOCKS.
Clientul VPN se conectează, browserul funcționează, iar singura comandă care te interesează pleacă tot prin conexiunea de acasă. Sau se întâmplă invers: tunelul preia ruta implicită, iar serverul de fișiere local dispare de pe desktop. Am întâlnit asta cu un agent de build care trebuia să ajungă la o oglindă internă de artefacte în timp ce restul gazdei rămânea în LAN-ul biroului, iar clientul oferea exact două opțiuni, tot traficul sau nimic. Cererea este aceeași în ambele direcții, acest program și nu întregul computer, iar majoritatea clienților nu au o bifă pentru ea, pentru că tabela de rutare nu a știut niciodată ce este un program.
De ce tabela de rutare nu vede programul tău
Când un proces trimite un pachet, stiva caută adresa de destinație în tabela de rutare. Nici numele executabilului, nici portul nu intră în acea căutare. Numele de domenii eșuează din același motiv: un tunel duce traficul către o adresă, iar când un nume se rezolvă într-una, rutarea este singura pârghie pe care nucleul o mai are. De aceea "VPN doar pentru acest site" ajunge să însemne un proxy, iar "VPN doar pentru acest program" ajunge să însemne unul dintr-un set mic de mecanisme.
| Mecanism | Ce selectează | Acoperă | La căderea tunelului |
|---|---|---|---|
| Legarea socketului | adresa sursă | un client pe care îl controlezi | conexiunile eșuează |
| Regulă UID (Linux) | utilizatorul proprietar | fiecare proces al unui cont | conexiunile eșuează |
| Profil de firewall (Windows) | profilul adaptorului | un singur program | eșuează închis |
| Namespace de rețea | apartenența la namespace | tot ce pornește în interior | conexiunile eșuează |
| Salt SOCKS | setarea de proxy a aplicației | o aplicație sau un profil de browser | conexiunile eșuează |
Niciuna dintre ele nu este o rută per proces. Fiecare mută decizia într-un strat care poate vedea programul, utilizatorul sau configurația proprie a aplicației.
O singură destinație este tot o rută, nu o regulă pentru program. Pe macOS rețeta obișnuită este să debifezi "Trimite tot traficul prin VPN" și să adaugi o rută de gazdă: sudo route add -host internal-tuner.example -interface ppp0, ștearsă apoi cu sudo route delete -host internal-tuner.example -interface ppp0. Citește numele interfeței din ifconfig mai întâi, fiindcă ppp0 este doar un exemplu. Metacaracterele nu funcționează într-o rută de gazdă, așa că un serviciu care vorbește cu mai multe nume are nevoie de o intrare pentru fiecare nume, iar regula acoperă fiecare proces de pe mașină, nu pe cel la care te gândeai.
Leagă socketul de adresa tunelului
Când controlezi clientul, legarea este cel mai ieftin selector. Un socket legat de adresa locală a tunelului iese prin acea interfață atât timp cât acolo există o rută către destinație, iar restul mașinii își păstrează calea normală. Subtilitatea este că legarea alege adresa sursă, în timp ce tabela de rutare alege în continuare ieșirea. Legarea singură nu mută în tunel traficul care nu are rută, iar aceasta este greșeala din spatele majorității rapoartelor de tipul "l-am legat și tot pleacă pe drumul greșit".
import socket
s = socket.socket()
s.bind(("10.8.0.2", 0))
s.connect(("build.internal.example", 443))Instrumentele de nivel înalt expun aceeași idee: curl --interface 10.8.0.2, CURLOPT_INTERFACE din PHP și argumentul source_address din clienții HTTP ai Python. Adresa trebuie să aparțină tunelului, iar o greșeală de tastare aici eșuează ca o eroare obișnuită de conexiune, nu ca o reclamație de rutare. Legarea de un nume de dispozitiv în loc de o adresă este un privilegiu Linux și nu se transferă pe Windows sau macOS, de aceea scripturile portabile transmit adresa.
Linux: potrivește proprietarul, nu numele programului
Nucleul poate potrivi utilizatorul proprietar al unui socket, așa că un cont dedicat devine selectorul. Pune tunelul în propria tabelă de rutare și trimite acolo doar traficul acelui cont:
ip rule add uidrange 1001-1001 lookup 100
ip route add default dev tun0 table 100Asta funcționează doar dacă tunelul nu este deja ruta implicită. Pornește clientul cu --route-nopull, sau filtrează redirecționarea impusă cu --pull-filter ignore "redirect-gateway" pe OpenVPN 2.4 și versiunile ulterioare, apoi adaugă singur intrarea în tabelă. Altfel există ambele rute implicite și câștigă cea cu prioritate mai mică. Versiunea deliberată a acestui lucru este să îndrepți ruta implicită către gateway-ul tunelului sau să o faci dintr-un script de deconectare: ține traficul departe de internet când tunelul cade, dar acesta este un kill switch, nu un selector, și rămâne până când cineva îl elimină.
Versiunea mai puternică este un namespace de rețea. Creează-l, rulează clientul înăuntru și pornește aplicația cu ip netns exec. Nimic din afara namespace-ului nu vede interfața tunelului, așa că aplicația nu poate reveni accidental la ruta gazdei. Costul este real: namespace-ul are nevoie de o pereche veth pentru a ajunge la rețeaua fizică și de propriul fișier de rezolvare, pentru că namespace-ul de montare nu partajează setările de rezolvare ale gazdei.
Două moduri de eșec explică majoritatea scurgerilor. Rezolvarea numelor se face tot în resolverul gazdei, așa că interogarea pleacă în afara tunelului chiar și când conexiunea TCP merge înăuntru. Iar IPv6 are nevoie de propriul ip -6 rule, pentru că o regulă v4 nu îl acoperă.
Windows și macOS: limitează firewallul sau predă un proxy
Nici Windows nu are un selector de rută per aplicație, dar regulile sale de firewall pot fi limitate la un profil, iar un adaptor VPN poate fi clasificat ca Public în timp ce LAN-ul de acasă rămâne Privat. Creează reguli de blocare a ieșirii pentru program pe profilurile Privat și Domeniu, lasă Public deschis, iar aplicația poate ajunge la rețea doar prin tunel. Setează mai întâi categoria de rețea a conexiunii, apoi confirmă în panoul de monitorizare al firewallului că regula s-a potrivit. Este o blocare, nu o rută: eșuează închis când tunelul cade și nu poate opri o aplicație care ignoră firewallul.
macOS urmează aceeași logică cu alte instrumente. Un listener SOCKS accesat prin SSH este opțiunea cea mai puțin invazivă, fiindcă ssh -D 1080 user@host creează un port local și doar aplicațiile configurate să îl folosească merg pe acea cale. O regulă pf poate bloca un program după utilizator sau grup, dar tot nu poate alege tunelul doar pentru acel program. Tunnelblick nu poate filtra deloc după domeniu, pentru că rutele lucrează cu adrese; utilizatorii OpenConnect apelează la vpn-slice când serverul împinge o listă de prefixe prea largă.
Două soluții ocolitoare acoperă ce ratează instrumentele platformei. Extensiile de browser care schimbă proxy-ul pe domeniu sunt răspunsul practic pentru "doar acest site", pentru că acționează la nivelul aplicației, unde numele încă există; acoperă un browser și nimic altceva. Varianta brută este să rulezi clientul furnizorului într-o mașină virtuală, astfel încât invitatul să dețină schimbarea de rută în timp ce gazda își păstrează calea, cu costul a două stive de rețea de întreținut.
Un salt SOCKS pentru unelte fără opțiune de legare
Majoritatea aplicațiilor pot fi îndreptate către un proxy HTTP sau SOCKS chiar dacă nu știu nimic despre interfețe. Un client SSH furnizează unul fără nicio modificare pe server:
ssh -N -D 127.0.0.1:9999 [email protected]Orice este configurat să folosească acel port iese prin bastion, în timp ce restul desktopului își păstrează conexiunea normală. O instanță locală 3proxy legată de adresa tunelului face același lucru pentru un port fără SSH, iar stunnel acoperă un singur port înfășurat în TLS. Limitele sunt practice: un proxy transportă doar protocoalele care știu să îl folosească, UDP are nevoie de asocierea UDP SOCKS5 pe care setarea "SOCKS5" a multor clienți nu o activează, iar DNS trebuie rezolvat de proxy (socks5h în curl) sau numele se scurge către resolverul local.
Ține listenerul pe loopback dacă nu ai nevoie de el de pe altă mașină.
Verifică calea cu o captură, nu cu un mesaj de stare
Ferestrele de stare ale aplicației descriu setarea, nu pachetul. Capturează pe ambele interfețe și compară:
tcpdump -i any -n port 443
ss -tnp | grep 443Dacă conexiunea apare pe interfața fizică fără o intrare corespunzătoare pe tunel, selectorul nu este în vigoare, indiferent ce raportează clientul. Verifică IPv6 și resolverul separat și caută o a doua cale, cum ar fi un proces ajutător sau un proxy de sistem moștenit.
Apoi alege granița care corespunde cerinței: un proxy este suficient pentru un site, o regulă UID este suficientă pentru un cont, un profil de firewall este suficient când conexiunea trebuie să eșueze închis, iar un namespace este singura opțiune care ascunde tunelul de tot ce este în afara lui.