Einen zweiten OpenVPN-Client auf einem Linux-Host zu stapeln, schlägt an einer von zwei Stellen fehl. Entweder erreicht der innere Client seinen eigenen Server nicht, weil der äußere Tunnel diesen Transport übernommen hat, oder der zweite Client schreibt die Routentabelle um, und der erste Tunnel funktioniert nicht mehr. Beide Probleme liegen im Routing, nicht in der Verschlüsselung, und beide werden gelöst, bevor der neue Tunnel Anwendungsverkehr trägt. Die Adressen hier sind illustrativ: tun0 ist der äußere Tunnel, 203.0.113.9 ist der innere Server und TCP 9999 ist der Flow, der den inneren Pfad nehmen muss.

Zwei Fehlerbilder, eine Routentabelle

Ein innerer Client öffnet einen UDP-Socket zu seinem Server, und dieser Socket folgt der aktuellen Routentabelle wie jeder andere. Gehört die Standardroute bereits dem äußeren Tunnel, laufen die Handshake-Pakete in den äußeren Tunnel. Das funktioniert nur, wenn der äußere Server den inneren Server erreichen kann und die Gegenseite den Flow zu tragen bereit ist; sonst bleibt der Handshake stehen, und der Client versucht es erneut.

Der zweite Fehler kommt vom Push des Servers. Der übliche Push ist redirect-gateway def1, der 0.0.0.0/1 und 128.0.0.0/1 installiert, statt die Standardroute zu ersetzen. Zwei Clients, die jeweils ihr eigenes /1-Paar installieren, kollidieren daher, und das neueste Paar gewinnt für fast jedes Ziel. Suchen Sie diese beiden Präfixe in der Tabelle, wenn ein Tunnel nach dem Verbinden des zweiten stirbt, denn die Standardroute selbst hat sich vielleicht nie geändert.

Flow markieren und ihm eine Tabelle geben

Lenken Sie den ausgewählten Verkehr, indem Sie ihn in der mangle-Tabelle markieren und die Markierung in einer Policy-Regel abgleichen. Die Regel zeigt auf eine eigene Tabelle, deren Standardroute das innere Gerät ist, sodass nur markierte Pakete den inneren Pfad nehmen.

iptables -t mangle -A PREROUTING -p tcp -m multiport --dport 9999 -j MARK --set-mark 0x9999
ip rule add fwmark 0x9999 table 200 pref 1000
ip route add default dev tun1 table 200

Die meisten Fehler in diesem Regelsatz stammen aus zwei Fehlern. Der von der Firewall gesetzte Markierungswert und der von der Regel abgeglichene Wert müssen identisch sein, denn eine Abweichung erzeugt weder Fehler noch Wirkung. Und PREROUTING sieht nur Pakete, die von außen ankommen, also braucht eine auf diesem Host gestartete Verbindung eine OUTPUT-Regel mit demselben Abgleich, bevor sie überhaupt markiert wird.

Die Routenhoheit vom zweiten Client nehmen

Zwei Optionen hindern den zweiten Client am Bearbeiten der Tabelle, und sie wirken an unterschiedlichen Stellen. route-nopull ignoriert die gepushten Routenoptionen, sodass nichts installiert wird. route-noexec übernimmt die gepushten Optionen in die Umgebung des Clients, überlässt die Tabellenänderung aber einem Hook, was genau das ist, was Sie wollen, wenn Sie sehen müssen, was der Server angeboten hat.

Der Hook läuft mit den gepushten Werten in seiner Umgebung und sollte nur die Einträge hinzufügen, die Sie freigegeben haben. Machen Sie ihn idempotent: Leeren Sie die Tabelle, die er besitzt, bevor Sie sie neu aufbauen, und nehmen Sie nicht an, dass der vorherige Lauf einen sauberen Zustand hinterlassen hat. Eine Wiederverbindung ruft den Hook erneut auf, und ein Hook, der ohne Leeren anhängt, sammelt doppelte Routen an.

Wählen Sie einen Eigentümer und schreiben Sie ihn auf. Ein Profil, das beide Optionen setzt, oder ein Hook, der mit einem manuellen Skript konkurriert, erzeugt eine Tabelle, die später niemand erklären kann.

Beide Serveradressen festnageln

Der innere Server braucht eine Host-Route über das äußere Gerät, bevor der innere Client sich verbindet. Dieser Eintrag hält den inneren Transport im äußeren Tunnel, statt ihn der Standardroute folgen zu lassen.

ip route add 203.0.113.9/32 dev tun0
ip route get 203.0.113.9

Fügen Sie auch eine passende Host-Route für den äußeren Server in die Haupttabelle ein, sonst kann eine Wiederverbindung den eigenen Transport des äußeren Clients in seinen eigenen Tunnel routen. Die Reihenfolge ist nicht optional: Fügen Sie diese Einträge hinzu, nachdem die betreffende Schnittstelle existiert, also über einen Hook oder ein Skript, das nach dem Verbinden läuft. Eine Route, die auf ein ausgefallenes Gerät zeigt, wird entfernt oder ignoriert, sodass ein beim Booten geschriebener Eintrag die von Ihnen angenommene Reihenfolge nicht übersteht.

Prüfen Sie die Suche, statt die ganze Tabelle zu lesen. Die Ausgabe der Routensuche nennt das Gerät und die Quelladresse, die der Kernel verwenden wird, und genau das bekommt der Client, wenn er seinen Socket öffnet.

Die Kette testen und den Fehler sichtbar machen

Zeichnen Sie an der äußeren Schnittstelle auf, während der innere Client sich verbindet. Der innere Handshake sollte als verschlüsselte Nutzlast im äußeren Tunnel erscheinen; erscheint er als einfaches UDP zu 203.0.113.9, fehlt die Host-Route, und die Kette wurde nie aufgebaut.

Eine Kette, die still auf den physischen Uplink zurückfällt, ist das schlechteste Ergebnis, weil der Schnittstellenzustand nichts ankündigt und der innere Verkehr von der falschen Quelladresse ausgeht. Kann der zweite Client sich direkt wiederverbinden, wenn der äußere Tunnel ausfällt, deaktivieren Sie diesen Weg oder binden Sie ihn an die äußere Schnittstelle, damit der Fehler sichtbar wird.

Schließen Sie mit zwei Flows und einem Regel-Dump ab: Eine markierte und eine unmarkierte Verbindung sollten verschiedene Wege nehmen, und ip rule show sollte die beabsichtigte Priorität auflisten. Nehmen beide Flows denselben Weg, wird die Markierung nicht dort angewendet, wo Sie denken.

Ähnliche Artikel