Uno script che avvia un client VPN di un fornitore resta bloccato finché non scade il timeout, e il registro mostra che il client chiede un codice di verifica che nessuno ha digitato. La password era stata fornita, quindi la conclusione ovvia è che la password sia sbagliata. Non lo è. La richiesta del codice è una conversazione separata su un canale separato, e il tuo script non l'ha mai aperta.

Esegui i cinque controlli qui sotto in ordine. Fermati al primo che fallisce, perché ogni passo successivo presuppone che i precedenti valgano.

Controllo 1: il client legge l'input standard, almeno?

Prova prima di scrivere qualsiasi automazione: invia un segreto finto al client attraverso una pipe e osserva cosa succede. Se stampa la sua richiesta e poi attende, non sta leggendo la pipe.

Molti client aprono direttamente il dispositivo terminale, o rifiutano di prendere un segreto da un flusso che non è un terminale, così che una pipe vagante non possa far trapelare una password. OpenVPN è l'eccezione documentata, perché --auth-user-pass accetta un percorso di file. I client dei fornitori che chiedono un codice monouso di solito non hanno un'opzione equivalente, e la loro richiesta attende un terminale che il tuo script non ha mai fornito.

Controllo 2: il processo figlio ha un terminale?

Un pty assegna un terminale al figlio, il che trasforma le richieste in output leggibile a cui puoi rispondere in ordine.

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)

Le stringhe di richiesta qui sopra sono illustrative; copia il testo esatto che il tuo client stampa, incluse maiuscole e punteggiatura. Ogni attesa ha un timeout, e un figlio chiuso solleva un'eccezione invece di restituire uno stato, quindi avvolgi la sequenza e tratta la fine del file come un fallimento. Nota cosa non fa questo blocco: il figlio muore con lo script, perché un pty non è un meccanismo di distacco e disown è un comando interno della shell che non può raggiungere un processo che il tuo script ha avviato.

Controllo 3: esiste un input non interattivo supportato?

Lo scraping dello schermo si rompe agli aggiornamenti del client, quindi cerca prima un'opzione documentata.

Controlla l'output della guida per un'opzione token o codice monouso, e verifica se il servizio accetta il codice aggiunto al campo password. Dove il client legge un file di credenziali, dagli il file invece di una richiesta:

openvpn --config office.ovpn --auth-user-pass <(printf "%s\n%s\n" "$USER_NAME" "$PASSWORD")

La sostituzione di processo dà al client un percorso leggibile sui sistemi che forniscono /dev/fd. Il segreto resta un segreto dal punto di vista del client, quindi tienilo fuori da registri, cronologia della shell ed elenchi dei processi. Se il client rifiuta /dev/fd o apre il percorso delle credenziali più di una volta, scrivi un file reale con permessi restrittivi ed eliminalo nello stesso script.

Controllo 4: il tunnel sopravvivrà allo script?

Per impostazione predefinita il tunnel muore con il suo genitore, che è l'opposto di ciò che serve a uno script di avvio.

disown -h aiuta solo quando hai avviato il client come job della shell, perché modifica la tabella dei job di quella shell e non può raggiungere un processo che il tuo script ha avviato tramite un pty. Un gestore di servizi è una casa migliore per tutto ciò che deve riconnettersi senza supervisione, perché riavvia il client e registra perché è morto. In ogni caso, aspettati un nuovo codice dopo ogni riconnessione: un token monouso viene consumato dalla prima autenticazione riuscita.

Osserva cosa fa sudo alla sequenza. La richiesta di password di sudo non è la richiesta del VPN, e rispondere con la password del VPN è il modo più comune in cui un ordine di attese per il resto corretto fallisce comunque.

Controllo 5: il codice di una persona deve stare in uno script?

Questo è il controllo che le persone saltano. Un token appartiene a una persona, mentre un tunnel che deve salire secondo un orario appartiene alla macchina.

Se un lavoro deve riconnettersi senza supervisione, chiedi se un certificato macchina o un account di servizio possono sostituire l'accesso interattivo. Automatizzare il codice monouso di qualcuno trasforma un secondo fattore in un segreto memorizzato, che è un cambiamento di policy e non una comodità di scripting. Dove il servizio supporta entrambi, scegli la credenziale che corrisponde a come il lavoro viene realmente eseguito. Scrivi la risposta, perché la prossima persona chiederà perché lo script detiene un token.

Che aspetto ha uno script che funziona

Lo script stampa il passo raggiunto, mai il segreto, ed esce con un codice diverso da zero a ogni attesa fallita.

Provalo rompendolo di proposito: password sbagliata, gruppo sbagliato, token scaduto. Ogni fallimento dovrebbe fermarsi a un passo con un nome invece di bloccarsi. Poi eseguilo una volta contro il servizio reale e controlla il tunnel dall'esterno dello script, non dall'interno.

Articoli correlati