Înlănțuiește doi clienți OpenVPN pe un singur host Linux cu fwmark
Ambii clienți vor ruta implicită. Fixează serverul interior cu o rută de host, oprește al doilea client să rescrie tabelele, apoi dirijează fluxurile cu fwmark.
Adăugarea unui al doilea client OpenVPN pe un singur host Linux eșuează într-unul din două locuri. Fie clientul interior nu ajunge la propriul server pentru că tunelul exterior a capturat acel transport, fie al doilea client rescrie tabela de rute și primul tunel nu mai funcționează. Ambele probleme țin de rutare, nu de criptare, și ambele se rezolvă înainte ca tunelul nou să transporte orice trafic de aplicație. Adresele de aici sunt ilustrative: tun0 este tunelul exterior, 203.0.113.9 este serverul interior, iar TCP 9999 este fluxul care trebuie să folosească calea interioară.
Două moduri de eșec, o singură tabelă de rute
Un client interior deschide un socket UDP către serverul său, iar acel socket urmează tabela de rute curentă ca oricare altul. Dacă tunelul exterior deține deja ruta implicită, pachetele de handshake intră în tunelul exterior. Asta funcționează doar când serverul exterior poate ajunge la serverul interior și cealaltă parte acceptă să transporte fluxul; altfel handshake-ul se blochează și clientul reîncearcă.
Al doilea eșec vine din push-ul serverului. Push-ul obișnuit este redirect-gateway def1, care instalează 0.0.0.0/1 și 128.0.0.0/1 în loc să înlocuiască ruta implicită. Doi clienți care instalează fiecare propria pereche /1 se ciocnesc de aceea, iar cea mai nouă pereche câștigă pentru aproape orice destinație. Caută acele două prefixe în tabelă când un tunel moare după ce al doilea se conectează, pentru că ruta implicită în sine poate să nu se fi schimbat niciodată.
Marchează fluxul și dă-i o tabelă
Dirijează traficul selectat marcându-l în tabela mangle și potrivind marcajul într-o regulă de politică. Regula indică o tabelă dedicată a cărei rută implicită este dispozitivul interior, astfel încât doar pachetele marcate iau calea interioară.
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 200Majoritatea eșecurilor din acest set de reguli provin din două greșeli. Valoarea marcajului setată de firewall și valoarea potrivită de regulă trebuie să fie identice, pentru că o nepotrivire nu produce nici eroare, nici efect. Iar PREROUTING vede doar pachete care sosesc din exterior, așa că o conexiune pornită pe acest host are nevoie de o regulă OUTPUT cu aceeași potrivire înainte de a fi măcar marcată.
Ia proprietatea rutelor de la al doilea client
Două opțiuni împiedică al doilea client să editeze tabela și acționează în puncte diferite. route-nopull ignoră opțiunile de rută trimise prin push, deci nu se instalează nimic. route-noexec acceptă opțiunile trimise în mediul clientului, dar lasă schimbarea tabelei unui hook, și asta vrei când trebuie să vezi ce a oferit serverul.
Hook-ul rulează cu valorile trimise în mediul său și ar trebui să adauge doar intrările pe care le-ai aprobat. Fă-l idempotent: golește tabela pe care o deține înainte de a o reconstrui și nu presupune că rularea anterioară a lăsat o stare curată. O reconectare apelează din nou hook-ul, iar un hook care adaugă fără să golească acumulează rute duplicate.
Alege un singur proprietar și scrie-l. Un profil care setează ambele opțiuni, sau un hook care concurează cu un script manual, produce o tabelă pe care nimeni nu o poate explica mai târziu.
Fixează ambele adrese ale serverelor
Serverul interior are nevoie de o rută de host prin dispozitivul exterior înainte ca clientul interior să se conecteze. Acea intrare este cea care ține transportul interior în tunelul exterior în loc să-l lase să urmeze ruta implicită.
ip route add 203.0.113.9/32 dev tun0
ip route get 203.0.113.9Adaugă și o rută de host corespunzătoare pentru serverul exterior în tabela principală, altfel o reconectare poate direcționa transportul clientului exterior în propriul său tunel. Ordinea nu este opțională: adaugă aceste intrări după ce interfața relevantă există, adică un hook sau un script care rulează după conectare. O rută care indică un dispozitiv oprit este eliminată sau ignorată, așa că o intrare scrisă la pornire nu supraviețuiește secvenței pe care ai presupus-o.
Verifică căutarea în loc să citești toată tabela. Ieșirea căutării de rută numește dispozitivul și adresa sursă pe care le va folosi kernelul, și exact asta primește clientul când își deschide socketul.
Testează lanțul și fă eșecul vizibil
Captează pe interfața exterioară în timp ce clientul interior se conectează. Handshake-ul interior ar trebui să apară ca payload criptat în tunelul exterior; dacă apare ca UDP simplu către 203.0.113.9, lipsește ruta de host și lanțul nu a fost construit niciodată.
Un lanț care revine silențios la uplink-ul fizic este cel mai rău rezultat, pentru că starea interfeței nu anunță nimic și traficul interior pleacă de la adresa sursă greșită. Dacă al doilea client se poate reconecta direct când tunelul exterior cade, dezactivează acea cale sau leagă-o de interfața exterioară ca eșecul să fie vizibil.
Termină cu două fluxuri și un dump de reguli: o conexiune marcată și una nemarcată ar trebui să urmeze căi diferite, iar ip rule show ar trebui să listeze prioritatea pe care ai intenționat-o. Dacă ambele fluxuri urmează aceeași cale, marcajul nu se aplică unde crezi.