VPN-klienten kobler til, nettleseren virker, og den ene kommandoen du faktisk bryr deg om går fortsatt ut via hjemmeforbindelsen. Eller det motsatte skjer: tunnelen tar standardruten og den lokale filserveren forsvinner fra skrivebordet. Jeg traff dette med en byggeagent som måtte nå et internt artifakt-speil mens resten av verten ble værende på kontor-LAN-et, og klienten tilbød nøyaktig to valg, all trafikk eller ingenting. Forespørselen er den samme begge veier, dette programmet og ikke hele maskinen, og de fleste klienter har ingen avkrysningsboks for den, fordi rutetabellen aldri har visst hva et program er.

Hvorfor rutetabellen ikke kan se programmet ditt

Når en prosess sender en pakke, slår stakken opp måladdressen i rutetabellen. Verken kjørbarhetsnavnet eller porten kommer inn i det oppslaget. Domenenavn feiler av samme grunn: en tunnel bærer trafikk til en adresse, og når et navn er løst til en, er ruting den eneste spaken kjernen fortsatt har. Det er derfor “VPN bare for dette nettstedet” ender med å bety en proxy, og “VPN bare for dette programmet” ender med å bety en av et lite sett mekanismer.

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

Ingen av disse er en rute per prosess. Hver av dem flytter avgjørelsen til et lag som kan se programmet, brukeren eller appens egen konfigurasjon.

En enkelt destinasjon er fortsatt en rute, ikke en programregel. På macOS er den vanlige oppskriften å fjerne merket for “Send all trafikk over VPN” og legge til en vertsrute: sudo route add -host internal-tuner.example -interface ppp0, fjernet igjen med sudo route delete -host internal-tuner.example -interface ppp0. Les grensesnittnavnet fra ifconfig først, siden ppp0 bare er et eksempel. Jokertegn fungerer ikke i en vertsrute, så en tjeneste som snakker med flere navn trenger én oppføring per navn, og regelen dekker hver prosess på maskinen, ikke den du hadde i tankene.

Bind socketen til tunneladressen

Når du kontrollerer klienten, er binding den billigste velgeren. En socket bundet til tunnelens lokale adresse går ut gjennom det grensesnittet så lenge en rute for målet finnes der, og alt annet på maskinen beholder sin normale vei. Det subtile er at bindingen velger kildeadressen mens rutetabellen fortsatt velger utgangen. En binding alene flytter ikke trafikk som ikke har noen rute inn i tunnelen, og det er feilen bak de fleste rapportene om at “jeg bandt den og den går fortsatt feil vei”.

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

Verktøy på høyere nivå viser den samme ideen: curl --interface 10.8.0.2, PHPs CURLOPT_INTERFACE, og source_address-argumentet i Pythons HTTP-klienter. Adressen må tilhøre tunnelen, og en skrivefeil her feiler som en vanlig tilkoblingsfeil, ikke som en ruteklage. Å binde til et enhetsnavn i stedet for en adresse er et Linux-privilegium og overføres ikke til Windows eller macOS, og derfor sender portable skript adressen.

Linux: match eieren, ikke programnavnet

Kjernen kan matche den eide brukeren av en socket, så en dedikert konto blir velgeren.

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

Dette virker bare hvis tunnelen ikke allerede er standardruten. Start klienten med --route-nopull, eller filtrer den dyttede omdirigeringen med --pull-filter ignore "redirect-gateway" på OpenVPN 2.4 og nyere, og legg deretter til tabelloppføringen selv. Ellers finnes begge standardene og den med lavere prioritet vinner. Den bevisste versjonen er å peke standardruten mot tunnelens gateway, eller gjøre det fra et frakoblingsskript: det holder trafikken borte fra internett når tunnelen faller, men det er en kill switch, ikke en velger, og den blir til noen fjerner den.

Den sterkere versjonen er et nettverksnavnerom. Opprett det, kjør klienten inni det, og start applikasjonen med ip netns exec. Ingenting utenfor navnerommet ser tunnelgrensesnittet, så applikasjonen kan ikke falle tilbake til vertsruten ved et uhell. Kostnaden er reell: navnerommet trenger et veth-par for å nå det fysiske nettverket og sin egen resolverfil, fordi mount-navnerommet ikke deler vertens oppløsningsinnstillinger.

To feilmoduser forklarer de fleste lekkasjene. Navneoppløsning skjer fortsatt i vertens resolver, så spørringen går utenfor tunnelen selv når TCP-tilkoblingen går inn. Og IPv6 trenger sin egen ip -6 rule, fordi en v4-regel ikke dekker den.

Windows og macOS: begrens brannmuren eller overlat en proxy

Windows har heller ingen rutefinner per applikasjon, men brannmurreglene kan begrenses til en profil, og en VPN-adapter kan klassifiseres som Offentlig mens hjemme-LAN-et forblir Privat. Opprett utgående blokkregler for programmet på profilene Privat og Domene, la Offentlig være åpen, og applikasjonen kan bare nå nettverket gjennom tunnelen. Sett tilkoblingens nettverkskategori først, og bekreft deretter at regelen samsvarte i brannmurens overvåkingspanel. Det er en blokk, ikke en rute: den feiler lukket når tunnelen faller, og den kan ikke stoppe en applikasjon som ignorerer brannmuren.

macOS følger samme logikk med andre verktøy. En SOCKS-lytter nådd over SSH er det minst invasive alternativet, siden ssh -D 1080 user@host oppretter én lokal port og bare applikasjoner konfigurert til å bruke den tar den veien. En pf-regel kan blokkere et program etter bruker eller gruppe, men den kan fortsatt ikke velge tunnelen for det programmet alene. Tunnelblick kan ikke filtrere etter domene i det hele tatt, fordi ruter virker på adresser; OpenConnect-brukere tyr til vpn-slice når serveren dytter en prefiksliste som er for bred.

To arbeidsomveier dekker det plattformverktøyene mangler. Nettleserutvidelser som bytter proxy per domene er det praktiske svaret for “bare dette nettstedet”, fordi de virker på applikasjonslaget der navn fortsatt finnes; de dekker én nettleser og ingenting annet. Den grove er å kjøre leverandørklienten inne i en virtuell maskin slik at gjesten eier ruteendringen mens verten beholder sin vei, til kostnaden av to nettverksstakker å vedlikeholde.

Et SOCKS-hopp for verktøy uten bindingsvalg

De fleste applikasjoner kan pekes mot en HTTP- eller SOCKS-proxy selv når de ikke vet noe om grensesnitt. En SSH-klient skaffer en uten endring på serversiden:

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

Alt som er konfigurert til å bruke den porten, går ut ved bastionen mens resten av skrivebordet beholder sin normale tilkobling. En lokal 3proxy-instans bundet til tunneladressen gjør det samme for én port uten SSH, og stunnel dekker én enkelt TLS-innpakket port. Grensene er praktiske: en proxy bærer bare protokoller som vet hvordan de bruker den, UDP trenger SOCKS5 UDP-assosiasjon som mange klienters “SOCKS5”-innstilling ikke aktiverer, og DNS må løses av proxyen (socks5h i curl) ellers lekker navnet til den lokale resolveren.

Hold lytteren på loopback med mindre en annen maskin trenger den.

Verifiser veien med en fangst, ikke en statusmelding

Applikasjonsstatusvinduer beskriver innstillingen, ikke pakken. Fang på begge grensesnittene og sammenlign:

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

Hvis tilkoblingen vises på det fysiske grensesnittet uten en samsvarende oppføring på tunnelen, er ikke velgeren i kraft, uansett hva klienten rapporterer. Sjekk IPv6 og resolveren separat, og sjekk etter en annen vei som en hjelpeprosess eller en arvet systemproxy.

Velg så grensen som passer kravet: en proxy er nok for ett nettsted, en UID-regel er nok for én konto, en brannmurprofil er nok når tilkoblingen må feile lukket, og et navnerom er det eneste alternativet som skjuler tunnelen for alt utenfor det.

Relaterte artikler