Vrstvení druhého klienta OpenVPN na jeden hostitel Linux selhává na jednom ze dvou míst. Buď se vnitřní klient nedostane na svůj server, protože vnější tunel zachytil tento transport, nebo druhý klient přepíše směrovací tabulku a první tunel přestane fungovat. Oba problémy jsou ve směrování, ne v šifrování, a oba se řeší dřív, než nový tunel ponese jakýkoli aplikační provoz. Adresy jsou zde ilustrativní: tun0 je vnější tunel, 203.0.113.9 je vnitřní server a TCP 9999 je tok, který musí jít vnitřní cestou.

Dva režimy selhání, jedna směrovací tabulka

Vnitřní klient otevře UDP socket na svůj server a tento socket se řídí aktuální směrovací tabulkou jako každý jiný. Pokud vnější tunel už vlastní výchozí trasu, pakety handshake jdou do vnějšího tunelu. To funguje jen tehdy, když se vnější server dostane na vnitřní server a druhá strana souhlasí s nesení toku; jinak handshake uvízne a klient to zkouší znovu.

Druhé selhání přichází z push od serveru. Obvyklý push je redirect-gateway def1, který instaluje 0.0.0.0/1 a 128.0.0.0/1 místo nahrazení výchozí trasy. Dva klienti, z nichž každý instaluje svou vlastní dvojici /1, se proto střetnou a nejnovější dvojice vyhraje pro téměř každé cílové umístění. Hledejte tyto dva prefixy v tabulce, když tunel po připojení druhého umře, protože samotná výchozí trasa se možná nikdy nezměnila.

Označte tok a dejte mu tabulku

Nasměrujte vybraný provoz tak, že jej označíte v tabulce mangle a označení porovnáte v pravidle zásady. Pravidlo ukazuje na vyhrazenou tabulku, jejíž výchozí trasou je vnitřní zařízení, takže vnitřní cestou jdou jen označené pakety.

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

Většina selhání v této sadě pravidel pochází ze dvou chyb. Hodnota značky, kterou nastaví firewall, a hodnota, kterou porovnává pravidlo, musí být shodné, protože neshoda nevyvolá chybu ani účinek. A PREROUTING vidí jen pakety přicházející zvenčí, takže spojení zahájené na tomto hostiteli potřebuje pravidlo OUTPUT se stejným porovnáním, než bude vůbec označeno.

Seberte vlastnictví tras druhému klientovi

Dvě volby brání druhému klientovi upravovat tabulku a působí v různých bodech. route-nopull ignoruje předané volby tras, takže se nic nenainstaluje. route-noexec přijme předané volby do prostředí klienta, ale změnu tabulky nechá na hooku, což je přesně to, co chcete, když potřebujete vidět, co server nabídl.

Hook běží s předanými hodnotami ve svém prostředí a měl by přidat jen položky, které jste schválili. Udělejte jej idempotentní: před přestavbou vyprázdněte tabulku, kterou spravuje, a nepředpokládejte, že předchozí běh zanechal čistý stav. Opětovné připojení hook vyvolá znovu a hook, který přidává bez čištění, nahromadí duplicitní trasy.

Vyberte jednoho vlastníka a zapište ho. Profil, který nastavuje obě volby, nebo hook, který soupeří s ručním skriptem, vytvoří tabulku, kterou později nikdo nevysvětlí.

Přišpendlete obě adresy serverů

Vnitřní server potřebuje trasu k hostiteli přes vnější zařízení dřív, než se vnitřní klient připojí. Právě tato položka drží vnitřní transport uvnitř vnějšího tunelu místo toho, aby nechala jej následovat výchozí trasu.

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

Přidejte odpovídající trasu k hostiteli i pro vnější server do hlavní tabulky, jinak může opětovné připojení poslat vlastní transport vnějšího klienta do jeho vlastního tunelu. Pořadí není volitelné: tyto položky přidávejte až poté, co příslušné rozhraní existuje, tedy hookem nebo skriptem, který běží po připojení. Trasa ukazující na vypnuté zařízení se odstraní nebo ignoruje, takže položka zapsaná při startu nepřežije sled, který jste předpokládali.

Kontrolujte vyhledání místo čtení celé tabulky. Výstup vyhledání trasy uvádí zařízení a zdrojovou adresu, které jádro použije, což je přesně to, co klient dostane, když otevře svůj socket.

Otestujte řetěz a zviditelněte selhání

Zachytávejte na vnějším rozhraní, když se vnitřní klient připojuje. Vnitřní handshake by se měl objevit jako zašifrovaná zátěž uvnitř vnějšího tunelu; pokud se objeví jako obyčejný UDP provoz na 203.0.113.9, trasa k hostiteli chybí a řetěz nebyl nikdy postaven.

Řetěz, který tiše spadne zpět na fyzický uplink, je nejhorší výsledek, protože stav rozhraní nic neoznamuje a vnitřní provoz odchází ze špatné zdrojové adresy. Pokud se druhý klient může přímo znovu připojit, když vnější tunel spadne, tuto cestu zakažte nebo ji přivažte k vnějšímu rozhraní, aby bylo selhání vidět.

Skončete dvěma toky a jedním výpisem pravidel: označené a neoznačené spojení mají jít různými cestami a ip rule show má vypsat prioritu, kterou jste zamýšleli. Pokud oba toky jdou stejnou cestou, značka se neuplatňuje tam, kde si myslíte.

Související články