Când un script client-connect se termină cu un cod diferit de zero, OpenVPN respinge clientul, iar operatorii presupun de obicei că scriptul client-disconnect corespunzător face ordine. Adesea nu rulează niciodată, pentru că procesarea disconnect depinde de ce a reușit să finalizeze etapa connect. Tratarea respingerii și a revenirii ca un singur mecanism este ceea ce transformă o autentificare eșuată într-o regulă de firewall scursă sau într-o înregistrare orfană. Sunt două obligații separate, și doar una dintre ele este automată. Citiți calea de ieșire proprie a hook-ului înainte de a da vina pe server că a lăsat stare în urmă.

O ieșire diferită de zero respinge clientul; nu vă anulează munca

Starea de ieșire a client-connect este o decizie de admitere. Zero înseamnă admitere, orice altceva înseamnă demolarea instanței înainte de a fi pe deplin stabilită. Nimic din acel contract nu inspectează ce a făcut deja scriptul în lumea exterioară. Dacă scriptul a adăugat o regulă iptables, a scris un rând sau a incrementat un contor, acele modificări supraviețuiesc respingerii.

Aceeași asimetrie se aplică modulelor, unde mai multe funcții connect pot rula pentru o singură instanță. Un eșec ulterior nu anulează retroactiv un succes anterior, iar funcția anterioară nu are cum să afle că o funcție soră a eșuat.

Respingerea este automată, curățarea nu

Manualul OpenVPN 2.6 leagă procesarea client-disconnect de succesul procesării connect anterioare. O excepție documentată acoperă lanțurile în cascadă: dacă un pas connect a returnat deja succes, pașii disconnect corespunzători rulează totuși la demolarea instanței, chiar dacă un pas connect ulterior a eșuat. Excepția este îngustă și depinde de câte callback-uri sunt configurate, de aceea testarea unui singur script de shell nu vă spune nimic despre un lanț.

Cartografiați configurația reală înainte de a proiecta în jurul ei: ce script, ce modul și în ce ordine rulează. Tabelul de mai jos enumeră ce lasă în urmă o configurare pe jumătate terminată.

What the hook did before failingWhat the client seesWhat stays behind
Created a firewall entryA rejection, with no tunnelA rule matching an address nobody owns
Wrote a state rowA rejection, with no tunnelA record of a session that never started
Allocated a tunnel addressA rejection, with no tunnelAn address marked busy until something releases it

Fiecare rând se termină la fel pentru client și diferit pentru gazdă. Clientul nu poate ști care dintre ele s-a întâmplat, așa că hook-ul trebuie să dea înapoi ce a făcut înainte de a returna starea de eșec.

Compensați în interiorul scriptului care a eșuat

Validați tot ce puteți înainte de a atinge starea partajată: câmpurile certificatului, disponibilitatea adresei, grupul din care face parte clientul. Cea mai ieftină respingere este cea care are loc înainte de orice alocare. Odată ce configurarea acoperă mai multe operații externe, țineți o evidență a celor finalizate și dați-le înapoi în ordine inversă înainte de a returna starea de eșec.

Un caz concret: scriptul creează o intrare de firewall pentru fiecare client, apoi eșuează la scrierea rândului de stare care o descrie. Ramura care eșuează ar trebui să elimine intrarea abia creată și apoi să iasă cu un cod diferit de zero. Așteptarea unui eveniment disconnect care poate nu va fi niciodată programat este felul în care o regulă supraviețuiește sesiunii pentru care era destinată.

Faceți compensarea idempotentă și cu proprietar

Curățarea trebuie să fie sigură la rulare de două ori și precisă cu privire la ce poate șterge. Un eveniment târziu dintr-o încercare anterioară nu ar trebui să elimine alocarea unei sesiuni mai noi doar pentru că ambele împart un nume comun sau o adresă de tunel reciclată. Înregistrați handle-ul sau id-ul de operație specific împreună cu resursa, și ștergeți după acea cheie, nu după nume. Tranzacțiile bazei de date ajută doar cu modificările proprii ale bazei; o regulă de firewall în afara acelei limite necesită un pas explicit de compensare.

Presupuneți că procesul poate muri între modificare și curățare, și adăugați un reconciliator care compară starea externă cu lista clienților admiși conform unui program.

Testați cele trei căi care chiar diferă

Exersați trei cazuri: o respingere înainte de a se aloca ceva, una după o alocare parțială și o sesiune admisă normală care se termină mai târziu. Acestea trei produc linii de jurnal diferite și reziduuri diferite. Verificați starea externă după fiecare, nu doar mesajul de eșec al clientului, pentru că eșecul clientului este rezultatul așteptat în două din trei.

Înregistrați faza hook-ului, starea de ieșire și ce a fost alocat sau eliberat, fără acreditări. Un jurnal de hook care numește resursa creată este ceea ce face o regulă scursă găsibilă șase luni mai târziu.

Articole similare