Quellen-Routing für zwei VPN-Pools: was deine Regel übersieht
Eine Regel der Form from passt auf die Client-Adresse, bevor SNAT sie umschreibt, und ein Test vom Gateway passt auf keines von beiden. Führe das Lookup pro Pool aus und lies stattdessen die Prioritäten.
Zwei Client-Pools, ein Gateway, und der Plan wirkt trivial: Eine Regel schickt den ersten Pool über den schnellen Uplink, eine andere den zweiten Pool durch einen Partnertunnel. Dann stehen die Regeln, und der zweite Pool verlässt das Netz weiterhin über die Standardroute. Die Regel ist nicht kaputt. Die Quelle des Pakets ist nicht die, die der Autor im Moment des Lookups erwartet, weil Übersetzung und Lookup an verschiedenen Punkten derselben Paketreise stattfinden, und die Reihenfolge ist das Gegenteil des mentalen Diagramms, das die meisten zeichnen.
Das Lookup passiert, bevor die Quelle umgeschrieben wird
Für ein Paket, das von einem VPN-Client weitergeleitet wird, führt der Kernel eine feste Reihenfolge aus. Es kommt auf dem Tunnelinterface an, durchläuft prerouting, wo conntrack und DNAT leben, dann wird die Routingentscheidung getroffen, dann die Weiterleitung, dann postrouting, wo SNAT zuletzt angewendet wird. Eine Regel der Form from wird während dieser Routingentscheidung herangezogen, sieht also die ursprüngliche Adresse des Clients, nicht die übersetzte.
Eine masquerade-Regel hinzuzufügen ändert also nicht, welche Policy auf den ausgehenden Fluss passt. Sie ändert, was die Gegenseite sieht, und damit, welcher Rückweg existiert. Das sind zwei verschiedene Folgen, und Change-Reviews verwechseln sie ständig. Trennt man sie, wird das Design meist innerhalb einer Minute offensichtlich.
In der Antwortrichtung taucht die übersetzte Adresse in den Regeln auf. Ein Paket, das zum Gateway zurückkommt, ist an die übersetzte Adresse adressiert, und wenn du Policy-Regeln auf das Ziel oder auf das Interface hast, über das es hereinkam, ist das der Wert, den du matchst. Dieselbe Verbindung, in jeder Richtung eine andere Quelle der Wahrheit.
| Stage | Source the kernel sees | What it means for your rule |
|---|---|---|
| Prerouting | Client address, 172.31.7.42 | Where marks and DNAT are applied |
| Routing decision | Still 172.31.7.42 | This is where from rules match |
| Postrouting | Becomes 198.51.100.9 | What the far end and its return route see |
Zwei Pools mit unterschiedlichen Quellen brauchen also tatsächlich unterschiedliche Tabellen, und das Design ist solide. Was die Tabelle oben ausschließt, ist die Abkürzung, zu debuggen, indem man schaut, was die Gegenseite empfangen hat, und daraus die Regel ableitet.
Ein Test vom Gateway ist kein Test des Pools
Verkehr, den das Gateway selbst erzeugt, durchläuft nie prerouting, weil er nicht auf einem Interface angekommen ist. Er wird im Output-Pfad geroutet, mit der eigenen Adresse des Gateways als Quelle, und eine from-Regel, die auf einen Client-Bereich zeigt, passt nicht. Jedes Ticket in diesem Bereich, das sagt, es funktioniere von der Box, aber nicht vom Client, ist genau diese Tatsache in anderen Worten.
Simuliere den Client, statt ihn zu approximieren. Das Routing-Lookup lässt sich direkt abfragen, mit einer Quelle und einem Eingangsinterface:
ip route get 198.51.100.9 from 172.31.7.42 iif tun0
ip route get 198.51.100.9 from 172.31.8.42 iif tun0
ip route show table 201
Führe es einmal pro Pool aus. Wenn beide Befehle dasselbe Interface zurückgeben, trennen die Regeln die Pools nicht, und der Grund ist entweder eine fehlende Regel oder eine Regel mit höherer Priorität, die auf beide passt. Wenn sie unterschiedliche Interfaces zurückgeben, der Client aber weiterhin scheitert, liegt der Fehler hinter dem Lookup, und du kannst aufhören, Regeln anzusehen.
Priorität, nicht Position in der Datei
Regeln werden in aufsteigender Prioritätsreihenfolge ausgewertet, und die Zahl zählt, nicht die Reihenfolge, in der du sie getippt hast. Die local-Tabelle liegt auf Priorität 0, die main-Tabelle auf 32766 und die Standardregel auf 32767, deshalb landet eine mit Standardpriorität hinzugefügte Regel nahe dem Ende der Liste und verliert gegen fast alles.
ip rule show
ip rule add from 172.31.8.0/24 lookup 201 priority 1000
ip -4 route show table 201
Eine explizite Priorität macht die beabsichtigte Reihenfolge in der Datei und im Review sichtbar. Sie verhindert auch, dass eine spätere Ergänzung stillschweigend vor eine bestehende Regel springt, was die häufigste Ursache dafür ist, dass eine funktionierende Policy nach einer unabhängigen Änderung nicht mehr funktioniert.
Eine passende Regel, die auf eine unvollständige Tabelle zeigt, ist die andere Falle. Enthält Tabelle 201 eine Standardroute, aber keine Route zum Ziel, wird das Paket verworfen oder an die falsche Stelle geschickt, und die Regel wirkt isoliert vollkommen korrekt. Lies die Tabelle, nicht nur die Regel. Eine Regel ist ein Zeiger, und eine leere Tabelle ist ein Zeiger ins Nichts.
Der Rückweg muss trotzdem existieren
Quell-Policy wählt einen Ausgang. Sie erzeugt keinen Rückweg. Verlässt der zweite Pool das Netz durch einen Partnertunnel, muss der Partner wissen, wie er 172.31.8.0/24 erreicht, entweder über eine Route zurück zum Gateway oder indem er übersetzte Adressen erhält, die er bereits kennt.
Die Wahl dazwischen ist eine Designentscheidung mit sichtbaren Folgen. Den Client-Bereich zurück zu routen bewahrt die Identität jedes Clients beim Partner und verlangt, dass er dein Präfix akzeptiert. Übersetzung verbirgt den Pool hinter einer Adresse und verlangt, dass der Partner nur diese Adresse akzeptiert, zum Preis, dass deine und seine Logs sich nicht mehr darüber einig sind, wer verbunden hat.
Was du auch wählst, schreib es neben die Pool-Definition. Eine Rückroute, die nur in jemandes Gedächtnis existiert, ist genau die Art Ding, die überlebt, bis der Partner eine Firewall ändert, und dann einen Tag zur Rekonstruktion kostet.
Fang den eigenen Tunnel des Gateways nicht in der Policy
Die Regeln, die du für Clients schreibst, gelten auch für Verkehr, den das Gateway erzeugt, es sei denn, du schließt ihn aus. Eine breite Regel auf einer Markierung oder auf dem Output-Interface kann die eigene Verbindung des Gateways zum Partner einfangen, und dann wird der Tunnel, der die Policy trägt, von der Policy geroutet. Das erzeugt eine Schleife oder einen Ausfall, der wie ein Providerfehler aussieht.
Fasse jede Regel so eng, wie das Design erlaubt: Quellpräfix, Eingangsinterface und, wo unterstützt, eine Markierung, die nur Client-Pakete tragen. Teste danach die eigene Konnektivität des Gateways nach jeder Regeländerung, denn der Fehler ist selbstreferenziell und zeigt sich in einem Client-Test nicht.
Die Manpage zu ip-rule dokumentiert die Semantik von Selektor und Priorität. Kombiniere sie mit einem Lookup-Test pro Pool, und das Design wird überprüfbar, was der Unterschied zwischen Policy-Routing ist, das ein Jahr überlebt, und solchem, das jeder anzufassen fürchtet.