Sådan sender du én app gennem en VPN uden hele maskinen
Ingen uadministreret computer tilbyder en VPN-kontakt pr. proces. Vælg det lag, der udvælger trafikken: en bundet socket, en UID-regel, en firewallprofil eller et SOCKS-hop.
VPN-klienten opretter forbindelse, browseren virker, og den ene kommando, du faktisk bekymrer dig om, forlader stadig gennem din hjemmeforbindelse. Eller det modsatte sker: tunnelen tager standardruten, og den lokale filserver forsvinder fra skrivebordet. Jeg ramte det med en build-agent, der skulle nå et internt artefakt-spejl, mens resten af værten blev på kontorets LAN, og klienten tilbød præcis to valg: al trafik eller intet. Anmodningen er den samme i begge retninger, dette program og ikke hele maskinen, og de fleste klienter har ingen afkrydsningsboks til det, fordi rutetabellen aldrig har vidst, hvad et program er.
Hvorfor rutetabellen ikke kan se dit program
Når en proces sender en pakke, slår stakken destinationsadressen op i rutetabellen. Hverken den eksekverbare fils navn eller porten indgår i det opslag. Domænenavne fejler af samme grund: en tunnel fører trafik til en adresse, og når et navn er opløst til en, er routing det eneste håndtag, kernen stadig har. Derfor ender »VPN kun til dette websted« med at betyde en proxy, og »VPN kun til dette program« med at betyde en af en lille gruppe mekanismer.
| 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 |
Intet af dette er en rute pr. proces. Hver af dem flytter beslutningen til et lag, der kan se programmet, brugeren eller appens egen konfiguration.
En enkelt destination er stadig en rute, ikke en programregel. På macOS er den sædvanlige opskrift at fjerne markeringen i »Send al trafik over VPN« og tilføje en værtsrute: sudo route add -host internal-tuner.example -interface ppp0, som fjernes igen med sudo route delete -host internal-tuner.example -interface ppp0. Læs først grænsefladens navn fra ifconfig, da ppp0 kun er et eksempel. Jokertegn virker ikke i en værtsrute, så en tjeneste, der taler med flere navne, har brug for én post pr. navn, og reglen dækker hver proces på maskinen, ikke den, du havde i tankerne.
Bind socket'en til tunnelens adresse
Når du kontrollerer klienten, er binding den billigste vælger. En socket bundet til tunnelens lokale adresse forlader gennem den grænseflade, så længe der findes en rute til destinationen der, og alt andet på maskinen beholder sin normale vej. Det fine er, at bindingen vælger kildeadressen, mens rutetabellen stadig vælger udgangen. Binding alene flytter ikke trafik, der ikke har nogen rute ind i tunnelen, og det er fejlen bag de fleste rapporter om »jeg bandt den, og den går stadig den gale vej«.
import socket
s = socket.socket()
s.bind(("10.8.0.2", 0))
s.connect(("build.internal.example", 443))Værktøjer på højere niveau viser samme idé: curl --interface 10.8.0.2, CURLOPT_INTERFACE i PHP og argumentet source_address i Pythons HTTP-klienter. Adressen skal tilhøre tunnelen, og en tastefejl her fejler som en almindelig forbindelsesfejl, ikke som en klage over routing. At binde til et enhedsnavn i stedet for en adresse er en Linux-privilegium og overføres ikke til Windows eller macOS, hvorfor portable scripts sender adressen.
Linux: match ejeren, ikke programnavnet
Kernen kan matche ejeren af en socket, så en dedikeret konto bliver vælgeren. Læg tunnelen i sin egen rutetabel og send kun den kontos trafik derhen:
ip rule add uidrange 1001-1001 lookup 100
ip route add default dev tun0 table 100Det virker kun, hvis tunnelen ikke allerede er standardruten. Start klienten med --route-nopull, eller filtrer den skubbede omdirigering med --pull-filter ignore "redirect-gateway" på OpenVPN 2.4 og senere, og tilføj derefter selv tabelposten. Ellers findes begge standardruter, og den med lavest prioritet vinder. Den bevidste version af det er at pege standardruten mod tunnelens gateway eller gøre det fra et afbrydelsesscript: det holder trafikken væk fra internettet, når tunnelen falder, men det er en kill switch, ikke en vælger, og den bliver, til nogen fjerner den.
Den stærkere version er et netværksnamespace. Opret det, kør klienten indeni, og start applikationen med ip netns exec. Intet uden for namespacet ser tunnelens grænseflade, så applikationen kan ikke falde tilbage til værtens rute ved et uheld. Prisen er reel: namespacet har brug for et veth-par for at nå det fysiske netværk og sin egen resolver-fil, fordi mount-namespacet ikke deler værtens opløsningsindstillinger.
To fejltilstande forklarer de fleste lækager. Navneopløsning sker stadig i værtens resolver, så forespørgslen forlader uden om tunnelen, selv når TCP-forbindelsen går indeni. Og IPv6 har brug for sin egen ip -6 rule, fordi en v4-regel ikke dækker den.
Windows og macOS: afgræns firewallen eller giv en proxy videre
Windows har heller ikke en rutevælger pr. program, men firewallreglerne kan afgrænses til en profil, og en VPN-adapter kan klassificeres som »offentlig«, mens hjemmets LAN forbliver »privat«. Opret udgående blokeringsregler for programmet på profilerne »privat« og »domæne«, lad »offentlig« være åben, og applikationen kan kun nå netværket gennem tunnelen. Indstil først forbindelsens netværkskategori, og bekræft derefter, at reglen matchede i firewallens overvågningspanel. Det er en blokering, ikke en rute: den fejler lukket, når tunnelen falder, og den kan ikke stoppe et program, der ignorerer firewallen.
macOS følger samme logik med andre værktøjer. En SOCKS-lytter nået over SSH er den mindst indgribende mulighed, fordi ssh -D 1080 user@host opretter én lokal port, og kun programmer konfigureret til at bruge den tager den vej. En pf-regel kan blokere et program efter bruger eller gruppe, men den kan stadig ikke vælge tunnelen for det program alene. Tunnelblick kan slet ikke filtrere efter domæne, fordi ruter arbejder på adresser; OpenConnect-brugere griber til vpn-slice, når serveren skubber en for bred præfikseliste.
To alternative løsninger dækker det, platformsværktøjerne overser. Browserudvidelser, der skifter proxy pr. domæne, er det praktiske svar på »kun dette websted«, fordi de virker på applikationslaget, hvor navne stadig findes; de dækker én browser og intet andet. Den grove løsning er at køre leverandørens klient i en virtuel maskine, så gæsten ejer ruteændringen, mens værten beholder sin vej, til prisen af to netværksstakke at vedligeholde.
Et SOCKS-hop til værktøjer uden bindingsmulighed
De fleste applikationer kan peges på en HTTP- eller SOCKS-proxy, selv når de intet ved om grænseflader. En SSH-klient leverer en uden ændring på serversiden:
ssh -N -D 127.0.0.1:9999 [email protected]Alt, der er konfigureret til at bruge den port, forlader ved bastionen, mens resten af skrivebordet beholder sin normale forbindelse. En lokal 3proxy-instans bundet til tunnelens adresse gør det samme for én port uden SSH, og stunnel dækker én TLS-indpakket port. Grænserne er praktiske: en proxy bærer kun protokoller, der ved, hvordan de bruger den, UDP kræver SOCKS5 UDP-association, som mange klienters »SOCKS5«-indstilling ikke aktiverer, og DNS skal opløses af proxyen (socks5h i curl), ellers lækker navnet til den lokale resolver.
Hold lytteren på loopback, medmindre en anden maskine har brug for den.
Bekræft vejen med en optagelse, ikke en statusmeddelelse
Applikationens statusvinduer beskriver indstillingen, ikke pakken. Optag på begge grænseflader og sammenlign:
tcpdump -i any -n port 443
ss -tnp | grep 443Hvis forbindelsen vises på den fysiske grænseflade uden en tilsvarende post på tunnelen, er vælgeren ikke i kraft, uanset hvad klienten rapporterer. Kontroller IPv6 og resolveren særskilt, og kontroller for en anden vej som en hjælpeproces eller en nedarvet systemproxy.
Vælg så den grænse, der passer til kravet: en proxy er nok til ét websted, en UID-regel er nok til én konto, en firewallprofil er nok, når forbindelsen skal fejle lukket, og et namespace er den eneste mulighed, der skjuler tunnelen for alt udenfor.