Hoe om een program deur ’n VPN te stuur sonder die hele masjien
Geen onbeheerde lessenaar bied ’n VPN-skakelaar per proses nie. Kies die laag wat die verkeer keur: ’n gebinde aansluiting, ’n UID-reël, ’n firewall-profiel of ’n SOCKS-hop.
Die VPN-kliënt maak verbinding, die blaaier werk, en die een opdrag waaroor jy werklik bekommerd is, verlaat steeds deur jou tuisverbinding. Of die teenoorgestelde gebeur: die tonnel gryp die verstekroete en die plaaslike lêerbediener verdwyn van die lessenaar. Ek het dit beleef met ’n bou-agent wat ’n interne artefak-spieël moes bereik terwyl die res van die gasheer op die kantoor-LAN gebly het, en die kliënt het presies twee keuses gebied: alle verkeer of niks. Die versoek is in beide rigtings dieselfde, hierdie program en nie die hele masjien nie, en die meeste kliënte het geen merkblokkie daarvoor nie, want die roetetabel het nog nooit geweet wat ’n program is nie.
Waarom die roetetabel jou program nie kan sien nie
Wanneer ’n proses ’n pakket stuur, soek die stapel die bestemmingsadres in die roetetabel op. Nie die uitvoerbare lêer se naam nie en ook nie die poort nie kom in daardie soektog voor. Domeinname misluk om dieselfde rede: ’n tonnel dra verkeer na ’n adres, en teen die tyd dat ’n naam in een ontbind, is roetering die enigste hefboom wat die kern nog het. Dis waarom “VPN net vir hierdie werf” uiteindelik ’n instaanbediener beteken, en “VPN net vir hierdie program” een van ’n klein stel meganismes beteken.
| Mechanism | What selects | Covers | On tunnel drop |
|---|---|---|---|
| Socket bind | source address | a client you control | connections fail |
| UID rule (Linux) | owning user | every process of one account | connections fail |
| Firewall profile (Windows) | adapter profile | one program | fails closed |
| Network namespace | namespace membership | everything started inside | connections fail |
| SOCKS hop | app proxy setting | one app or one browser profile | connections fail |
Nie een hiervan is ’n roete per proses nie. Elkeen skuif die besluit na ’n laag wat die program, die gebruiker of die app se eie konfigurasie kan sien.
’n Enkele bestemming is steeds ’n roete, nie ’n programreël nie. Op macOS is die gewone resep om “Stuur alle verkeer oor VPN” te ontmerk en ’n gasheerreël by te voeg: sudo route add -host internal-tuner.example -interface ppp0, weer verwyder met sudo route delete -host internal-tuner.example -interface ppp0. Lees eers die koppelvlaknaam uit ifconfig, want ppp0 is net ’n voorbeeld. Jokertekens werk nie in ’n gasheerreël nie, dus het ’n diens wat met verskeie name praat een inskrywing per naam nodig, en die reël dek elke proses op die masjien, nie die een wat jy in gedagte gehad het nie.
Bind die aansluiting aan die tonneladres
Wanneer jy die kliënt beheer, is binding die goedkoopste keurder. ’n Aansluiting wat aan die tonnel se plaaslike adres gebind is, verlaat deur daardie koppelvlak solank daar ’n roete vir die bestemming bestaan, en alles anders op die masjien hou sy normale pad. Die subtiliteit is dat die binding die bronadres kies terwyl die roetetabel steeds die uitgang kies. ’n Binding alleen skuif nie verkeer wat geen roete in die tonnel het nie, en dit is die fout agter die meeste verslae van “ek het dit gebind en dit gaan steeds die verkeerde kant uit”.
import socket
s = socket.socket()
s.bind(("10.8.0.2", 0))
s.connect(("build.internal.example", 443))Hoër-vlak gereedskap bied dieselfde idee: curl --interface 10.8.0.2, PHP se CURLOPT_INTERFACE, en die source_address-argument in Python se HTTP-kliënte. Die adres moet aan die tonnel behoort, en ’n tikfout hier misluk as ’n gewone verbindingsfout, nie as ’n roeteringsklag nie. Binding aan ’n toestelnaam eerder as ’n adres is ’n Linux-voorreg en dra nie oor na Windows of macOS nie, en daarom gee draagbare skripte die adres deur.
Linux: pas die eienaar, nie die programnaam nie
Die kern kan die eienaar-gebruiker van ’n aansluiting pas, dus word ’n toegewyde rekening die keurder. Sit die tonnel in sy eie roetetabel en stuur net daardie rekening se verkeer daarheen:
ip rule add uidrange 1001-1001 lookup 100
ip route add default dev tun0 table 100Dit werk net as die tonnel nie reeds die verstekroete is nie. Begin die kliënt met --route-nopull, of filter die gestuurde herleiding met --pull-filter ignore "redirect-gateway" op OpenVPN 2.4 en later, en voeg dan self die tabelinskrywing by. Anders bestaan albei verstekke en die een met laer prioriteit wen. Die doelbewuste weergawe daarvan is om die verstekroete na die tonnel se poort te wys, of om dit uit ’n ontkoppelskrip te doen: dit hou verkeer van die internet af wanneer die tonnel val, maar dit is ’n noodskakelaar, nie ’n keurder nie, en dit bly tot iemand dit verwyder.
Die sterker weergawe is ’n netwerk-naamruimte. Skep dit, laat die kliënt binne hardloop, en begin die toepassing met ip netns exec. Niks buite die naamruimte sien die tonnelkoppelvlak nie, dus kan die toepassing nie per ongeluk na die gasheerroete terugval nie. Die koste is reëel: die naamruimte het ’n veth-paar nodig om die fisiese netwerk te bereik en sy eie resolver-lêer, want die monteernaamruimte deel nie die gasheer se resolusie-instellings nie.
Twee foutmodusse verduidelik die meeste lekke. Naamresolusie gebeur steeds in die gasheer se resolver, dus verlaat die navraag buite die tonnel selfs wanneer die TCP-verbinding binne gaan. En IPv6 het sy eie ip -6 rule nodig, want ’n v4-reël dek dit nie.
Windows en macOS: beperk die firewall of gee ’n instaanbediener oor
Windows het ook geen roetekeurder per toepassing nie, maar sy firewall-reëls kan tot ’n profiel beperk word, en ’n VPN-adapter kan as Public geklassifiseer word terwyl die tuis-LAN Private bly. Skep uitgaande blokreëls vir die program op die Private- en Domain-profiele, laat Public oop, en die toepassing kan die netwerk net deur die tonnel bereik. Stel eers die verbinding se netwerkkategorie, bevestig dan dat die reël in die firewall se monitorpaneel gepas het. Dit is ’n blok, nie ’n roete nie: dit sluit wanneer die tonnel val, en dit kan nie ’n toepassing keer wat die firewall ignoreer nie.
macOS volg dieselfde logika met ander gereedskap. ’n SOCKS-luisteraar wat oor SSH bereik word, is die mins indringende opsie, want ssh -D 1080 user@host skep een plaaslike poort en net toepassings wat ingestel is om dit te gebruik, neem daardie pad. ’n pf-reël kan ’n program volgens gebruiker of groep blokkeer, maar dit kan steeds nie die tonnel vir daardie program alleen kies nie. Tunnelblick kan glad nie volgens domein filter nie, want roetes werk op adresse; OpenConnect-gebruikers gryp na vpn-slice wanneer die bediener ’n voorvoegsellys stuur wat te wyd is.
Twee alternatiewe dek wat die platformgereedskap mis. Blaaieruitbreidings wat die instaanbediener per domein wissel, is die praktiese antwoord vir “net hierdie werf”, want hulle werk op die toepassingslaag waar name nog bestaan; hulle dek een blaaier en niks anders nie. Die bot een is om die verskaffer se kliënt in ’n virtuele masjien te laat loop sodat die gas die roeteverandering besit terwyl die gasheer sy pad behou, teen die koste van twee netwerkstapels om te onderhou.
’n SOCKS-hop vir gereedskap sonder ’n bind-opsie
Die meeste toepassings kan na ’n HTTP- of SOCKS-instaanbediener gewys word selfs wanneer hulle niks van koppelvlakke weet nie. ’n SSH-kliënt voorsien een sonder enige verandering aan die bedienerkant:
ssh -N -D 127.0.0.1:9999 [email protected]Enigiets wat ingestel is om daardie poort te gebruik, verlaat by die bastion terwyl die res van die lessenaar sy normale verbinding behou. ’n Plaaslike 3proxy-geval wat aan die tonneladres gebind is, doen dieselfde vir een poort sonder SSH, en stunnel dek ’n enkele TLS-verpakte poort. Die grense is prakties: ’n instaanbediener dra net protokolle wat weet hoe om dit te gebruik, UDP het SOCKS5 UDP-assosiasie nodig wat baie kliënte se “SOCKS5”-instelling nie aktiveer nie, en DNS moet deur die instaanbediener opgelos word (socks5h in curl), anders lek die naam na die plaaslike resolver.
Hou die luisteraar op loopback tensy ’n ander masjien dit nodig het.
Verifieer die pad met ’n opname, nie ’n statusboodskap nie
Toepassingstatusvensters beskryf die instelling, nie die pakket nie. Maak opnames op albei koppelvlakke en vergelyk:
tcpdump -i any -n port 443
ss -tnp | grep 443As die verbinding op die fisiese koppelvlak verskyn met geen ooreenstemmende inskrywing op die tonnel nie, is die keurder nie in werking nie, wat die kliënt ook al rapporteer. Kontroleer IPv6 en die resolver afsonderlik, en kyk vir ’n tweede pad soos ’n hulpproses of ’n oorgeërfde stelselinstaanbediener.
Kies dan die grens wat by die vereiste pas: ’n instaanbediener is genoeg vir een werf, ’n UID-reël is genoeg vir een rekening, ’n firewall-profiel is genoeg wanneer die verbinding gesluit moet faal, en ’n naamruimte is die enigste opsie wat die tonnel vir alles daarbuite wegsteek.