Skrypt auth-user-pass-verify OpenVPN: właściwy kod wyjścia
Status wyjścia hooka jest decyzją, a nie jego linią logu. Przypisz wyniki weryfikatora do 0, 1 lub odroczonego, czytaj plik poświadczeń jako dane i kończ się zamkniętym niepowodzeniem.
OpenVPN działa na podstawie dokładnie jednego wyjścia skryptu logowania: statusu wyjścia. Wszystko inne, co robi skrypt, to dekoracja. Hook może dotrzeć do właściwego weryfikatora, przeanalizować odpowiedź i wypisać przyjazną linię, a zwrócić status oznaczający przeciwieństwo intencji autora. Przeczytaj najpierw kontrakt i napisz adapter wokół niego, a cała ta klasa błędów znika.
Kontrakt jest mały: zidentyfikuj zgłoszone konto, zadaj zatwierdzonemu weryfikatorowi zamierzone pytanie i przyjmij tylko udokumentowany pozytywny wynik tego weryfikatora dla tego żądania. Wszystko inne to infrastruktura.
Trzy statusy, które hook może zwrócić
Dla auth-user-pass-verify definiuje podręcznik OpenVPN 2.6 kod wyjścia 0 jako akceptację, 1 jako odrzucenie, a 2 jako odroczoną autoryzację. To jedyne trzy odpowiedzi, które rozumie serwer, i każda ma inne znaczenie dla połączenia. Skrypt, który kończy się statusem ostatniego polecenia, faktycznie pozwolił, by o głosie zdecydowało wywołanie logu albo krok sprzątania. Zapisuj status jawnie w każdej gałęzi, także w tych, które nigdy nie dotarły do weryfikatora.
Sukces HTTP i sukces uwierzytelnienia to oddzielne warstwy, a ich mieszanie jest najczęstszą przyczyną błędu tego hooka. Odpowiedź 202 oznacza, że żądanie przyjęto do przetworzenia, a nie że hasło zostało zweryfikowane. Zdefiniuj oczekiwany status, kształt odpowiedzi i ostateczny werdykt na podstawie otrzymanego kontraktu API i nie wymyślaj nazwy pola, bo wygląda wiarygodnie.
Jeśli odpowiedź naprawdę przychodzi później, to jest ścieżka odroczona i tak trzeba ją zbudować. Odroczona próba, która nigdy się nie kończy, trzyma zasoby do wygaśnięcia limitu czasu.
Czytaj plik poświadczeń dokładnie tak, jak został zapisany
W udokumentowanej metodzie przez plik serwer zapisuje nazwę użytkownika i hasło jako pierwsze dwie linie pliku tymczasowego, przekazuje tę ścieżkę skryptowi i usuwa plik, gdy skrypt wraca. Czytaj te dwie linie jako dane: nigdy nie uruchamiaj pliku przez source, nigdy go nie oceniaj i nigdy nie wklejaj jego zawartości do wiersza poleceń, gdzie może go zobaczyć tabela procesów. Zachowaj hasło bajt po bajcie, bo normalizacja, jaką zastosowałbyś do nazwy użytkownika, po cichu zmienia poświadczenie. Katalog tego pliku tymczasowego to decyzja wdrożeniowa, więc potwierdź, że istnieje i nie jest czytelny dla wszystkich na hoście.
Trzymaj własne poświadczenie weryfikatora osobno od poświadczenia użytkownika: należy do integracji bramy, mieszka w magazynie sekretów platformy i wymaga tylko uprawnienia do weryfikacji, więc nie jest dowodem, że uwierzytelniła się konkretna osoba. Nie pozwól też, by hook stał się drugim weryfikatorem haseł. Ręczne przepisanie porównania hasha nie staje się bezpieczne przez optymalizację pętli, a utrzymywany weryfikator już rozumie solony i kosztowny schemat magazynu kont.
Zdecyduj, co oznacza odpowiedź niemożliwa do sklasyfikowania
Przekroczenia czasu, błędy transportu, zniekształcone odpowiedzi i niedostępny backend mają jedną cechę: żadna z nich nie jest pozytywną weryfikacją. Hook powinien w każdym z tych przypadków zawieść zamknięty i zapisać powód, na podstawie którego operator może działać. Ten powód należy do chronionego logu, a nie do komunikatu zwracanego osobie próbującej się połączyć.
Bądź równie ostrożny wobec ścieżek, które w ogóle nie docierają do weryfikatora. Brakująca wartość konfiguracji, nieczytelny sekret lub niezainstalowana zależność powinny dawać odrzucenie z odrębnym znacznikiem operacyjnym zamiast przypadkowej akceptacji. Testuj te gałęzie, usuwając zależność w laboratorium, a nie rozważając je teoretycznie. Tam, gdzie błąd logowania może zmienić status wyjścia, logowanie stało się częścią decyzji i musi być nieszkodliwe.
Konfiguracja serwera też tu ma znaczenie: skrypty definiowane przez użytkownika są respektowane tylko wtedy, gdy działająca konfiguracja pozwala na nie na wymaganym poziomie bezpieczeństwa, a to ustawienie jest świadomym wyborem, a nie wartością domyślną do podniesienia.
Weryfikuj werdykt, a nie wydrukowaną linię
Zbuduj test, który bezpośrednio sprawdza status wyjścia. Napisz przykładowy plik poświadczeń, uruchom na nim hook ze znanym dobrym i znanym złym kontem, i czytaj status zgłaszany przez powłokę, a nie tekst drukowany przez skrypt. Ten jeden test wyłapuje większość błędów kontraktu, zanim klient w ogóle się połączy.
Następnie przepuść te same przypadki przez prawdziwego klienta wobec odbiorcy nieprodukcyjnego i potwierdź, co zapisał serwer: zaakceptowano, odrzucono lub odroczono. Pokryj wyłączone konto, błędne hasło, zniekształconą odpowiedź weryfikatora, przekroczenie czasu i nieosiągalny weryfikator. Jeśli którykolwiek przypadek trafi w stan, którego nie umiesz nazwać, adapter nie jest gotowy.
Trzymaj przypisanie wyniku weryfikatora do statusu wyjścia obok skryptu w repozytorium, aby następny inżynier mógł przeczytać kontrakt bez inżynierii odwrotnej kodu.