Un script qui lance un client VPN d'un fournisseur reste bloqué jusqu'au délai d'expiration, et le journal montre que le client demande un code de vérification que personne n'a saisi. Le mot de passe a été fourni, donc la conclusion évidente est que le mot de passe est faux. Il ne l'est pas. La demande de code est une conversation séparée sur un canal séparé, et votre script ne l'a jamais ouverte.

Passez les cinq vérifications ci-dessous dans l'ordre. Arrêtez-vous à la première qui échoue, car chaque étape ultérieure suppose que les précédentes tiennent.

Vérification 1 : le client lit-il seulement l'entrée standard ?

Testez avant d'écrire la moindre automatisation : envoyez un faux secret au client par un tube et regardez ce qui se passe. S'il affiche sa demande puis attend, il ne lit pas le tube.

Beaucoup de clients ouvrent directement le périphérique de terminal, ou refusent de prendre un secret dans un flux qui n'est pas un terminal, afin qu'un tube égaré ne puisse pas laisser fuir un mot de passe. OpenVPN est l'exception documentée, car --auth-user-pass accepte un chemin de fichier. Les clients de fournisseur qui demandent un code à usage unique n'ont généralement pas d'option équivalente, et leur demande attend un terminal que votre script n'a jamais fourni.

Vérification 2 : l'enfant a-t-il un terminal ?

Un pty alloue un terminal à l'enfant, ce qui transforme les demandes en sortie lisible à laquelle vous pouvez répondre dans l'ordre.

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)

Les chaînes de demande ci-dessus sont illustratives ; copiez le texte exact que votre client imprime, y compris la casse et la ponctuation. Chaque attente porte un délai, et un enfant fermé lève une exception au lieu de renvoyer un statut, donc enveloppez la séquence et traitez la fin de fichier comme un échec. Notez ce que ce bloc ne fait pas : l'enfant meurt avec le script, car un pty n'est pas un mécanisme de détachement et disown est une commande interne du shell qui ne peut pas atteindre un processus que votre script a lancé.

Vérification 3 : existe-t-il une entrée non interactive prise en charge ?

Le grattage d'écran casse à chaque mise à jour du client, alors cherchez d'abord une option documentée.

Consultez la sortie d'aide pour une option de jeton ou de code à usage unique, et vérifiez si le service accepte le code ajouté au champ mot de passe. Là où le client lit un fichier d'identifiants, donnez-lui le fichier au lieu d'une demande :

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

La substitution de processus donne au client un chemin lisible sur les systèmes qui fournissent /dev/fd. Le secret reste un secret du point de vue du client, alors gardez-le hors des journaux, de l'historique du shell et des listes de processus. Si le client refuse /dev/fd ou ouvre le chemin des identifiants plus d'une fois, écrivez un vrai fichier avec des permissions restrictives et supprimez-le dans le même script.

Vérification 4 : le tunnel survivra-t-il au script ?

Par défaut, le tunnel meurt avec son parent, ce qui est l'inverse de ce dont un script d'amorçage a besoin.

disown -h n'aide que lorsque vous avez lancé le client comme tâche du shell, car elle modifie la table des tâches de ce shell et ne peut pas atteindre un processus que votre script a lancé via un pty. Un gestionnaire de services est un meilleur foyer pour tout ce qui doit se reconnecter sans surveillance, car il redémarre le client et consigne pourquoi il est mort. Dans tous les cas, attendez un nouveau code après chaque reconnexion : un jeton à usage unique est consommé par la première authentification réussie.

Regardez ce que sudo fait à la séquence. Une demande de mot de passe sudo n'est pas la demande du VPN, et y répondre avec le mot de passe du VPN est la façon la plus courante dont un ordre d'attentes par ailleurs correct échoue encore.

Vérification 5 : le code d'une personne doit-il figurer dans un script ?

C'est la vérification que les gens sautent. Un jeton appartient à une personne, tandis qu'un tunnel qui doit monter selon un calendrier appartient à la machine.

Si une tâche doit se reconnecter sans surveillance, demandez si un certificat de machine ou un compte de service peut remplacer la connexion interactive. Automatiser le code à usage unique de quelqu'un transforme un second facteur en secret stocké, ce qui est un changement de politique et non une commodité de script. Là où le service prend en charge les deux, choisissez l'identifiant qui correspond à la façon dont la tâche s'exécute réellement. Écrivez la réponse, car la personne suivante demandera pourquoi le script détient un jeton.

À quoi ressemble un script qui fonctionne

Le script imprime l'étape atteinte, jamais le secret, et sort avec un code non nul à toute attente échouée.

Testez-le en le cassant exprès : mauvais mot de passe, mauvais groupe, jeton expiré. Chaque échec devrait s'arrêter à une étape nommée au lieu de rester bloqué. Exécutez-le ensuite une fois contre le vrai service et vérifiez le tunnel depuis l'extérieur du script, pas depuis l'intérieur.

Articles similaires