Hur du dirigerar en app via VPN utan hela datorn
Ingen ohanterad dator erbjuder en VPN-växel per process. Välj det lager som väljer trafiken: en bunden socket, en UID-regel, en brandväggsprofil eller ett SOCKS-hopp.
VPN-klienten ansluter, webbläsaren fungerar, och det enda kommando du faktiskt bryr dig om lämnar fortfarande via din hemmauppkoppling. Eller så händer det motsatta: tunneln tar över standardrutten och den lokala filservern försvinner från skrivbordet. Jag råkade ut för detta med en byggagent som behövde nå en intern artefaktspegel medan resten av värden stannade i kontorets LAN, och klienten erbjöd exakt två val, all trafik eller ingen. Begäran är densamma i båda riktningarna, det här programmet och inte hela datorn, och de flesta klienter har ingen kryssruta för den, eftersom routingtabellen aldrig har vetat vad ett program är.
Varför routingtabellen inte kan se ditt program
När en process skickar ett paket slår stacken upp destinationsadressen i routingtabellen. Varken det körbara filnamnet eller porten ingår i den uppslagningen. Domännamn misslyckas av samma skäl: en tunnel transporterar trafik till en adress, och när ett namn har lösts upp till en sådan är routingen den enda spaken kärnan fortfarande har. Det är därför "VPN bara för den här webbplatsen" till slut betyder en proxyserver, och "VPN bara för det här programmet" betyder en av en liten uppsättning mekanismer.
| Mekanism | Vad som väljer | Täcker | Vid tunnelbrott |
|---|---|---|---|
| Socketbindning | källadress | en klient du kontrollerar | anslutningarna misslyckas |
| UID-regel (Linux) | ägande användare | varje process i ett konto | anslutningarna misslyckas |
| Brandväggsprofil (Windows) | adapterns profil | ett program | misslyckas stängt |
| Nätverksnamnrymd | medlemskap i namnrymden | allt som startas inuti | anslutningarna misslyckas |
| SOCKS-hopp | appens proxyinställning | en app eller en webbläsarprofil | anslutningarna misslyckas |
Ingen av dem är en rutt per process. Var och en flyttar beslutet till ett lager som kan se programmet, användaren eller appens egen konfiguration.
Ett enda mål är fortfarande en rutt, inte en programregel. På macOS är det vanliga receptet att avmarkera "Skicka all trafik över VPN" och lägga till en värdrutt: sudo route add -host internal-tuner.example -interface ppp0, som tas bort igen med sudo route delete -host internal-tuner.example -interface ppp0. Läs gränssnittsnamnet från ifconfig först, eftersom ppp0 bara är ett exempel. Jokertecken fungerar inte i en värdrutt, så en tjänst som pratar med flera namn behöver en post per namn, och regeln täcker varje process på datorn, inte den du hade i tankarna.
Bind socketen till tunnelns adress
När du kontrollerar klienten är bindning den billigaste väljaren. En socket bunden till tunnelns lokala adress lämnar via det gränssnittet så länge det finns en rutt dit för målet, och allt annat på datorn behåller sin normala väg. Det subtila är att bindningen väljer källadressen medan routingtabellen fortfarande väljer utgången. En bindning ensam flyttar inte trafik som saknar rutt in i tunneln, och det är misstaget bakom de flesta rapporter av typen "jag band den och den går ändå fel väg".
import socket
s = socket.socket()
s.bind(("10.8.0.2", 0))
s.connect(("build.internal.example", 443))Verktyg på högre nivå visar samma idé: curl --interface 10.8.0.2, CURLOPT_INTERFACE i PHP och argumentet source_address i Pythons HTTP-klienter. Adressen måste tillhöra tunneln, och ett stavfel här misslyckas som ett vanligt anslutningsfel, inte som ett routingklagomål. Att binda till ett enhetsnamn i stället för en adress är ett Linuxprivilegium och följer inte med till Windows eller macOS, vilket är varför portabla skript skickar adressen.
Linux: matcha ägaren, inte programnamnet
Kärnan kan matcha socketens ägande användare, så ett dedikerat konto blir väljaren. Lägg tunneln i en egen routingtabell och skicka bara det kontots trafik dit:
ip rule add uidrange 1001-1001 lookup 100
ip route add default dev tun0 table 100Detta fungerar bara om tunneln inte redan är standardrutten. Starta klienten med --route-nopull, eller filtrera bort den påtvingade omdirigeringen med --pull-filter ignore "redirect-gateway" på OpenVPN 2.4 och senare, och lägg sedan till tabellposten själv. Annars finns båda standardrutterna och den med lägre prioritet vinner. Den avsiktliga varianten är att peka standardrutten mot tunnelns gateway, eller att göra det från ett frånkopplingsskript: det håller trafiken borta från internet när tunneln faller, men det är en nödstopp, inte en väljare, och den stannar tills någon tar bort den.
Den starkare varianten är en nätverksnamnrymd. Skapa den, kör klienten inuti och starta programmet med ip netns exec. Inget utanför namnrymden ser tunnelns gränssnitt, så programmet kan inte av misstag falla tillbaka på värdens rutt. Kostnaden är verklig: namnrymden behöver ett veth-par för att nå det fysiska nätet och en egen resolverfil, eftersom monteringsnamnrymden inte delar värdens upplösningsinställningar.
Två felmoder förklarar de flesta läckor. Namnupplösning sker fortfarande i värdens resolver, så frågan lämnar utanför tunneln även när TCP-anslutningen går inuti. Och IPv6 behöver sin egen ip -6 rule, eftersom en v4-regel inte täcker den.
Windows och macOS: begränsa brandväggen eller lämna över en proxy
Inte heller Windows har en ruttväljare per program, men brandväggsreglerna kan begränsas till en profil, och en VPN-adapter kan klassas som Offentlig medan hemmanätet förblir Privat. Skapa regler som blockerar utgående trafik för programmet på profilerna Privat och Domän, lämna Offentlig öppen, och programmet kan bara nå nätet via tunneln. Ställ först in anslutningens nätverkskategori och bekräfta sedan i brandväggens övervakningspanel att regeln matchade. Det är en blockering, inte en rutt: den misslyckas stängt när tunneln faller och kan inte stoppa ett program som ignorerar brandväggen.
macOS följer samma logik med andra verktyg. En SOCKS-lyssnare nådd via SSH är det minst invasiva alternativet, eftersom ssh -D 1080 user@host skapar en lokal port och bara program som konfigurerats att använda den tar den vägen. En pf-regel kan blockera ett program per användare eller grupp, men den kan fortfarande inte välja tunneln bara för det programmet. Tunnelblick kan inte filtrera på domän alls, eftersom rutter arbetar med adresser; OpenConnect-användare tar till vpn-slice när servern skickar en alltför bred prefixlista.
Två undantag täcker det plattformens verktyg missar. Webbläsartillägg som byter proxy per domän är det praktiska svaret för "bara den här webbplatsen", eftersom de verkar på applikationslagret där namn fortfarande finns; de täcker en webbläsare och inget annat. Den grova varianten är att köra leverantörens klient i en virtuell maskin så att gästen äger ruttändringen medan värden behåller sin väg, till priset av två nätverksstackar att underhålla.
Ett SOCKS-hopp för verktyg utan bindningsval
De flesta program kan pekas mot en HTTP- eller SOCKS-proxy även när de inte vet något om gränssnitt. En SSH-klient tillhandahåller en utan ändring på serversidan:
ssh -N -D 127.0.0.1:9999 [email protected]Allt som konfigurerats att använda den porten går ut vid bastionen medan resten av skrivbordet behåller sin normala anslutning. En lokal 3proxy-instans bunden till tunnelns adress gör samma sak för en port utan SSH, och stunnel täcker en enda TLS-inlindad port. Gränserna är praktiska: en proxy bär bara protokoll som vet hur man använder den, UDP kräver SOCKS5 UDP-association som många klienters "SOCKS5"-inställning inte aktiverar, och DNS måste lösas av proxyn (socks5h i curl) annars läcker namnet till den lokala resolvern.
Håll lyssnaren på loopback om ingen annan dator behöver den.
Verifiera vägen med en avlyssning, inte med ett statusmeddelande
Programmets statusfönster beskriver inställningen, inte paketet. Fånga trafik på båda gränssnitten och jämför:
tcpdump -i any -n port 443
ss -tnp | grep 443Om anslutningen dyker upp på det fysiska gränssnittet utan motsvarande post på tunneln är väljaren inte verksam, vad klienten än rapporterar. Kontrollera IPv6 och resolvern separat, och leta efter en andra väg som en hjälpprocess eller en ärvd systemproxy.
Välj sedan den gräns som matchar kravet: en proxy räcker för en webbplats, en UID-regel räcker för ett konto, en brandväggsprofil räcker när anslutningen måste misslyckas stängt, och en namnrymd är det enda alternativet som döljer tunneln för allt utanför den.