Rekord rozłączenia przychodzi po tym, jak druga sesja pod tą samą nazwą już się rozpoczęła, a program obsługi aktualizujący najnowszy wiersz dla tego użytkownika zamyka niewłaściwą sesję. Błąd nie leży w porównaniu znaczników czasu. Leży w założeniu, że nazwa plus cała sekunda identyfikują jedną sesję. Zdarzenia hooków opisują połączenia, a połączenie potrzebuje klucza, który należy tylko do niego.

Zacznij od tego, co naprawdę oznaczają zmienne rozłączenia. Dopiero wtedy wybór identyfikatora ma sens.

Co tak naprawdę niesie hook rozłączenia

W Community OpenVPN 2.6 dokumentacja hooków time_unix definiuje jako moment, w którym klient się połączył; jest on ustawiany przed uruchomieniem hooka połączenia i nie jest przedefiniowywany, gdy hook rozłączenia zadziała później. time_duration podaje długość tej sesji w sekundach, więc koniec to w przybliżeniu czas połączenia plus czas trwania. Oba są całymi sekundami i żaden nie jest momentem, w którym twoja baza danych przetworzyła zdarzenie. To właśnie rozdzielenie tych trzech czasów sprawia, że korelacja się trzyma, zamiast tylko wyglądać poprawnie podczas testów.

Ta arytmetyka zasługuje na zapisanie, bo to jedyny czas zakończenia, jaki oferuje sam hook. Opóźnienie przetwarzania i różnice zegarów między hostami nadal pozostają poza nim.

Dlaczego oczywista para nie jest unikalna

Połączenie nazwy z czasem połączenia zawęża wyszukiwanie i nadal nie identyfikuje sesji. Klient może połączyć się ponownie w tej samej sekundzie, więc dwie sesje mogą dzielić obie wartości. Dwie instancje demona albo wymiana serwera zachowująca tę samą konfigurację mogą wytworzyć tę parę z różnych procesów. Adresy tuneli są odzyskiwane z tego samego powodu, co czyni je atrybutami sesji, a nie jej identyfikatorami.

Tryb awarii jest cichy. Spóźnione rozłączenie pierwszej sesji pasuje do najnowszego wiersza i kończy przydział, którego druga sesja wciąż używa, więc użytkownik doświadcza losowej utraty dostępu, której nie spowodował. Liczba wierszy, która potem wygląda poprawnie, utrudnia wykrycie problemu.

Skąd bierze się stabilna tożsamość

Interfejs zarządzania nadaje każdemu klientowi identyfikator instancji klienta, a ten identyfikator jest ograniczony do instancji demona, która go wydała. Działający klucz zewnętrzny łączy stabilny identyfikator serwera, epokę zmieniającą się przy starcie demona i ten identyfikator klienta. Epoka ma znaczenie, bo identyfikatory restartują się wraz z procesem, a sam identyfikator procesu może później zostać ponownie użyty przez coś niepowiązanego.

Zmienne środowiskowe ustawione wewnątrz podrzędnego skryptu połączenia nie trafiają do późniejszego skryptu rozłączenia nadrzędnego demona, ponieważ te skrypty działają jako osobne procesy z własnymi środowiskami. Dlatego generowanie UUID w momencie połączenia to połowa rozwiązania. Strona rozłączenia musi umieć nazwać ten sam UUID, co oznacza, że tożsamość musi żyć gdzieś, gdzie dotrą oba zdarzenia.

Uczyń zakończenie warunkowym i idempotentnym

Kończ według właściciela sesji, nie według użytkownika. Program obsługi, który usuwa dowolny przydział aktualnie należący do nazwy, w końcu usunie niewłaściwy, a jedyną obroną jest sprawdzenie, że usuwany rekord to rekord opisany przez to zdarzenie.

Interfejs zarządzania przyjmuje jednego podłączonego klienta zarządzania naraz, więc integruj się z tym, kto już go posiada, zamiast podłączać drugiego obserwatora konkurującego o ten sam gniazdo.

Obsługuj powtarzające się zdarzenia bez dwukrotnej zmiany wyniku i zachowuj niesparowane zdarzenia do uzgodnienia, zamiast je wyrzucać. Wywołanie zwrotne rozłączenia ma udokumentowane warunki cyklu życia i po awarii lub twardym zatrzymaniu może nigdy nie przyjść, więc projekt potrzebuje sposobu przedstawienia sesji, której zdarzenia końcowego brakuje.

Przetestuj trudne przypadki celowo: dwa ponowne połączenia w ciągu jednej sekundy, dwie instancje demona, połączenie, którego przetwarzanie nie powiodło się, oraz restart demona w środku sesji. Klucz, który przetrwa wszystkie cztery, jest kluczem wartym zaufania.

Powiązane artykuły