Die Anfrage ist immer eine Variante desselben Satzes: das VPN zum Surfen behalten, aber SSH oder einen Backup-Job die normale Verbindung nutzen lassen. Der Rat, der dazu kursiert, den Port mit iptables-NAT-Regeln weiterzuleiten, leistet das nicht. Eine NAT-Regel schreibt Adressen und Ports innerhalb eines Flusses um; sie wählt nie, über welche Schnittstelle das Paket hinausgeht. Diese Entscheidung gehört zum Routing-Lookup, und der Routing-Lookup braucht Hilfe, um einen Port zu sehen.

Warum eine einfache Route keinen Port treffen kann

Linux wählt eine Route, indem es die Zieladresse in einer Tabelle nachschlägt, nachdem die Kandidatentabellen durch Policy-Regeln gefiltert wurden. Diese Regeln können auf eine Firewall-Markierung, eine Quelladresse, eine eingehende Schnittstelle oder eine UID passen. Der Routeneintrag selbst hat kein Portfeld, weil die Route gewählt wird, bevor der Transport-Header zählt.

Aktuelle iproute2 und Kernel erweitern den Selektor der Regel: ipproto, sport und dport werden akzeptiert, sodass ip rule add ipproto tcp dport 22 table 100 die Absicht in einer Zeile ausdrückt. Führen Sie ip rule add help auf dem echten Host aus, um zu sehen, ob Ihr Build dport auflistet. Wenn ja, sind Sie mit einem Befehl fertig; wenn nicht, ist der Markierungsweg unten das Äquivalent.

So oder so ist die Regel nur ein Zeiger. Tabelle 100 braucht trotzdem eine Route, und genau diesen Teil vergessen die Leute.

Drei Teile oder einer

Die moderne Form ist die einzelne Regel oben plus eine Route in der alternativen Tabelle. Die klassische Form schiebt netfilter dazwischen: Paket markieren, dann die Markierung in einer Regel treffen. Beide enden beim selben Lookup; sie unterscheiden sich darin, wo der Port gelesen wird.

Der Markierungsansatz existiert, weil netfilter den Transport-Header sieht und der Routing-Code ihn historisch nicht sah. Das ist der ganze Mechanismus, und es erklärt, warum der Rückfallweg drei zusammenarbeitende Teile sind und nicht ein kluger Befehl.

Die drei Teile der Reihe nach

Zuerst die Flüsse markieren, die umgeleitet werden sollen, in einer Kette, die früh genug läuft, damit die Markierung vor der Routenentscheidung existiert:

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

Zweitens markierte Pakete in eine eigene Tabelle schicken:

ip rule add fwmark 0x1/0x1 table 100

Drittens dieser Tabelle eine Standardroute über den physischen Link geben. Die Adressen sind beispielhaft; setzen Sie Ihre eigenen ein:

ip route add default via 192.168.1.1 dev wlan0 table 100

Lassen Sie den dritten Schritt weg, findet das markierte Paket keine Route in Tabelle 100 und scheitert. Das Symptom sieht aus, als hätte die Regel das Netzwerk kaputt gemacht, während sie in Wahrheit den einzigen Weg entfernt hat, der dem Paket noch blieb.

Was es nach dem ersten Paket am Laufen hält

Markierungen gelten für ausgehende Pakete. Die Antwort kommt auf der physischen Schnittstelle mit der Adresse an, die dieser Link zugewiesen hat, und die Haupttabelle bevorzugt für dieses Ziel möglicherweise weiterhin den Tunnel. Zwei Mechanismen halten den Antwortweg konsistent: eine quellenbasierte Regel für die Link-Adresse und Connection Tracking, das die Entscheidung für die Lebensdauer des Flusses bewahrt. Die Markierung in conntrack zu speichern und bei der Antwort wiederherzustellen ist der übliche Weg, eine zustandsbehaftete Ausnahme stabil zu machen.

Reverse-Path-Filterung ist der andere stille Killer. Wenn der Kernel den Rückweg streng prüft und die Antwort auf einer Schnittstelle ankommt, die die Haupttabelle nicht wählen würde, wird das Paket ohne Meldung verworfen. rp_filter Standardwerte unterscheiden sich je nach Distribution und danach, ob die Schnittstelle weitergeleitet wird, also lesen Sie den effektiven Wert, statt anzunehmen, er sei aus.

Markieren Sie nicht den Transport des Tunnels selbst. Ein WireGuard- oder UDP-OpenVPN-Tunnel ist kein TCP 22, daher ist das Rezept oben sicher, aber eine Ausnahme, die als alles außer dem Tunnelziel geschrieben ist, kann die verschlüsselten Pakete einfangen und den Tunnel so brechen, dass es wie eine Routingschleife aussieht.

Prüfen statt annehmen

Fragen Sie den Kernel, welchen Weg er nehmen würde, für das Ziel und für den Port, und führen Sie dann die echte Übertragung aus:

ip route get 203.0.113.10
ip rule show
nft list ruleset

Wenn die Markierung existiert, die Zähler sich aber nie bewegen, trifft die Regel nicht. Zähler sind der Unterschied zwischen einer Konfiguration, die Sie geschrieben haben, und einer Policy, die der Kernel anwendet.

Wann das das falsche Werkzeug ist

Portbasierte Ausnahmen sind von Natur aus brüchig. Dienste wechseln Ports, HTTP/3 kommt als UDP 443, und eine Anwendung kann mehrere Flüsse verschiedener Art öffnen. Wenn die eigentliche Anforderung ist, dass dieses Programm den normalen Link nutzt, ist die Routing-Schicht der falsche Ort; ein Netzwerk-Namespace oder ein Proxy pro Anwendung passt besser.

Wenn die Anforderung ist, dass dieses Subnetz den normalen Link nutzt, lassen Sie die Markierungen ganz weg und fügen Sie eine spezifischere Route hinzu. Es ist einfacher, in einer Tabelle sichtbar, und die nächste Person kann es prüfen, ohne ein Regelset zu lesen.

Schreiben Sie auf, welche der drei Anforderungen Sie tatsächlich haben, bevor Sie die Firewall anfassen. Der Mechanismus oben ist präzise, und Präzision auf der falschen Schicht ist nur eine längere Ausfallzeit.

Ähnliche Artikel