Selhání client-connect v OpenVPN: je úklid při disconnect zaručen?
Odmítnutý klient nevrátí zpět pravidlo firewallu ani zapsaný záznam. Porovnejte smlouvu zpětného volání s vrácením změn, které musí váš hook provést sám.
Když skript client-connect skončí s nenulovým kódem, OpenVPN klienta odmítne a správci obvykle předpokládají, že odpovídající skript client-disconnect vše uklidí. Často se vůbec nespustí, protože zpracování disconnect závisí na tom, co se fázi connect podařilo dokončit. Považovat odmítnutí a vrácení změn za jeden mechanismus je to, co z neúspěšného přihlášení dělá uniklé pravidlo firewallu nebo osiřelý záznam. Jsou to dva oddělené závazky a jen jeden z nich je automatický. Přečtěte si vlastní výstupní cestu hooku, než začnete obviňovat server, že po sobě nechal stav.
Nenulový výstup klienta odmítne; vaši práci nevrátí
Výstupní stav client-connect je rozhodnutí o přijetí. Nula znamená přijmout, cokoli jiného znamená zbourat instanci dřív, než se plně ustaví. Nic v této smlouvě nezkoumá, co skript už udělal ve vnějším světě. Pokud skript přidal pravidlo iptables, zapsal řádek nebo zvýšil čítač, tyto změny odmítnutí přežijí.
Stejná asymetrie platí pro zásuvné moduly, kde se pro jednu instanci může spustit několik funkcí connect. Pozdější selhání zpětně neruší dřívější úspěch a dřívější funkce nemá jak zjistit, že sourozenecká funkce selhala.
Odmítnutí je automatické, úklid ne
Manuál OpenVPN 2.6 váže zpracování client-disconnect na úspěšné dokončení předchozího zpracování connect. Dokumentovaná výjimka pokrývá kaskádové řetězy: pokud jeden krok connect už vrátil úspěch, odpovídající kroky disconnect se při bourání instance přesto spustí, i když pozdější krok connect selhal. Ta výjimka je úzká a závisí na tom, kolik zpětných volání je nakonfigurováno, a proto vám testování jednoho shell skriptu nic neřekne o řetězu.
Zmapujte skutečnou konfiguraci, než kolem ní budete navrhovat: který skript, který modul a v jakém pořadí se spouštějí. Tabulka níže uvádí, co po polovičatém nastavení zůstane.
| 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 |
Každý řádek končí pro klienta stejně a pro hostitele jinak. Klient nemůže poznat, který z nich nastal, takže hook musí před vrácením stavu selhání odvinout, co udělal.
Kompenzujte uvnitř skriptu, který selhal
Ověřte vše, co můžete, než se dotknete sdíleného stavu: pole certifikátu, dostupnost adresy, skupinu, do které klient patří. Nejlevnější odmítnutí je to, které proběhne před jakýmkoli přidělením. Jakmile nastavení zahrne několik vnějších operací, veďte záznam o tom, které se dokončily, a před vrácením stavu selhání je odviňte v opačném pořadí.
Konkrétní případ: skript vytvoří záznam firewallu pro každého klienta a pak selže při zápisu řádku stavu, který ho popisuje. Selhávající větev má právě vytvořený záznam odstranit a potom skončit s nenulovým kódem. Čekání na událost disconnect, která nemusí být nikdy naplánována, je způsob, jak pravidlo přežije relaci, pro kterou bylo určeno.
Udělejte kompenzaci idempotentní a s vlastníkem
Úklid musí být bezpečný pro dvojí spuštění a přesný v tom, co smí smazat. Pozdní událost z dřívějšího pokusu nesmí odstranit přidělení novější relace jen proto, že obě sdílejí společné jméno nebo recyklovanou adresu tunelu. Zaznamenejte konkrétní handle nebo identifikátor operace spolu se zdrojem a mažte podle tohoto klíče, ne podle jména. Databázové transakce pomáhají jen s vlastními změnami databáze; pravidlo firewallu mimo tuto hranici potřebuje výslovný kompenzační krok.
Předpokládejte, že proces může zemřít mezi změnou a úklidem, a přidejte rekonciliační proces, který podle plánu porovnává vnější stav se seznamem přijatých klientů.
Otestujte tři cesty, které se skutečně liší
Procvičte tři případy: odmítnutí před jakýmkoli přidělením, odmítnutí po částečném přidělení a normální přijatou relaci, která později skončí. Ty tři dávají různé řádky protokolu a různé zbytky. Ověřujte vnější stav po každém z nich, nejen zprávu o selhání klienta, protože selhání klienta je očekávaným výsledkem ve dvou ze tří.
Zaznamenávejte fázi hooku, výstupní stav a co bylo přiděleno nebo uvolněno, bez přihlašovacích údajů. Protokol hooku, který pojmenuje vytvořený zdroj, je to, co činí uniklé pravidlo dohledatelným o šest měsíců později.