OpenVPN client-connect-fel: är städningen vid disconnect garanterad?
En avvisad klient rullar inte tillbaka en brandväggsregel eller en skriven post. Jämför callback-kontraktet med den återställning som din hook själv måste utföra.
När ett client-connect-skript avslutas med en kod som inte är noll avvisar OpenVPN klienten, och drifttekniker antar oftast att motsvarande client-disconnect-skript städar upp. Ofta körs det aldrig, eftersom disconnect-behandlingen beror på vad connect-fasen hann slutföra. Att behandla avvisning och återställning som en enda mekanism är det som förvandlar en misslyckad inloggning till en läckt brandväggsregel eller en föräldralös post. Det är två separata åtaganden, och bara ett av dem är automatiskt. Läs hookens egen utgångsväg innan du skyller servern för att lämna kvar tillstånd.
En utgång som inte är noll avvisar klienten; den rullar inte tillbaka ditt arbete
Utgångsstatusen för client-connect är ett antagningsbeslut. Noll betyder släpp in, allt annat betyder riv instansen innan den är fullt etablerad. Ingenting i det kontraktet granskar vad skriptet redan gjort mot omvärlden. Om skriptet lade till en iptables-regel, skrev en rad eller ökade en räknare överlever dessa ändringar avvisningen.
Samma asymmetri gäller för plugins, där flera connect-funktioner kan köras för en instans. Ett senare fel upphäver inte en tidigare framgång retroaktivt, och den tidigare funktionen har inget sätt att få veta att en systerfunktion misslyckades.
Avvisning är automatisk, städning är det inte
OpenVPN 2.6-handboken knyter behandlingen av client-disconnect till att den föregående connect-behandlingen lyckades. Ett dokumenterat undantag täcker kaskadkedjor: om ett connect-steg redan har returnerat framgång körs motsvarande disconnect-steg ändå när instansen rivs, även om ett senare connect-steg misslyckades. Undantaget är smalt och beror på hur många callbacks som är konfigurerade, därför säger test av ett enda skalskript ingenting om en kedja.
Kartlägg den faktiska konfigurationen innan du designar runt den: vilket skript, vilken plugin och i vilken ordning de körs. Tabellen nedan listar vad en halvfärdig uppsättning lämnar efter sig.
| What the hook did before failing | What the client sees | What stays behind |
|---|---|---|
| Created a firewall entry | A rejection, with no tunnel | A rule matching an address nobody owns |
| Wrote a state row | A rejection, with no tunnel | A record of a session that never started |
| Allocated a tunnel address | A rejection, with no tunnel | An address marked busy until something releases it |
Varje rad slutar likadant för klienten och olika för värden. Klienten kan inte se vilken av dem som inträffade, så hooken måste rulla tillbaka det den gjorde innan den returnerar felstatusen.
Kompensera inne i skriptet som misslyckades
Validera allt du kan innan du rör delat tillstånd: certifikatfält, adressens tillgänglighet, gruppen klienten tillhör. Den billigaste avvisningen är den som sker före någon tilldelning. När uppsättningen omfattar flera externa operationer, för register över vilka som slutfördes och rulla tillbaka dem i omvänd ordning innan du returnerar felstatusen.
Ett konkret fall: skriptet skapar en brandväggspost per klient och misslyckas sedan med att skriva tillståndsraden som beskriver den. Den misslyckade grenen bör ta bort posten den nyss skapade och sedan avsluta med en kod som inte är noll. Att vänta på en disconnect-händelse som kanske aldrig schemaläggs är hur en regel överlever den session den var avsedd för.
Gör kompensationen idempotent och ägd
Städning måste vara säker att köra två gånger och precis om vad den får radera. En sen händelse från ett tidigare försök bör inte ta bort en nyare sessions tilldelning bara för att båda delar ett gemensamt namn eller en återanvänd tunneladress. Registrera det specifika handtaget eller operations-ID:t tillsammans med resursen, och radera på den nyckeln i stället för på namn. Databastransaktioner hjälper bara med databasens egna ändringar; en brandväggsregel utanför den gränsen behöver ett uttryckligt kompensationsteg.
Anta att processen kan dö mellan ändringen och städningen, och lägg till en avstämmare som enligt ett schema jämför externt tillstånd med listan över godkända klienter.
Testa de tre vägar som faktiskt skiljer sig
Öva tre fall: en avvisning innan något tilldelats, en efter en partiell tilldelning och en normal godkänd session som senare avslutas. De tre ger olika loggrader och olika rester. Verifiera externt tillstånd efter var och en, inte bara klientens felmeddelande, eftersom klientens fel är det förväntade resultatet i två av de tre.
Logga hookens fas, utgångsstatusen och vad som tilldelades eller frigjordes, utan inloggningsuppgifter. En hooklogg som namnger resursen den skapade är det som gör en läckt regel hittbar sex månader senare.