Jak zautomatyzować logowanie do VPN, które czeka na kod tokenu
Klient VPN w wierszu poleceń, który pyta w terminalu, blokuje skrypt przesyłający tylko hasło potokiem. Wykonaj tych pięć kontroli, zanim sięgniesz po expect.
Skrypt, który uruchamia klienta VPN dostawcy, wisi aż do przekroczenia limitu czasu, a dziennik pokazuje, że klient prosi o kod weryfikacyjny, którego nikt nie wpisał. Hasło zostało podane, więc oczywisty wniosek jest taki, że hasło jest błędne. Nie jest. Prośba o kod to osobna rozmowa na osobnym kanale, a twój skrypt nigdy jej nie otworzył.
Przejdź przez pięć kontroli poniżej po kolei. Zatrzymaj się na pierwszej, która zawiedzie, ponieważ każdy późniejszy krok zakłada, że wcześniejsze obowiązują.
Kontrola 1: czy klient w ogóle czyta standardowe wejście?
Sprawdź, zanim napiszesz jakąkolwiek automatyzację: podaj klientowi fałszywy sekret potokiem i zobacz, co się dzieje. Jeśli wypisze swój monit i potem czeka, nie czyta potoku.
Wielu klientów otwiera urządzenie terminala bezpośrednio albo odmawia przyjęcia sekretu ze strumienia, który nie jest terminalem, żeby zabłąkany potok nie mógł wyciec hasła. OpenVPN jest udokumentowanym wyjątkiem, bo --auth-user-pass przyjmuje ścieżkę do pliku. Klienci dostawców proszący o kod jednorazowy zwykle nie mają równoważnej opcji, a ich monit czeka na terminal, którego twój skrypt nigdy nie zapewnił.
Kontrola 2: czy proces potomny ma terminal?
pty przydziela terminal potomkowi, co zamienia monity w czytelne wyjście, na które możesz odpowiadać po kolei.
import pexpect
child = pexpect.spawn("openconnect vpn.example.net", encoding="utf-8", timeout=30)
child.expect("GROUP:")
child.sendline("staff")
child.expect("USERNAME:")
child.sendline("alice")
child.expect("PASSWORD:")
child.sendline(PASSWORD)
child.expect("Connected as", timeout=60)Ciągi monitów powyżej są przykładowe; skopiuj dokładny tekst, który wypisuje twój klient, wraz z wielkością liter i interpunkcją. Każde oczekiwanie ma limit czasu, a zamknięty potomek zgłasza wyjątek zamiast zwracać status, więc otocz sekwencję i traktuj koniec pliku jako porażkę. Zauważ, czego ten blok nie robi: potomek umiera razem ze skryptem, bo pty nie jest mechanizmem odłączania, a disown to wbudowana komenda powłoki, która nie dociera do procesu uruchomionego przez twój skrypt.
Kontrola 3: czy istnieje obsługiwane wejście nieinteraktywne?
Zeskrobywanie ekranu psuje się przy aktualizacjach klienta, więc najpierw szukaj udokumentowanej opcji.
Sprawdź wyjście pomocy pod kątem opcji tokenu lub kodu jednorazowego i sprawdź, czy usługa przyjmuje kod dopisany do pola hasła. Tam, gdzie klient czyta plik poświadczeń, podaj mu plik zamiast monitu:
openvpn --config office.ovpn --auth-user-pass <(printf "%s\n%s\n" "$USER_NAME" "$PASSWORD")Podstawienie procesu daje klientowi czytelną ścieżkę w systemach, które udostępniają /dev/fd. Sekret nadal jest sekretem z punktu widzenia klienta, więc trzymaj go z dala od dzienników, historii powłoki i list procesów. Jeśli klient odrzuca /dev/fd albo otwiera ścieżkę poświadczeń więcej niż raz, zapisz prawdziwy plik z restrykcyjnymi uprawnieniami i usuń go w tym samym skrypcie.
Kontrola 4: czy tunel przeżyje skrypt?
Domyślnie tunel umiera razem z rodzicem, co jest odwrotnością tego, czego potrzebuje skrypt startowy.
disown -h pomaga tylko wtedy, gdy uruchomiłeś klienta jako zadanie powłoki, bo edytuje tabelę zadań tej powłoki i nie dociera do procesu, który twój skrypt uruchomił przez pty. Menedżer usług jest lepszym domem dla wszystkiego, co musi łączyć się ponownie bez nadzoru, bo restartuje klienta i zapisuje, dlaczego padł. W każdym razie po każdym ponownym połączeniu oczekuj nowego kodu: token jednorazowy zużywa pierwsze udane uwierzytelnienie.
Uważaj, co sudo robi z sekwencją. Monit o hasło sudo nie jest monitem VPN, a odpowiadanie na niego hasłem VPN to najczęstszy sposób, w jaki inaczej poprawna kolejność oczekiwań jednak zawodzi.
Kontrola 5: czy kod osoby powinien być w skrypcie?
To kontrola, którą ludzie pomijają. Token należy do osoby, a tunel, który musi wstawać według harmonogramu, należy do maszyny.
Jeśli zadanie musi łączyć się ponownie bez nadzoru, zapytaj, czy certyfikat maszyny albo konto usługi mogą zastąpić logowanie interaktywne. Automatyzowanie czyjegoś kodu jednorazowego zamienia drugi czynnik w zapisany sekret, co jest zmianą polityki, a nie wygodą skryptu. Tam, gdzie usługa obsługuje oba, wybierz poświadczenie pasujące do tego, jak zadanie naprawdę działa. Zapisz odpowiedź, bo następna osoba zapyta, dlaczego skrypt trzyma token.
Jak wygląda działający skrypt
Skrypt wypisuje krok, do którego doszedł, nigdy sekretu, i kończy się kodem różnym od zera przy każdym nieudanym oczekiwaniu.
Sprawdź go, celowo go psując: złe hasło, zła grupa, wygasły token. Każda porażka powinna zatrzymać się na nazwanym kroku zamiast wisieć. Potem uruchom go raz przeciw prawdziwej usłudze i sprawdź tunel z zewnątrz skryptu, nie od środka.