Como encamiñar unha soa aplicación por VPN sen toda a máquina
Ningún escritorio non xestionado ofrece un conmutador de VPN por proceso. Escolla a capa que selecciona o tráfico: un socket vinculado, unha regra UID, un perfil de devasa ou un salto SOCKS.
O cliente VPN conéctase, o navegador funciona, e o único comando que de verdade che importa segue saíndo pola túa conexión de casa. Ou ocorre o contrario: o túnel fíxase coa ruta por defecto e o servidor de ficheiros local desaparece do escritorio. Atopei isto cun axente de compilación que tiña que chegar a un espello interno de artefactos mentres o resto do host ficaba na LAN da oficina, e o cliente ofrecía exactamente dúas opcións: todo o tráfico ou nada. A petición en ambos os sentidos é a mesma, este programa e non toda a máquina, e a maioría dos clientes non teñen casilla para iso, porque a táboa de rutas nunca soubo que é un programa.
Por que a táboa de rutas non pode ver o teu programa
Cando un proceso envía un paquete, a pila busca o enderezo de destino na táboa de rutas. Nin o nome do executábel nin o porto entran nesa busca. Os nomes de dominio fallan polo mesmo motivo: un túnel leva o tráfico a un enderezo, e para cando un nome se resolve nun, o encamiñamento é a única panca que lle queda ao núcleo. Por iso «VPN só para este sitio» acaba significando un proxy, e «VPN só para este programa» acaba significando un dun pequeno conxunto de mecanismos.
| 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 |
Ningunha destas é unha ruta por proceso. Cada unha traslada a decisión a unha capa que pode ver o programa, o usuario ou a propia configuración da aplicación.
Un só destino segue sendo unha ruta, non unha regra de programa. En macOS a receita habitual é desmarcar «Enviar todo o tráfico por VPN» e engadir unha ruta de host: sudo route add -host internal-tuner.example -interface ppp0, que se retira outra vez con sudo route delete -host internal-tuner.example -interface ppp0. Lea primeiro o nome da interface en ifconfig, porque ppp0 é só un exemplo. Os comodíns non funcionan nunha ruta de host, así que un servizo que fala con varios nomes precisa unha entrada por nome, e a regra cobre todos os procesos da máquina, non o que tiña en mente.
Vincule o socket ao enderezo do túnel
Cando controlas o cliente, vincular é o selector máis barato. Un socket vinculado ao enderezo local do túnel sae por esa interface mentres exista alí unha ruta cara ao destino, e todo o demais da máquina conserva o seu camiño normal. A sutileza é que a vinculación escolle o enderezo de orixe mentres a táboa de rutas segue escollendo a saída. A vinculación por si soa non move o tráfico que non ten ruta cara ao túnel, e ese é o erro detrás da maioría dos informes de «vincul e segue saíndo polo lado equivocado».
import socket
s = socket.socket()
s.bind(("10.8.0.2", 0))
s.connect(("build.internal.example", 443))As ferramentas de máis alto nivel expoñen a mesma idea: curl --interface 10.8.0.2, CURLOPT_INTERFACE en PHP e o argumento source_address nos clientes HTTP de Python. O enderezo ten que pertencer ao túnel, e unha errata aquí falla como un erro de conexión corrente, non como unha queixa de encamiñamento. Vincular a un nome de dispositivo en lugar de a un enderezo é un privilexio de Linux e non se traslada a Windows nin a macOS, por iso os scripts portátiles pasan o enderezo.
Linux: faga coincidir o propietario, non o nome do programa
O núcleo pode facer coincidir o usuario propietario dun socket, así que unha conta dedicada convértese no selector. Pon o túnel na súa propia táboa de rutas e envía alí só o tráfico desa conta:
ip rule add uidrange 1001-1001 lookup 100
ip route add default dev tun0 table 100Isto só funciona se o túnel non é xa a ruta por defecto. Inicie o cliente con --route-nopull, ou filtre o redireccionamento empurrado con --pull-filter ignore "redirect-gateway" en OpenVPN 2.4 e posteriores, e logo engada vostede mesmo a entrada na táboa. Se non, existen as dúas rutas por defecto e gaña a de menor prioridade. A versión deliberada diso é apuntar a ruta por defecto á porta de enlace do túnel, ou facelo desde un script de desconexión: mantén o tráfico fóra de internet cando o túnel cae, pero iso é un interruptor de seguridade, non un selector, e queda ata que alguén o retire.
A versión máis forte é un espazo de nomes de rede. Créao, execute o cliente dentro e arrinque a aplicación con ip netns exec. Nada fóra do espazo de nomes ve a interface do túnel, así que a aplicación non pode volver por accidente á ruta do host. O custo é real: o espazo de nomes necesita un par veth para chegar á rede física e o seu propio ficheiro de resolución, porque o espazo de nomes de montaxe non comparte os axustes de resolución do host.
Dous modos de fallo explican a maioría das fugas. A resolución de nomes segue a ocorrer no resolutor do host, así que a consulta sae fóra do túnel incluso cando a conexión TCP entra dentro. E IPv6 necesita a súa propia ip -6 rule, porque unha regra v4 non a cobre.
Windows e macOS: acouta a devasa ou cede un proxy
Windows tampouco ten selector de ruta por aplicación, pero as súas regras de devasa poden acoutarse a un perfil, e un adaptador VPN pode clasificarse como «público» mentres a LAN de casa segue «privada». Cree regras de bloqueo de saída para o programa nos perfís «privado» e «dominio», deixe «público» aberto, e a aplicación só poderá chegar á rede polo túnel. Fixe primeiro a categoría de rede da conexión e logo confirme que a regra coincidiu no panel de supervisión da devasa. É un bloqueo, non unha ruta: falla pechado cando o túnel cae, e non pode deter unha aplicación que ignora a devasa.
macOS segue a mesma lóxica con outras ferramentas. Un oínte SOCKS alcanzado por SSH é a opción menos invasiva, porque ssh -D 1080 user@host crea un porto local e só as aplicacións configuradas para usalo toman ese camiño. Unha regra pf pode bloquear un programa por usuario ou grupo, pero segue sen poder escoller o túnel só para ese programa. Tunnelblick non pode filtrar por dominio de ningún xeito, porque as rutas traballan con enderezos; os usuarios de OpenConnect recorren a vpn-slice cando o servidor empurra unha lista de prefixos demasiado ampla.
Dúas solucións alternativas cobren o que as ferramentas da plataforma pasan por alto. As extensións de navegador que cambian o proxy por dominio son a resposta práctica para «só este sitio», porque actúan na capa de aplicación, onde os nomes aínda existen; cobren un navegador e nada máis. A solución tosca é executar o cliente do provedor dentro dunha máquina virtual para que o convidado sexa dono do cambio de ruta mentres o anfitrión conserva o seu camiño, a costa de dúas pilas de rede que manter.
Un salto SOCKS para ferramentas sen opción de vinculación
A maioría das aplicacións pódense apuntar a un proxy HTTP ou SOCKS mesmo cando non saben nada de interfaces. Un cliente SSH fornece un sen ningún cambio no lado do servidor:
ssh -N -D 127.0.0.1:9999 [email protected]Todo o configurado para usar ese porto sae no bastión mentres o resto do escritorio conserva a súa conexión normal. Unha instancia local de 3proxy vinculada ao enderezo do túnel fai o mesmo para un porto sen SSH, e stunnel cobre un único porto envolto en TLS. Os límites son prácticos: un proxy só transporta protocolos que saben usalo, UDP necesita unha asociación UDP de SOCKS5 que o axuste «SOCKS5» de moitos clientes non activa, e o DNS debe resolvelo o proxy (socks5h en curl) ou o nome fíltrase ao resolutor local.
Manteña o oínte en loopback salvo que outra máquina o necesite.
Verifique o camiño cunha captura, non cunha mensaxe de estado
As ventás de estado da aplicación describen o axuste, non o paquete. Capture nas dúas interfaces e compare:
tcpdump -i any -n port 443
ss -tnp | grep 443Se a conexión aparece na interface física sen unha entrada correspondente no túnel, o selector non está en efecto, diga o que diga o cliente. Comprobe IPv6 e o resolutor por separado, e busque un segundo camiño como un proceso auxiliar ou un proxy de sistema herdado.
Logo escolla a fronteira que encaixe co requisito: un proxy abonda para un sitio, unha regra UID abonda para unha conta, un perfil de devasa abonda cando a conexión debe fallar pechada, e un espazo de nomes é a única opción que esconde o túnel de todo o que está fóra.