Poate un script cârlig OpenVPN să folosească tunelul pe care tocmai l-a construit?
OpenVPN rulează cârligele în interiorul propriei porniri, așa că un script care are nevoie de tunel așteaptă un tunel care îl așteaptă. Detașați munca și alegeți cârligul după eveniment.
Dacă un script cârlig ajunge pe cealaltă parte a tunelului depinde de trei condiții: de ce eveniment este legat, dacă OpenVPN se află încă în secvența sa de pornire când rulează scriptul și dacă munca a fost detașată de acea secvență. Majoritatea scripturilor eșuează la a treia. Adăugarea unei pauze tratează o problemă de ciclu de viață ca pe o problemă de sincronizare, și funcționează exact atât de des încât să inducă în eroare.
Decideți mai întâi evenimentul, apoi mecanismul de amânare a muncii, apoi locul unde rulează munca amânată. Aceste trei decizii sunt independente, iar amestecarea lor este felul în care un cârlig de două linii devine o după-amiază întreagă.
De ce cârligul așteaptă un tunel care îl așteaptă
OpenVPN își apelează cârligele sincron, în interiorul aceluiași proces care mută pachetele. up se declanșează când dispozitivul tun sau tap există, dar înainte de a fi adăugată vreo rută, iar route-up se declanșează după rute; niciunul dintre aceste momente nu promite că partea cealaltă va răspunde. Cât timp un script al oricărui cârlig încă rulează, demonul nu redirecționează, așa că o comandă care așteaptă ca traficul să traverseze tunelul așteaptă un tunel care așteaptă comanda. Această așteptare circulară este motivul pentru care o rulare manuală trece, iar cârligul ajunge la expirare.
Petecul evident, să dormi câteva secunde, funcționează uneori pentru că extremitatea îndepărtată s-a ridicat singură, nu pentru că pauza a reparat ceva. Un ajutor care întreabă despre disponibilitate este alt răspuns: cârligul revine, redirecționarea începe, iar ajutorul primește ce are nevoie când chiar există.
Alegeți cârligul după eveniment, nu după comoditate
Atașați up la munca ce are nevoie doar de dispozitiv, route-up la munca ce are nevoie de rute, și păstrați client-connect și client-disconnect pentru sarcini pe client de partea serverului. Curățarea la deconectare trebuie să suporte să ruleze de două ori, deoarece reconectările printr-o interfață persistentă nu urmează aceeași secvență ca o repornire completă. Pe server, client-connect se declanșează când clientul se autentifică, nu când pornește demonul, ceea ce merită confirmat înainte de a muta logica acolo. Dacă munca aparține ciclului de viață al serviciului și nu unei singure conexiuni, o unitate systemd este o casă mai bună decât orice cârlig.
Comenzile cârligului sunt executate direct de demon, fără shell între ele, așa că ifconfig_remote într-o linie up sosește ca text literal. Înfășurați comanda într-un shell explicit sau păstrați logica într-un script care citește mediul. ipchange este o directivă care adaugă propriile argumente: transmite comenzii adresa și portul, așa că un script scris pentru niciunul vede două neașteptate.
Trei moduri de a detașa munca
Prima opțiune este să trimiteți ajutorul în fundal din interiorul cârligului: porniți-l cu setsid sau systemd-run --scope, închideți descriptorii moșteniți și ieșiți imediat cu zero. A doua este să lăsați cârligul să scrie un mic fișier de stare și un timer systemd sau o unitate de cale să acționeze asupra lui, ceea ce ține cârligul trivial și pune politica de reîncercare acolo unde există jurnale. A treia este să renunțați complet la cârlig și să mutați munca într-o unitate ordonată după serviciul VPN, care este răspunsul corect când munca nu este deloc per conexiune. Toate trei împărtășesc o regulă: cârligul trebuie să revină înainte de a avea nevoie de tunel.
Orice ați alege, faceți ajutorul idempotent și observabil. Două invocări suprapuse nu ar trebui să execute aceeași acțiune ireversibilă de două ori, iar un ajutor care eșuează trebuie să lase ceva ce un om poate citi.
Separați eșecurile de sandbox de eșecurile ciclului de viață
Când un script rulează dintr-un prompt root și eșuează ca serviciu, cauza este de obicei un sandbox, nu rețeaua. Un ProtectHome restrictiv, un ProtectSystem doar-citire sau un director de lucru care nu există pot face scriptul sau datele lui inaccesibile, iar pe un gazdă SELinux fișierul are nevoie și de un context care permite execuția. Citiți unitatea cu systemctl cat openvpn-server@server și jurnalul cu journalctl -u openvpn-server@server -n 80, apoi căutați o eroare de permisiune sau de context în loc de un timeout de conexiune. Dezactivarea sandbox-ului ar trebui să fie ultima mișcare, nu prima.
Un eșec al ciclului de viață arată altfel: cârligul pornește, ajutorul rulează, iar socketul pur și simplu nu este încă acolo. Scrieți o linie cu marcă de timp din cârligul însuși, deconectați și reconectați, și citiți evenimentele în ordine. Când ajutorul chiar are nevoie de tunel, lăsați-l să întrebe despre disponibilitate în loc să doarmă o perioadă fixă.
Trei câmpuri fac raportul următor util: ce cârlig a rulat, ce cont a folosit și unde s-a oprit. Fără ele, fiecare investigație ulterioară începe de la zero.