Eén Linux-proces buiten een WireGuard-tunnel draaien
Geef één programma zijn eigen netwerkcontext zodat het een WireGuard-host met volledige tunnel verlaat, en herstel daarna de doorstuur- en markeringsregels die het blokkeren.
Je kunt een programma niet vrijstellen van een WireGuard-host met volledige tunnel door routes te bewerken. Routes kiezen een pad op basis van bestemming, en wat je wilt vrijstellen is een proces, dus de kernel heeft niets om op te matchen. De werkbare aanpak is dat proces zijn eigen netwerkcontext geven: een namespace met een eigen interface, eigen adressen en een eigen standaardroute, verbonden met de host via een veth-paar. Ik heb dit op Ubuntu 24.04 met wireguard-tools 1.0.20210914 gedraaid, en de namespace was nooit het probleem — het doorstuurbeleid van de host en de markeringsregels van de tunnel waren dat.
Kies eerst het soort context dat je nodig hebt
In de praktijk komen er twee vormen voor. Als de host een volledige tunnel draait en één programma via het ISP-pad naar buiten moet, bouw je een tweede namespace, verbind je die met de host en laat je de host het verkeer daarvoor doorsturen en vertalen. Als een namespace al een VPN-interface heeft en je alleen een daarin gestart commando nodig hebt, is dat het gewone ip netns exec geval en is het meeste routeringswerk al gedaan.
Geen van beide vormen geeft je een regel die tot één proces-id beperkt is. Netfilter matcht adressen, poorten, markeringen en interfaces; er is geen routeringstabel per proces. Markering kan verkeer sturen, maar de markering moet op pakketten worden gezet die je al hebt geïdentificeerd, en dat is hetzelfde probleem een laag lager. Een namespace scheidt de hele stack voor die procesboom, en daarom is dat het gebruikelijke antwoord.
Als de eis echt is dat er niets de tunnel verlaat, stop dan de tunnel. De namespace bestaat voor het gemengde geval: één programma naar buiten, al het andere naar binnen, op dezelfde machine, op hetzelfde moment.
Bouw de tweede context en verbind die met de host
Een illustratieve opzet: de host heeft een WireGuard-interface die de standaardroute draagt, en een netwerknamespace genaamd clearnet die via een veth-paar met de host is verbonden. De adressen hieronder zijn voorbeelden.
ip netns add clearnet
ip link add veth-clear-out type veth peer name veth-clear-in
ip addr add 192.168.100.1/24 dev veth-clear-out
ip link set veth-clear-out up
ip link set veth-clear-in netns clearnet
ip netns exec clearnet ip addr add 192.168.100.2/24 dev veth-clear-in
ip netns exec clearnet ip link set veth-clear-in up
ip netns exec clearnet ip route add default via 192.168.100.1
Die namespace kan nu de host bereiken en niets anders. De host moet het verkeer doorsturen en het bronadres herschrijven, want 192.168.100.0/24 is privé en de upstream-router heeft geen route terug erin:
sysctl -w net.ipv4.ip_forward=1
iptables -t nat -A POSTROUTING -o <wan-interface> -j MASQUERADE
Bevestig de namespace-kant voordat je verder gaat.ip netns exec clearnet ip route zou de veth-route en de standaardroute via 192.168.100.1 moeten tonen, en een ping naar dat adres zou moeten antwoorden. Als de ping naar het hostadres mislukt, zijn de adressen of de linkstatus verkeerd en geen enkele firewallregel helpt.
Als namen wel resolven maar er niets terugkomt, lees dan FORWARD
De klassieke fout ziet eruit als een routeringsbug: de namespace resolvet een naam, het pakket gaat naar buiten en er komt nooit een antwoord. Op hosts met een FORWARD-beleid van DROP — dat doen verschillende distributies en de meeste gehardede images — wordt het pakket onderweg gedropt, en de fout die de toepassing afdrukt zegt niets over filtering.
Lees de keten voordat je iets toevoegt:
iptables -S FORWARD
nft list chain inet filter forward
Laat het verkeer dan in beide richtingen door, met tellers, zodat je kunt zien of de regel ooit heeft gematcht:
iptables -A FORWARD -i veth-clear-out -o <wan-interface> -j ACCEPT
iptables -A FORWARD -i <wan-interface> -o veth-clear-out -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
watch -n1 'iptables -L FORWARD -v -n'
Een regel met een pakketteller van nul staat niet in het pad. Of de interfacenamen wijken af van wat je aannam, of een eerdere regel in de keten heeft het pakket eerst geaccepteerd, gedropt of teruggegeven. Repareer de volgorde, niet de regeltekst.
Antwoordverkeer is de helft die mensen vergeten. De retourregel heeft dezelfde veth-naam nodig plus een match op verbindingsstatus, of een gewone interface-acceptatie als je het beleid open houdt. Voeg alleen de uitgaande regel toe en het oorspronkelijke symptoom is terug, terwijl je nieuwe regel daar staat en er correct uitziet.
Houd de markeringsregels van de tunnel buiten het nieuwe pad
Deze stap maakt van een werkende namespace een lekkende. WireGuard-installaties zetten meestal een firewallmarkeringsbeleid neer plus hun eigen routeringsregels en een aparte tabel, zodat ongemarkeerde pakketten terugvallen op de hoofdtabel terwijl gemarkeerde pakketten de tunneltabel gebruiken. Doorgestuurd verkeer uit de namespace wordt bij het naar buiten gaan tegen policy-routing beoordeeld en kan, afhankelijk van hoe die regels zijn geschreven, in de tunneltabel belanden.
Twee toevoegingen scheiden de paden. Keer vroeg terug in de mangle-keten voor het subnet van de namespace, en voeg een op de bron gebaseerde regel toe die dat subnet in de hoofdtabel zet:
iptables -t mangle -I PREROUTING -s 192.168.100.0/24 -j RETURN
ip rule add from 192.168.100.0/24 lookup main priority 100
Prioriteit 100 sorteert op mijn host voor de tunnelregels, dus de gewone tabel beslist voor die bron. Kopieer het nummer niet blind: de prioriteiten die wg-quick installeert, verschillen tussen versies en tussen de IPv4- en IPv6-opstellingen, dus voer ip rule show uit en kies een prioriteit die op jouw machine voor de tunnelregels valt.
Verifieer met een bronspecifieke zoekopdracht in plaats van de regellijst te vertrouwen:
ip route get 1.1.1.1 from 192.168.100.2
ip netns exec clearnet ip route get 1.1.1.1
Als de eerste opdracht de WireGuard-interface meldt, staat de nieuwe regel nog niet voor het tunnelbeleid. Als hij de WAN-interface meldt terwijl de tweede nog de veth-standaardroute toont, zijn de host en de namespace het oneens en moet je beide routeringstabellen opnieuw lezen.
Start het proces dat de verbinding opent
Het commando starten is één regel, maar start het proces dat de socket echt aanmaakt.ip netns exec clearnet curl -s https://ifconfig.me is de kleinste eerlijke test: het toont het adres dat de namespace aan de wereld presenteert, niet het adres dat de host zou gebruiken.
Voor een dienst: koppel de namespace via het pad, zodat het proces er zijn hele leven in blijft:
[Service]
NetworkNamespacePath=/run/netns/clearnet
Grafische toepassingen zijn waar de één-regel-aanpak stil faalt. Een browser die vanuit een menu start, geeft de aanvraag vaak door aan een instantie die al buiten de namespace draait, dus het nieuwe venster verschijnt en het verkeer gaat nog steeds via de tunnel naar buiten. Geef hem een apart profiel zodat er echt een nieuwe instantie wordt gemaakt, en controleer daarna de procescontext in plaats van te gokken:
readlink /proc/$(pgrep -f clearnet-profile | head -1)/ns/net
readlink /run/netns/clearnet
De twee uitvoerwaarden moeten overeenkomen. Dezelfde controle werkt voor elk langdurig programma, en het is de moeite waard om die één keer per toepassing te doen in plaats van aan te nemen dat de launcher deed wat je bedoelde.
Naamresolutie vereist een eigen beslissing. Een namespace aanmaken geeft hem geen resolverconfiguratie; bestanden per namespace worden ondersteund via /etc/netns/<name>/resolv.conf en zonder een daarvan erft u gedrag dat makkelijk verkeerd te lezen is. IPv6 vereist dezelfde behandeling: als de namespace geen v6-standaardroute heeft terwijl de toepassing v6 verkiest, kunnen aanvragen vastlopen voordat ze terugvallen, wat op een kapotte tunnel lijkt.
Controleer de inkadering, niet alleen het uitgangsadres
Een werkend openbaar adres bewijst de route, niet de hele eis. Doorloop de rest in deze volgorde:
ip netns exec clearnet ip route get 1.1.1.1toont de veth-standaardroute, niet de WireGuard-interface.- De FORWARD-tellers op beide regels lopen op terwijl een aanvraag draait.
- Het toepassingsproces meldt de namespace-inode die je verwacht.
- Na een herstart bestaat de namespace voordat de dienst start; een unit die eerder draait mislukt of, erger, draait buiten de namespace.
Beslis daarna wat er moet gebeuren als de tunnel valt. De namespace blijft werken, wat het punt van de oefening is en ook het risico: het vrijgestelde programma blijft verbonden terwijl al het andere stopt. Leg dat vast als een beleidskeuze en wees duidelijk over wat de vrijstelling niet dekt — accountidentiteit, opgeslagen browsertoestand en je DNS-provider staan allemaal buiten het bereik van een netwerknamespace, en de vrijstelling als anonimiteit behandelen is de fout die de configuratie overleeft.