Přihlášení k Cisco Secure Client z linuxového shellu bez GUI
Here-document není přihlašovací protokol. Zaznamenejte skutečné pořadí výzev, použijte expect, když se mění, a držte heslo mimo argv i historii.
Věří se, že here-document se čtyřmi řádky je úplné neinteraktivní přihlášení, a tahle víra vydrží přesně do chvíle, kdy brána nabídne dvě skupiny, vypíše banner nebo si po heslu vyžádá token. Pak stejný skript napíše heslo do menu, které čekalo číslo. Klient příkazové řádky na Linuxu se skriptovat dá, ale jen když mu předáte to, na co skutečně čeká, a ne to, co předpokládal návod.
Pevný here-document je mýtus
Ciscův linuxový klient instaluje malý program do /opt/cisco/anyconnect/bin/vpn a Secure Client 5 jej stále dodává. Když běží služba vpnagentd, přepínač -s čte odpovědi ze standardního vstupu v pořadí, v jakém se je klient ptá. To pořadí není mezi servery pevné.
Menu skupin přidá jeden řádek. Přihlašovací banner přidá další. Dotaz na token po heslu rozbije čtyřřádkový skript úplně, protože pátý řádek nikdy neměl být token. Selhání je tiché: klient dostane špatný řetězec, pokus o připojení skončí a skript neohlásí nic užitečného.
Spusťte klienta jednou ručně proti stejné bráně a verzi a zapište si výzvy v pořadí, v jakém přicházejí. Ten přepis je specifikace, kterou váš skript musí splnit.
Na co klient čeká
V obvyklém případě jedné skupiny je sled tento: uživatelské jméno, heslo a někdy potvrzení banneru. První řádek here-documentu se stane indexem menu jen tehdy, když server menu skutečně zobrazí, a proto stejný skript na jednom místě funguje a na dalším selže.
/opt/cisco/anyconnect/bin/vpn -s connect vpn.example.net <<EOF
0
alice
$VPN_PASSWORD
y
EOF
Čtěte to jako čtyři odpovědi na čtyři možné otázky, ne jako pevný protokol. Nula je volba v menu, ne heslo, a koncové y přijímá banner, který mnohé brány nikdy nepošlou.
Použijte expect, když pořadí není předvídatelné
Když se pořadí výzev mění, přestaňte počítat řádky a čekejte na výzvu. Skript expect spustí příkaz connect, počká na výzvu k uživatelskému jménu, odešle, počká na výzvu k heslu, odešle a pak čeká na stav připojeno. Každé čekání má vlastní časový limit.
Vypršení limitu berte jako selhání a skončete. Skript, který po neočekávané výzvě dál posílá vstup, ochotně napíše heslo do shellu, do menu nebo do čehokoli jiného, co zrovna čte. Tak se z přihlašovacího skriptu stane incident.
Expect vám také dá přepis, který můžete zalogovat s odstraněným tajemstvím, a ten má větší cenu než návratový kód, když brána po upgradu změní své výzvy.
Tajemství musí někde žít
Každá verze tohoto problému má jednu slabinu: heslo musí existovat ve formě, kterou klient umí přečíst. Předání jako argumentu ho vystaví v tabulce procesů. Vypsání přes rouru ho často dostane do historie shellu. Obojí mohou číst jiní místní uživatelé na sdíleném skokovém hostu.
install -m 600 /dev/null ~/.vpn-secret
umask 077; VPN_PASSWORD=$(cat ~/.vpn-secret) /opt/cisco/anyconnect/bin/vpn -s connect vpn.example.net
Soubor s právy 600 vlastněný účtem je minimum, a pořád je to tajemství v čistém textu na disku. secret-tool lookup čte z linuxové klíčenky a na macOS dělá stejnou práci security find-generic-password -w. Kde organizace podporuje autentizaci certifikátem nebo SAML, dejte jí přednost, protože uložené heslo plus MFA stále nedokáže dokončit přihlášení bez člověka.
Potvrďte tunel a pak se postarejte o ukončení
Nevěřte tomu, že chybí chybový text. Zeptejte se klienta na stav a rozhraní zkontrolujte nezávisle:
/opt/cisco/anyconnect/bin/vpn state
ip -brief address
Stav by měl hlásit Connected a tunelové rozhraní by mělo mít adresu. Pak si vyžádejte jeden zdroj, který má tunel dosáhnout, protože připojený stav bez trasy je skutečný výsledek a snadno se přehlédne.
Odpojte se pomocí vpn disconnect a nechte skript, který tunel spustil, ať se postará o ukončení. Neúspěšná úloha, která nechá relaci otevřenou, spotřebuje licenční místo a dalšímu člověku to vypadá jako problém s autentizací.
Pořadí práce: potvrďte vpnagentd, zachyťte skutečné pořadí výzev, vyberte here-document nebo expect, držte tajemství mimo argv a historii, ověřte stav a jeden zdroj a pak se ve stejném skriptu odpojte.