在Bash脚本中提供密码给ssh命令,不使用公钥和Expect。

21

我想在脚本中使用SSH,但该脚本不会在我的计算机上执行。

我的实现有两个限制。

  • 我不能超出shell的标准,因此我不能使用expect,因为我不知道它是否可用于此计算机。
  • 我不能指望这台计算机拥有SSH公钥

有哪些可能的选项和解决方案?

如何以自动化和安全的方式提供所需密码给ssh而不添加额外的依赖项?

是否可以在脚本内部提供密码?

提前感谢大家:)


其他编程语言如Perl或Python是否可用? - Bruce Barnett
通常是可以的,但在Python中我知道这是可能的 :) 无论如何,谢谢。 - stratis
5个回答

29

安装sshpass,然后运行以下命令:

sshpass -p "yourpassword" ssh -o StrictHostKeyChecking=no yourusername@hostname

非常感谢,但我不能期望我将运行脚本的计算机已安装sshpass,这就是我不能使用expect的原因。 - stratis

17

出于安全考虑,您必须避免在命令行中提供密码,否则运行ps命令的任何人都可以看到您的密码。最好使用sshpass实用工具,例如:

#!/bin/bash

export SSHPASS="your-password"
sshpass -e ssh -oBatchMode=no sshUser@remoteHost

您可能对如何从Bash脚本中使用密码运行sftp命令?感兴趣。


非常感谢,但我不能期望要运行脚本的计算机已经安装了sshpass,这就是我不能使用expect的原因。 - stratis
是的,它可能尚未安装,但您可以从提供的链接下载并自行编译。它不需要任何管理员权限。 - anubhava
我需要在每台想要运行脚本的计算机上都下载它吗?如果可以直接实现到我的代码中,那就没问题了,但我只想创建一个独立的脚本,它能够在任何地方运行而不需要权限或额外的软件包。这就是我想保持标准的原因。 - stratis

12
首先:除非您知道为什么这样做是安全的(即您已评估攻击者知道秘密可能造成的损害),否则不要将秘密放在明文中。
如果您愿意在脚本中放置秘密,可以使用ssh密钥并在ssh-agent shell中执行。
#!/usr/bin/env ssh-agent /usr/bin/env bash
KEYFILE=`mktemp`
cat << EOF > ${KEYFILE}
-----BEGIN RSA PRIVATE KEY-----
[.......]
EOF
ssh-add ${KEYFILE}

# do your ssh things here...

# Remove the key file.
rm -f ${KEYFILE}

使用ssh密钥的好处是可以轻松使用强制命令来限制密钥持有者在服务器上的操作。
更安全的方法是让脚本运行ssh-keygen -f ~/.ssh/my-script-key来创建专门用于此目的的私钥,但这样您还需要添加公钥到服务器的例程。

4
我认为生成私钥以供脚本使用并在使用后删除是唯一的解决方案。非常感谢。 - stratis
这是对我的问题最接近的答案 :) 谢谢 - stratis

2

我完全同意所有认为这几乎肯定是一个非常糟糕的主意的人。然而,如果你已经评估了你的需求并想知道如何做到这一点,在不使用密钥和安装任何其他程序的情况下,只需使用Bourne Shell机制是完全可能的

在评估了安全风险之后,请自行承担风险

答案

创建一个名为/path/to/saypass的程序,输出密码,例如以下最简脚本:

#!/bin/sh
echo 'secret'

使用以下命令使其可执行
chmod +x /path/to/saypass

这是主要命令:
SSH_ASKPASS="/path/to/saypass" DISPLAY=anything setsid ssh username@hostname [farcommand]

简而言之,这个程序以一种特殊的方式运行ssh,它使用你的saypass程序来获取密码。(将/path/to替换为你脚本的路径;anything可以是字面上的这个字符串;将username@hostname替换为你的详细信息。)
详细来说:
- 设置两个环境变量SSH_ASKPASSDISPLAY - 然后运行setsid - setsid再运行没有控制终端的ssh - ssh连接到远程hostname - 在本地运行saypass获取密码 - 将密码发送给远程服务器 - 假设密码正确 - 运行farcommand(如果提供了)或者一个交互式shell
我通常使用datehostname来测试可选的farcommand
有很多地方可能出错。
解释
这个技巧是,标准的Linux命令行ssh有一些环境变量可以用来选择一个程序来提供密码。这个设计是为了与X11用户界面一起使用的,但也可以用于我们的目的。 ssh(1)手册页上说:
SSH_ASKPASS如果ssh需要密码短语,它将从当前终端读取密码短语,如果它是从终端运行的。如果ssh没有与之关联的终端,但设置了DISPLAYSSH_ASKPASS,它将执行由SSH_ASKPASS指定的程序,并打开一个X11窗口来读取密码短语。

所以:你需要一个输出密码的程序(shell脚本或其他任何类型)。然后你需要说服ssh使用它:
  • SSH_ASKPASS设置为/path/to/saypass
  • DISPLAY设置为一些愚蠢的东西
  • 没有控制终端(这就是setsid的作用)

你可以将它们组合在以下的sh命令中:

SSH_ASKPASS="/path/to/saypass" DISPLAY=anything setsid ssh username@hostname [command]

ssh将执行以下两个命令之一:

/path/to/saypass "Password:"
/path/to/saypass "username@hostname's password:"

指纹检查
如果需要指纹,请在通常显示消息的位置查看。
The authenticity of host '*hostname* (*ipaddress*)' can't be established.
ECDSA key fingerprint is SHA256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.
Are you sure you want to continue connecting (yes/no)? 

然后ssh会像这样运行您的命令:
/path/to/saypass "Please type 'yes' or 'no':"

全能脚本

以下是一个用于创建、使用和删除主脚本中的saypass的单一脚本。大家都会告诉你不要将明文密码放在文件中,也永远不要硬编码密码。他们这样告诉你是有充分理由的:它会给你带来很多麻烦。请自行承担风险。

#!/bin/sh

echo "#!/bin/sh\necho 'secret';rm -f /tmp/saypass.$$" > /tmp/saypass.$$
chmod 775 /tmp/saypass.$$
SSH_ASKPASS="/tmp/saypass.$$" DISPLAY=anything setsid ssh "$@"

SCP

这也适用于scp,在ssh之上的复制程序:

SSH_ASKPASS=/path/to/saypas DISPLAY=anything setsid scp username@hostname:/path/to/farfile .

注意

除非在非常紧急的情况下,千万不要使用这个方法,比如当你有数百台电脑且无法安装任何像是ssh密钥、sshpass甚至expect之类的东西。

如果必须使用这种技术,请将密码放在一个文件中,并让saypass从那里获取。 尽可能保护好saypass的安全性;你可以通过环境变量将信息传递给它。

我不知道man页面上所说的"打开X11窗口"是什么意思,在我的测试中没有发生这样的事情。

测试环境:

  • OpenSSH_6.6.1p1 Ubuntu-2ubuntu2, OpenSSL 1.0.1f 6 Jan 2014 on Ubuntu 14.04.1 LTS
  • OpenSSH_7.2p2 Ubuntu-4ubuntu2.1, OpenSSL 1.0.2g 1 Mar 2016 on Ubuntu 16.04.2 LTS
  • OpenSSH_7.6p1 Ubuntu-4ubuntu0.3, OpenSSL 1.0.2n 7 Dec 2017 on Ubuntu 18.04.5 LTS

1
据我所知,除非使用密钥或者使用命令行版本的ssh,否则没有其他可能性。但是大多数编程语言都有库绑定,比如C、Python、PHP等等。您可以使用这些语言编写程序,以便自动传递密码。但是请注意,这当然是一个安全问题,因为密码将以明文形式存储在该程序中。

谢谢你的帮助,至少让我知道了什么是可能的。我考虑用Python开发,但在Python中使用SSH命令的代码行数比Shell版本的SSH多100倍 :) - stratis
问题在于ssh命令期望从终端输入密码。这就是为什么需要使用expect来模拟终端。也许有一种简单的方法可以做到同样的事情,但我不知道怎么做。不过你可以通过谷歌搜索找到100行代码实现相同的功能。 - hek2mgl

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接