非交互式 SSH 密码身份验证
Non-interactive SSH password authentication

原始链接: https://vincent.bernat.ch/en/blog/2023-sshpass-without-sshpass

SSH 通常依赖于传统的密码身份验证,但它存在安全缺陷,导致这些凭据容易被恶意实体拦截。 幸运的是,基于公钥的身份验证通常被认为比传统选项更安全,但由于供应商在将密钥与用户关联方面的限制以及有限的可扩展性,实施无密码解决方案已被证明具有挑战性。 此外,通过 TACACS+ 和 Radius 整合细粒度授权技术仍然是一项正在进行的工作。 本文探讨了各种方法,包括使用密码管理器和脚本,它们支持交互式和非交互式 SSH 会话,同时降低密码的可见性和可访问性。 它强调了具体的挑战,例如解析 SSH 输出或为不同主机创建多个密码条目,但也提出了解决这些问题的潜在改进和替代方案。 总体而言,无密码 SSH 身份验证继续为寻求更高安全性和简化管理的组织提供相当大的好处。

是的,这似乎是 ChatGPT 基于 Reddit 上有关非交互式和自动化 SSH 技术的讨论生成的摘要。 讨论内容包括密码保护、脚本和自动化、故障排除以及与 Expect/TCL、Ansible、Puppet 和 SaltStack 等替代方案的比较等主题。 讨论了开发和部署过程中面临的挑战以及可能的解决方案和方法。 此外,还解决了与扩展、并发性和高可用性相关的注意事项。 总的来说,这次讨论为使用 SSH 设计和实施有效且高效的自动化解决方案提供了见解和建议,重点是安全性、可靠性和可用性。
相关文章

原文

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 equipments.1

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 
#!$SHELL
pass show $passname | head -1
EOF
  chmod u+x $helper
  SSH_ASKPASS=$helper SSH_ASKPASS_REQUIRE=force command ssh "$@"
}

If the password is incorrect, we can display a prompt on the second tentative:

ssh() {
  set -o localoptions -o localtraps
  local passname=network/ssh/password
  local helper=$(mktemp)
  trap "command rm -f $helper" EXIT INT
  > $helper 
#!$SHELL
if [ -k $helper ]; then
  {
    oldtty=\$(stty -g)
    trap 'stty \$oldtty  /dev/null' EXIT INT TERM HUP
    stty -echo
    print "\rpassword: "
    read password
    printf "\n"
  } > /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 "$@"
}

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 
#!$SHELL
source ${(%):-%x}
ssh "\$@"
EOF
  command scp -S $helper "$@"
}

For the complete code, have a look at my zshrc. As an alternative, you can put the ssh() function body into its own script file and replace command ssh with /usr/bin/ssh to avoid an unwanted recursive call. In this case, the scp() function is not needed anymore.

Update (2023-12)

This post was heavily discussed on Hacker News.


  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. ↩︎

  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. ↩︎

  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. ↩︎

联系我们 contact @ memedata.com