Fixar un sol programa a una VPN a Linux és sobretot un problema d'encaminament, i la regla del tallafoc només decideix què passa després que s'hagi triat la ruta. Si feu coincidir l'UID del programa a OUTPUT abans que el túnel posseeixi la ruta per defecte, la regla descarta precisament el trànsit que volíeu protegir. La mateixa regla tampoc veu el procés del resoldre que respon a les consultes DNS del programa. Ordeneu-ho bé i un sol compte sense privilegis es converteix en un selector fiable de túnel dividit.

Doneu al compte una ruta abans d'un veredicte

La coincidència de propietari respon una pregunta: ha construït el paquet un socket local que pertany a aquest UID? No tria cap interfície i no pot moure el trànsit que la taula principal ja envia per la NIC física. Per això una regla d'UID sense cap ruta corresponent bloqueja el programa en lloc de redirigir-lo.

Envieu el compte primer per la seva taula, i després deixeu que el tallafoc confirmi el resultat. Això funciona en un amfitrió l'iproute2 del qual entén uidrange, cosa que ip rule help informa en una línia. La taula 100 conté una ruta per defecte pel túnel, i la regla envia només l'UID 1000 a aquesta taula. Tota la resta es queda a la taula principal i continua fent servir l'enllaç físic.

ip rule add uidrange 1000-1000 table 100
ip route add default dev tun0 table 100

Un segon compte que necessiti un túnel diferent rep la taula 101 i la seva pròpia regla, perquè dos perfils no poden posseir tots dos la ruta per defecte a la mateixa taula. Els perfils de NetworkManager són de tot el sistema, així que aquesta part continua sent manual, i les regles desapareixen en reiniciar si res no les restaura. Escriviu les taules en un script d'arrencada al costat de la definició del túnel perquè una reconnexió no pugui deixar un compte al camí equivocat.

Per què l'excepció de loopback mata el túnel

La majoria de versions copiades de la regla descarten cada paquet que no surt per loopback. El túnel no és loopback, així que aquesta forma també descarta el trànsit del túnel, i el resultat sembla un VPN mort més que una errada del tallafoc.

Quan s'executa OUTPUT, el nucli ja ha triat una interfície de sortida, així que -o es compara amb la resposta de la ruta i no amb la capçalera del paquet. La coincidència de propietari només es consulta a OUTPUT i POSTROUTING, i els paquets reenviats no porten cap socket perquè la llegeixi. Aquesta combinació vol dir que l'excepció ha de nomenar la interfície del túnel i negar aquesta coincidència, en lloc de negar loopback.

Loopback encara es mereix el seu propi ACCEPT per sobre del DROP, perquè el primer paquet DNS del programa va al stub local a través de lo. Posa les dues regles en aquest ordre i el compte arriba al stub, després al túnel, i enlloc més.

iptables -I OUTPUT 1 -o lo -m owner --uid-owner vpnapp -j ACCEPT
iptables -A OUTPUT -m owner --uid-owner vpnapp ! -o tun0 -j DROP

IPv6 és un conjunt de regles a part, i una parella només per a v4 deixa ip6tables intacte. Un programa que prefereixi registres AAAA sortiria llavors per l'enllaç físic sense arribar mai al DROP. Reflectiu totes dues regles a ip6tables, o desactiveu IPv6 a l'amfitrió quan el túnel no porti cap adreça v6. Les regles afegides a mà també desapareixen en reiniciar, així que deseu-les amb netfilter-persistent save o reconstruïu la mateixa lògica com una taula nftables carregada per la unitat d'arrencada.

Les respostes DNS vénen d'un altre procés

La consulta del programa a 127.0.0.53 passa l'ACCEPT de loopback, i és aquí on s'atura la coincidència d'UID. El resoldre stub reenvia cap amunt des del seu propi compte de servei, així que una regla vinculada a vpnapp no veu mai la segona consulta. Si el resoldre tria un servidor LAN per a l'enllaç, les respostes tornen per la NIC física mentre els paquets propis del programa es queden al túnel. El resultat visible és un túnel que funciona amb noms trencats, o noms respostos per un servidor que no vau triar.

Corregiu la resolució de noms a l'enllaç del túnel, no a l'aplicació. Doneu al perfil del túnel un servidor DNS i un domini d'encaminament, i després confirmeu amb resolvectl query i resolvectl status tun0 que el resoldre el fa servir per als noms que importen. Dins d'un espai de noms de xarxa el problema desapareix, perquè el resoldre de l'espai només coneix el túnel.

Comproveu la regla des del mateix compte

Pregunteu al nucli quina ruta rep un UID, després feu una petició com aquest compte i compareu l'adreça de sortida amb la del vostre propi compte. Si tots dos informen de la mateixa adreça, la regla coincideix amb una altra cosa que no és el trànsit que heu provat. La primera ordre de sota mostra la consulta de ruta sense enviar cap paquet, cosa que separa una errada d'encaminament d'una errada de tallafoc.

ip route get 1.1.1.1 uid 1000
sudo -u vpnapp curl -s https://ifconfig.co

Els comptadors mostren si una regla s'ha disparat mai. Executeu iptables -vnL OUTPUT, llegiu el recompte de paquets al costat de cada regla, i tracteu un zero al costat del DROP després de trànsit real com una errada de coincidència.

El trànsit reenviat queda fora d'aquest disseny. Un contenidor o una VM darrere d'un pont no té cap socket local propietat de vpnapp, així que la coincidència de propietari no s'activa mai i aquests paquets segueixen la política FORWARD. Si aquesta càrrega s'ha de cobrir, proveu-la per separat i escriviu una política vinculada al pont o a l'adreça del contenidor. El disseny aguanta quan la taula, l'excepció d'interfície, el resoldre i les regles v6 assenyalen tots la mateixa direcció; deixeu-ne fora qualsevol i el compte o bé filtra o perd la connexió.

Articles relacionats