Het verzoek is altijd een variant van dezelfde zin: houd de VPN voor het browsen, maar laat SSH of een back-uptaak de normale verbinding gebruiken. Het advies dat hierover rondgaat, de poort doorsturen met iptables NAT-regels, doet dat niet. Een NAT-regel herschrijft adressen en poorten binnen een stroom; hij kiest nooit welke interface het pakket verlaat. Die beslissing hoort bij de routeringsopzoeking, en die opzoeking heeft hulp nodig om een poort te zien.

Waarom een gewone route geen poort kan matchen

Linux kiest een route door het bestemmingsadres in een tabel op te zoeken, nadat kandidaattabellen via beleidsregels zijn gefilterd. Die regels kunnen een firewallmarkering, een bronadres, een inkomende interface of een UID matchen. Een route-item zelf heeft geen poortveld, want de route wordt gekozen voordat de transportheader ertoe doet.

Huidige iproute2 en kernels verruimen de regelselector: ipproto, sport en dport worden geaccepteerd, dus ip rule add ipproto tcp dport 22 table 100 drukt de bedoeling in één regel uit. Voer ip rule add help uit op de echte host om te zien of je build dport vermeldt. Als dat zo is, ben je klaar met één commando; zo niet, dan is het markeringspad hieronder het equivalent.

Hoe dan ook is de regel slechts een aanwijzer. Tabel 100 heeft nog steeds een route nodig, en dat is het stuk dat mensen vergeten.

Drie stukken, of één

De moderne vorm is de ene regel hierboven plus een route in de alternatieve tabel. De klassieke vorm voegt netfilter in het midden toe: markeer het pakket en match dan de markering in een regel. Beide eindigen bij dezelfde opzoeking; ze verschillen in waar de poort wordt gelezen.

De markeringsaanpak bestaat omdat netfilter de transportheader ziet en de routeringscode dat historisch niet deed. Dat is het hele mechanisme, en het verklaart waarom de terugvaloptie drie samenwerkende stukken is in plaats van één slim commando.

De drie stukken in volgorde

Markeer eerst de stromen die je wilt omleiden, in een chain die vroeg genoeg draait zodat de markering bestaat vóór de routeringsbeslissing:

nft add table inet vpnexempt
nft 'add chain inet vpnexempt out { type route hook output priority mangle; }'
nft add rule inet vpnexempt out tcp dport 22 meta mark set 0x1

Stuur ten tweede gemarkeerde pakketten naar een aparte tabel:

ip rule add fwmark 0x1/0x1 table 100

Geef ten derde die tabel een standaardroute via de fysieke link. De adressen zijn illustratief; vervang ze door je eigen:

ip route add default via 192.168.1.1 dev wlan0 table 100

Sla de derde stap over en het gemarkeerde pakket vindt geen route in tabel 100, dus het faalt. Het symptoom lijkt erop dat de regel het netwerk heeft gebroken, terwijl hij in feite het enige pad verwijderde dat het pakket nog had.

Wat het na het eerste pakket blijft laten werken

Markeringen gelden voor uitgaande pakketten. Het antwoord komt binnen op de fysieke interface met het adres dat die link toegewezen kreeg, en de hoofdtabel verkiest voor die bestemming mogelijk nog steeds de tunnel. Twee mechanismen houden het antwoordpad consistent: een op bron gebaseerde regel voor het linkadres, en connection tracking die de beslissing voor de duur van de stroom behoudt. De markering in conntrack opslaan en bij het antwoord herstellen is de gebruikelijke manier om een stateful uitzondering stabiel te maken.

Reverse-path filtering is de andere stille moordenaar. Als de kernel het retourpad strikt valideert en het antwoord binnenkomt op een interface die de hoofdtabel niet zou kiezen, wordt het pakket zonder melding gedropt. rp_filter standaardwaarden verschillen per distributie en per of de interface wordt doorgestuurd, dus lees de effectieve waarde in plaats van aan te nemen dat die uit staat.

Markeer het eigen transport van de tunnel niet. Een WireGuard- of UDP OpenVPN-tunnel is geen TCP 22, dus het recept hierboven is veilig, maar een uitzondering geschreven als alles behalve de tunnelbestemming kan de versleutelde pakketten vangen en de tunnel breken op een manier die als een routeringslus leest.

Controleer het in plaats van aan te nemen

Vraag de kernel welk pad hij zou nemen, voor de bestemming en voor de poort, en voer dan de echte overdracht uit:

ip route get 203.0.113.10
ip rule show
nft list ruleset

Als de markering bestaat maar de tellers nooit bewegen, matcht de regel niet. Tellers zijn het verschil tussen een configuratie die je hebt geschreven en een beleid dat de kernel toepast.

Wanneer dit het verkeerde gereedschap is

Poortgebaseerde uitzonderingen zijn van nature broos. Diensten verplaatsen poorten, HTTP/3 komt binnen als UDP 443, en één applicatie kan meerdere stromen van verschillende soorten openen. Als de echte eis is dat dit programma de normale link gebruikt, is de routeringslaag de verkeerde plek; een netwerknamespace of een proxy per applicatie past beter.

Als de eis is dat dit subnet de normale link gebruikt, sla de markeringen dan helemaal over en voeg een specifiekere route toe. Het is eenvoudiger, het is zichtbaar in één tabel, en de volgende persoon kan het auditen zonder een ruleset te lezen.

Schrijf op welke van de drie eisen je werkelijk hebt voordat je de firewall aanraakt. Het mechanisme hierboven is precies, en precisie toegepast op de verkeerde laag is gewoon een langere storing.

Gerelateerde artikelen