Gdy skrypt client-connect kończy się kodem różnym od zera, OpenVPN odrzuca klienta, a administratorzy zwykle zakładają, że odpowiedni skrypt client-disconnect posprząta. Często w ogóle się nie uruchamia, bo przetwarzanie disconnect zależy od tego, co udało się ukończyć etapowi connect. Traktowanie odrzucenia i wycofania jako jednego mechanizmu sprawia, że nieudane logowanie zamienia się w wyciekłą regułę firewalla lub osierocony rekord. To dwa osobne obowiązki i tylko jeden z nich jest automatyczny. Przeczytaj własną ścieżkę wyjścia hooka, zanim obwinisz serwer o pozostawienie stanu.

Wywołanie z kodem różnym od zera odrzuca klienta; nie cofa Twojej pracy

Status wyjścia client-connect jest decyzją o dopuszczeniu. Zero oznacza dopuszczenie, cokolwiek innego oznacza rozbiórkę instancji, zanim zostanie w pełni ustanowiona. Nic w tym kontrakcie nie sprawdza, co skrypt już zrobił w świecie zewnętrznym. Jeśli skrypt dodał regułę iptables, zapisał wiersz lub zwiększył licznik, te zmiany przeżywają odrzucenie.

Ta sama asymetria dotyczy wtyczek, gdzie dla jednej instancji może działać kilka funkcji connect. Późniejsza awaria nie anuluje wstecznie wcześniejszego sukcesu, a wcześniejsza funkcja nie ma sposobu dowiedzieć się, że siostrzana funkcja zawiodła.

Odrzucenie jest automatyczne, sprzątanie nie

Podręcznik OpenVPN 2.6 wiąże przetwarzanie client-disconnect z powodzeniem wcześniejszego przetwarzania connect. Udokumentowany wyjątek obejmuje łańcuchy kaskadowe: jeśli jeden krok connect już zwrócił sukces, odpowiadające mu kroki disconnect nadal się wykonują przy rozbiórce instancji, nawet jeśli późniejszy krok connect zawiódł. Ten wyjątek jest wąski i zależy od liczby skonfigurowanych wywołań zwrotnych, dlatego testowanie jednego skryptu powłoki nic nie mówi o łańcuchu.

Zmapuj rzeczywistą konfigurację, zanim zaczniesz wokół niej projektować: który skrypt, która wtyczka i w jakiej kolejności się uruchamiają. Poniższa tabela wymienia, co zostawia po sobie nieukończona konfiguracja.

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

Każdy wiersz kończy się tak samo dla klienta i inaczej dla hosta. Klient nie może stwierdzić, który z nich nastąpił, więc hook musi wycofać to, co zrobił, przed zwróceniem statusu błędu.

Wyrównaj w skrypcie, który zawiódł

Zweryfikuj wszystko, co możesz, zanim dotkniesz współdzielonego stanu: pola certyfikatu, dostępność adresu, grupę, do której należy klient. Najtańsze odrzucenie to takie, które następuje przed jakimkolwiek przydziałem. Gdy konfiguracja obejmuje kilka operacji zewnętrznych, prowadź rejestr ukończonych i wycofuj je w odwrotnej kolejności przed zwróceniem statusu błędu.

Konkretny przypadek: skrypt tworzy wpis firewalla dla klienta, a potem nie udaje mu się zapisać wiersza stanu, który go opisuje. Zawodząca gałąź powinna usunąć właśnie utworzony wpis i dopiero wtedy zakończyć się kodem różnym od zera. Czekanie na zdarzenie disconnect, które może nigdy nie zostać zaplanowane, to sposób, w jaki reguła przeżywa sesję, dla której była przeznaczona.

Uczyń wyrównanie idempotentnym i przypisanym

Sprzątanie musi być bezpieczne przy dwukrotnym uruchomieniu i precyzyjne co do tego, co może usunąć. Późne zdarzenie z wcześniejszej próby nie powinno usuwać przydziału nowszej sesji tylko dlatego, że oba dzielą wspólną nazwę lub recyklingowany adres tunelu. Zapisuj konkretny uchwyt lub identyfikator operacji razem z zasobem i usuwaj po tym kluczu, a nie po nazwie. Transakcje bazy danych pomagają tylko przy własnych zmianach bazy; reguła firewalla poza tą granicą wymaga jawnego kroku wyrównania.

Załóż, że proces może umrzeć między zmianą a sprzątaniem, i dodaj uzgadniacz, który według harmonogramu porównuje stan zewnętrzny z listą dopuszczonych klientów.

Przetestuj trzy ścieżki, które naprawdę się różnią

Przećwicz trzy przypadki: odrzucenie, zanim cokolwiek zostanie przydzielone, odrzucenie po częściowym przydziale oraz normalną dopuszczoną sesję, która później się kończy. Te trzy dają różne wiersze dziennika i różne pozostałości. Sprawdzaj stan zewnętrzny po każdym z nich, nie tylko komunikat błędu klienta, ponieważ awaria klienta jest oczekiwanym wynikiem w dwóch z trzech.

Zapisuj etap hooka, status wyjścia i to, co przydzielono lub zwolniono, bez danych uwierzytelniających. Dziennik hooka, który nazywa utworzony zasób, jest tym, co czyni wyciekłą regułę znajdowalną pół roku później.

Powiązane artykuły