Logowanie do Cisco Secure Client z powłoki Linux bez interfejsu graficznego
Dokument here to nie protokół logowania. Zapisz rzeczywistą kolejność monitów, użyj expect, gdy się zmienia, i trzymaj hasło poza argv i historią.
Przekonanie głosi, że dokument here z czterema liniami to kompletne nieinteraktywne logowanie i trzyma się ono dokładnie do chwili, gdy brama zaproponuje dwie grupy, wydrukuje baner albo poprosi o token po haśle. Wtedy ten sam skrypt wpisuje hasło do menu, które oczekiwało liczby. Klient wiersza poleceń na Linuksie da się skryptyzować, ale tylko wtedy, gdy podasz mu to, na co naprawdę czeka, a nie to, co założył poradnik.
Stały dokument here to mit
Klient Cisco dla Linuksa instaluje mały program w /opt/cisco/anyconnect/bin/vpn, a Secure Client 5 wciąż go dostarcza. Gdy usługa vpnagentd działa, flaga -s czyta odpowiedzi ze standardowego wejścia w kolejności, w jakiej klient o nie prosi. Ta kolejność nie jest stała między serwerami.
Menu grup dodaje jedną linię. Baner logowania dodaje kolejną. Prośba o token po haśle całkowicie łamie skrypt z czterech linii, bo piąta linia nigdy nie miała być tokenem. Awaria jest cicha: klient otrzymuje zły ciąg, próba połączenia się kończy, a skrypt nie zgłasza nic użytecznego.
Uruchom klienta ręcznie raz przeciw tej samej bramie i wersji, i zapisz monity w kolejności ich pojawiania się. Ten zapis to specyfikacja, którą twój skrypt musi spełnić.
Na co czeka klient
W zwykłym przypadku jednej grupy sekwencja to nazwa użytkownika, hasło i czasem potwierdzenie banera. Pierwsza linia dokumentu here staje się indeksem menu tylko wtedy, gdy serwer naprawdę pokazuje menu, dlatego ten sam skrypt działa w jednym miejscu, a zawodzi w następnym.
/opt/cisco/anyconnect/bin/vpn -s connect vpn.example.net <<EOF
0
alice
$VPN_PASSWORD
y
EOF
Czytaj to jako cztery odpowiedzi na cztery możliwe pytania, a nie jako stały protokół. Zero to wybór z menu, nie hasło, a końcowe y akceptuje baner, którego wiele bram nigdy nie wysyła.
Używaj expect, gdy kolejność nie jest przewidywalna
Gdy kolejność monitów się zmienia, przestań liczyć linie i zamiast tego czekaj na monit. Skrypt expect uruchamia polecenie connect, czeka na monit nazwy użytkownika, wysyła, czeka na monit hasła, wysyła, a potem czeka na stan połączenia. Każde oczekiwanie ma własny limit czasu.
Traktuj przekroczenie limitu czasu jako porażkę i wyjdź. Skrypt, który po nieoczekiwanym monicie nadal wysyła dane, ochoczo wpisze hasło do powłoki, menu albo czegokolwiek innego, co akurat czyta. Tak skrypt logowania staje się incydentem.
Expect daje też zapis, który możesz zalogować z usuniętym sekretem, a to jest warte więcej niż kod wyjścia, gdy brama po aktualizacji zmienia swoje monity.
Sekret musi gdzieś mieszkać
Każda wersja tego problemu ma jedną słabość: hasło musi istnieć w formie, którą klient potrafi odczytać. Przekazanie go jako argumentu ujawnia je w tablicy procesów. Wyświetlenie go przez potok często umieszcza je w historii powłoki. Oba są czytelne dla innych lokalnych użytkowników na współdzielonym hoście przeskoku.
install -m 600 /dev/null ~/.vpn-secret
umask 077; VPN_PASSWORD=$(cat ~/.vpn-secret) /opt/cisco/anyconnect/bin/vpn -s connect vpn.example.net
Plik z trybem 600 należący do konta to minimum, i wciąż jest sekretem w postaci czystego tekstu na dysku. secret-tool lookup czyta z pęku kluczy Linuksa, a w macOS to samo zadanie wykonuje security find-generic-password -w. Tam, gdzie organizacja wspiera uwierzytelnianie certyfikatem lub SAML, preferuj je, bo zapisane hasło plus MFA wciąż nie ukończy logowania bez człowieka.
Potwierdź tunel, a potem przejmij rozłączenie
Nie ufaj brakowi tekstu błędu. Zapytaj klienta o jego stan i sprawdź interfejs niezależnie:
/opt/cisco/anyconnect/bin/vpn state
ip -brief address
Stan powinien brzmieć Connected, a interfejs tunelu powinien mieć adres. Potem zażądaj jednego zasobu, do którego tunel ma dotrzeć, bo stan połączenia bez trasy jest realnym wynikiem i łatwo go przeoczyć.
Rozłącz się przez vpn disconnect i pozwól skryptowi, który uruchomił tunel, przejąć rozłączenie. Nieudane zadanie, które zostawia sesję otwartą, zużywa miejsce licencji i wygląda jak problem z uwierzytelnianiem dla następnej osoby, która spróbuje.
Kolejność pracy: potwierdź vpnagentd, zapisz rzeczywistą kolejność monitów, wybierz dokument here lub expect, trzymaj sekret poza argv i historią, zweryfikuj stan i jeden zasób, a potem rozłącz się w tym samym skrypcie.