Authentification SSH non interactive par mot de passe
Vincent Bernat
SSH offre plusieurs formes d’authentification, telles que par mot de passe et par clé publique. Cette dernière est considérée comme plus sécurisée. Cependant, l’authentification par mot de passe reste répandue, notamment avec les équipements réseau1.
Une solution classique pour éviter de taper un mot de passe à chaque connexion est sshpass, ou sa variante plus correcte passh. Voici une fonction pour Zsh, récupérant le mot de passe depuis pass, un gestionnaire de mots de passe2 :
pssh() { passh -p <(pass show network/ssh/password | head -1) ssh "$@" } compdef pssh=ssh
Cette approche est un peu fragile car elle nécessite d’examiner la sortie de la
commande ssh
pour y chercher une invite de mot de passe. De plus, si aucun mot
de passe n’est requis, le gestionnaire de mots de passe est tout de même
invoqué. Depuis OpenSSH 8.4, nous pouvons à la place utiliser SSH_ASKPASS
et SSH_ASKPASS_REQUIRE
:
ssh() { set -o localoptions -o localtraps local passname=network/ssh/password local helper=$(mktemp) trap "command rm -f $helper" EXIT INT > $helper <<EOF #!$SHELL pass show $passname | head -1 EOF chmod u+x $helper SSH_ASKPASS=$helper SSH_ASKPASS_REQUIRE=force command ssh "$@" }
Si le mot de passe est incorrect, nous pouvons afficher une invite de mot de passe à la seconde tentative :
ssh() { set -o localoptions -o localtraps local passname=network/ssh/password local helper=$(mktemp) trap "command rm -f $helper" EXIT INT > $helper <<EOF #!$SHELL if [ -k $helper ]; then { oldtty=\$(stty -g) trap 'stty \$oldtty < /dev/tty 2> /dev/null' EXIT INT TERM HUP stty -echo print "\rpassword: " read password printf "\n" } > /dev/tty < /dev/tty printf "%s" "\$password" else pass show $passname | head -1 chmod +t $helper fi EOF chmod u+x $helper SSH_ASKPASS=$helper SSH_ASKPASS_REQUIRE=force command ssh "$@" }
Une amélioration possible est d’utiliser une entrée de mot de passe différente en fonction de l’hôte distant3 :
ssh() { # Informations de connexion local -A details details=(${=${(M)${:-"${(@f)$(command ssh -G "$@" 2>/dev/null)}"}:#(host|hostname|user) *}}) local remote=${details[host]:-details[hostname]} local login=${details[user]}@${remote} # Nom du mot de passe local passname case "$login" in admin@*.example.net) passname=company1/ssh/admin ;; bernat@*.example.net) passname=company1/ssh/bernat ;; backup@*.example.net) passname=company1/ssh/backup ;; esac # Sans nom de mot de passe, on se rabat sur ssh [[ -z $passname ]] && { # Just regular ssh... command ssh "$@" return $? } # Invocation de SSH avec SSH_ASKPASS # […] }
Il est également possible d’adapter scp
pour qu’il utilise notre fonction
ssh
:
scp() { set -o localoptions -o localtraps local helper=$(mktemp) trap "command rm -f $helper" EXIT INT > $helper <<EOF #!$SHELL source ${(%):-%x} ssh "\$@" EOF command scp -S $helper "$@" }
Le code complet est disponible dans mon zshrc. Il est également possible de
placer le contenu de la fonction ssh()
dans un script séparé et de remplacer
command ssh
par /usr/bin/ssh
pour éviter un appel récursif. Dans ce cas, la
fonction scp()
n’est plus nécessaire.
Mise à jour (12.2023)
Cet article a été longuement débattu sur Hacker News.
-
Tout d’abord, certains constructeurs rendent difficile l’association d’une clé SSH à un utilisateur. Ensuite, de nombreux constructeurs ne prennent pas en charge l’authentification basée sur un certificat, ce qui complique la mise en place à grande échelle. Enfin, les interactions entre l’authentification par clé publique et des méthodes d’autorisation plus fines telles que TACACS+ et Radius restent un domaine inexploré. ↩︎
-
Le mot de passe en clair n’apparaît jamais sur la ligne de commande, dans l’environnement ou sur le disque, rendant difficile sa capture par un tiers non privilégié. Sur Linux, Zsh fournit le mot de passe via un descripteur de fichier. ↩︎
-
Pour comprendre la magie noire derrière la quatrième ligne, vous pouvez vous aider de
print -l
et de la page de manuel zshexpn(1).details
est un tableau associatif défini à partir d’un tableau alternant clefs et valeurs. ↩︎