https://vincent.bernat.ch/en/blog/2023-sshpass-without-sshpass Non-interactive SSH password authentication Vincent Bernat November 5, 2023 Also available in * francais Filed under * programming > shell ^1 First, some vendors make it difficult to associate an SSH key with a user. Then, many vendors do not support certificate-based authentication, making it difficult to scale. Finally, interactions between public-key authentication and finer-grained authorization methods like TACACS+ and Radius are still uncharted territory. SSH offers several forms of authentication, such as passwords and public keys. The latter are considered more secure. However, password authentication remains prevalent, particularly with network equipment.^1 ^2 The clear-text password never appears on the command line, in the environment, or on the disk, making it difficult for a third party without elevated privileges to capture it. On Linux, Zsh provides the password through a file descriptor. A classic solution to avoid typing a password for each connection is sshpass, or its more correct variant passh. Here is a wrapper for Zsh , getting the password from pass, a simple password manager:^2 pssh() { passh -p <(pass show network/ssh/password | head -1) ssh "$@" } compdef pssh=ssh This approach is a bit brittle as it requires to parse the output of the ssh command to look for a password prompt. Moreover, if no password is required, the password manager is still invoked. Since OpenSSH 8.4, we can use SSH_ASKPASS and SSH_ASKPASS_REQUIRE instead: ssh() { set -o localoptions -o localtraps local passname=network/ssh/password local helper=$(mktemp) trap "command rm -f $helper" EXIT INT > $helper < $helper < /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 "$@" } ^3 To decipher the fourth line, you may get help from print -l and the zshexpn(1) manual page. details is an associative array defined from an array alternating keys and values. A possible improvement is to use a different password entry depending on the remote host:^3 ssh() { # Grab login information 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} # Get password name 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 # No password name? Just use regular SSH [[ -z $passname ]] && { command ssh "$@" return $? } # Invoke SSH with the helper for SSH_ASKPASS # [...] } It is also possible to make scp invoke our custom ssh function: scp() { set -o localoptions -o localtraps local helper=$(mktemp) trap "command rm -f $helper" EXIT INT > $helper <