在 Bash 脚本中使用 Expect 为 SSH 命令提供密码

Posted

技术标签:

【中文标题】在 Bash 脚本中使用 Expect 为 SSH 命令提供密码【英文标题】:Use Expect in a Bash script to provide a password to an SSH command 【发布时间】:2011-06-14 10:22:42 【问题描述】:

我正在尝试在 Bash 脚本中使用 expect 来提供 SSH 密码。提供密码是可行的,但我并没有像我应该的那样进入 SSH 会话。它可以追溯到 Bash。

我的脚本:

#!/bin/bash

read -s PWD

/usr/bin/expect <<EOD
spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no usr@$myhost.example.com'
expect "password"
send "$PWD\n"
EOD
echo "you're out"

我的脚本的输出:

spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no usr@$myhost.example.com
usr@$myhost.example.com's password: you're out

我想要我的 SSH 会话,并且只有在我退出它时才能返回到我的 Bash 脚本。

我之所以在expect 之前使用 Bash,是因为我必须使用菜单。我可以选择要连接的单元/设备。

想要回复我应该使用 SSH 密钥的人,请弃权。

【问题讨论】:

请看第一行:想要回复我应该使用SSH密钥的人请弃权 我会编辑你的第一行以更友好一点。您可能会考虑类似“由于限制,我根本无法使用 SSH 密钥,我必须找到一种方法让它与期望一起工作”。您应该期望人们可能会自然而然地好奇您为什么不使用密钥,并且只是想提供帮助:) @Ignacio 没有建议您使用它们,他只是确认它为约束而不是疏忽。 在这种情况下我会尝试使用 kermit。它有一个非常健壮的脚本语言columbia.edu/kermit/skermit.html#scripts This page helped me a lot 【参考方案1】:

混合使用 Bash 和 Expect 并不是达到预期效果的好方法。我会尝试只使用 Expect:

#!/usr/bin/expect
eval spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no usr@$myhost.example.com

# Use the correct prompt
set prompt ":|#|\\\$"
interact -o -nobuffer -re $prompt return
send "my_password\r"
interact -o -nobuffer -re $prompt return
send "my_command1\r"
interact -o -nobuffer -re $prompt return
send "my_command2\r"
interact

bash 的示例解决方案可能是:

#!/bin/bash
/usr/bin/expect -c 'expect "\n"  eval spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no usr@$myhost.example.com; interact '

这将等待 Enter 然后返回(片刻)交互式会话。

【讨论】:

效果很好,谢谢。如果我想通过 SSH 登录后输入命令,我需要做什么? 这个脚本应该返回带有登录用户的非交互式shell。我不明白问题。如果您绝对想在 bash 中使用相同的脚本,请查看已编辑的条目。 与我在提示时发送密码的方式相同,我想在登录后发送系统命令。 示例代码已添加到上面的帖子中。当然,这将一直有效,直到 my_commandX 不更改返回的提示,如果发生这种情况,应更改提示变量。 @pietrushnic 你能解释一下为什么使用“interact -o -nobuffer -re $prompt return”而不是“expect $prompt”吗?后者看起来更常用..【参考方案2】:

最简单的方法是使用 sshpass。这在Ubuntu/Debian 存储库中可用,您不必处理将期望与 Bash 集成。

一个例子:

sshpass -p<password> ssh <arguments>
sshpass -ptest1324 ssh user@192.168.1.200 ls -l /tmp

上述命令可以很容易地与 Bash 脚本集成。

注意:请阅读man sshpass 中的安全注意事项部分,以全面了解安全隐患。

【讨论】:

我不知道这是否是一个好的解决方案,但它确实简化了很多。谢谢 从安全角度来看非常危险——命令行参数可以被系统上的任何其他进程读取。有可能覆盖它们,希望sshpass 能做到这一点,但即便如此,当密码可供任何/每个进程查看时,它仍然需要一段时间才能执行此操作。 @CharlesDuffy 当然,你是对的。 sshpass 用于在安全不是首要问题的本地网络环境中执行简单测试脚本的场景。事实上,man sshpass 中有一个部分解释了安全注意事项的整个部分。添加这个来回答,谢谢。 @erikb85 通常,一个包会为您完成所有肮脏的工作,但在所有情况下,这些脚本都是为这种用途而构建的,因此比添加您自己的东西要好。这条评论是关于不要重新发明***。只有在没有人处理过困难的事情时才处理它。 sshpass 是个好功能。 为了完整起见,我提到“man sshpass”向潜在用户提供适当的安全警告,指出“-p”是最不安全的使用方式,并提供“-e”通过环境变量获取密码的选项,这至少使其脱离命令行。【参考方案3】:

在 EOD 之前添加“交互”Expect 命令:

#!/bin/bash

read -s PWD

/usr/bin/expect <<EOD
spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no usr@$myhost.example.com
expect "password"
send -- "$PWD\r"
interact
EOD
echo "you're out"

这应该让您与远程计算机交互,直到您注销。然后你会回到 Bash。

【讨论】:

它进入并立即退出。打印出“you're out”。 我已将“interact”和“EOD”替换为“expect eof”,它对我有用。这是在 Mac 上。 此页面上的所有答案都不适合我,但@Emmanuel 使用expect eof 的建议解决了这个问题。 usr@$myhost.example.com' 中删除',它应该可以工作。也许你需要用\r替换\n,但是YMMV 请考虑添加this fix,否则某些密码可能无法使用【参考方案4】:

在寻找这个问题的答案几个月后,我终于找到了一个真正最好的解决方案:编写一个简单的脚本。

#!/usr/bin/expect

set timeout 20

set cmd [lrange $argv 1 end]
set password [lindex $argv 0]

eval spawn $cmd
expect "assword:"   # matches both 'Password' and 'password'
send -- "$password\r"; # -- for passwords starting with -, see https://***.com/a/21280372/4575793
interact

发给/usr/bin/exp,就可以使用了:

exp &lt;password&gt; ssh &lt;anything&gt; exp &lt;password&gt; scp &lt;anysrc&gt; &lt;anydst&gt;

完成!

【讨论】:

对于带有两个参数的 scp 来说也很棒。当然,ssh 也可以有 2 个参数。【参考方案5】:

一个简单的 Expect 脚本:

文件 Remotelogin.exp

    #!/usr/bin/expect
    set user [lindex $argv 1]
    set ip [lindex $argv 0]
    set password [lindex $argv 2]
    spawn ssh $user@$ip
    expect "password"
    send "$password\r"
    interact

例子:

./Remotelogin.exp <ip> <user name> <password>

【讨论】:

【参考方案6】:

还要确保使用

send -- "$PWD\r"

相反,以破折号 (-) 开头的密码将失败。

上面不会解释以破折号开头的字符串作为发送命令的选项。

【讨论】:

【参考方案7】:

使用辅助工具 fd0ssh(来自 hxtools、source for ubuntu、source for openSUSE,而不是 pmt)。它无需期待来自ssh 程序的特定提示即可工作。

它也“比在命令行上像 sshpass 那样传递密码安全得多”(-Charles Duffy 的评论)。

【讨论】:

这太棒了!让它运行有点困难(至少在 ubuntu 服务器中)但运行顺利!我们所做的配置:echo "yoursshpass" | fd0ssh ssh -c -L$port:$ip:$remote_port user@yourserver.com &amp; 谢谢!【参考方案8】:

我发现从 Bash 脚本中使用 small Expect 脚本的另一种方法如下。

...
Bash script start
Bash commands
...
expect - <<EOF
spawn your-command-here
expect "some-pattern"
send "some-command"
...
...
EOF
...
More Bash commands
...

这是因为...If the string "-" is supplied as a filename, standard input is read instead...

【讨论】:

这里不需要-,因为expect默认从stdin读取,如果你不带参数调用它。但是,如果您想在没有命令提示符的情况下以交互方式运行它(expectexpect -),或者如果您执行类似expect -f "$@" 的操作,其中第一个参数应该是一个文件,即使它看起来像一个选项,它也很有用(以- 开头)。在这种情况下,如果$1(给定的文件)是-,则读取自stdin【参考方案9】:

sshpass 如果您尝试在 Makefile 中的 Sublime Text 构建目标中使用它,则会损坏它。而不是sshpass,you can use passh

使用sshpass 你会这样做:

sshpass -p pa$$word ssh user@host

使用passh 你会这样做:

passh -p pa$$word ssh user@host

注意:不要忘记使用-o StrictHostKeyChecking=no。否则,连接将在您第一次使用时挂起。例如:

passh -p pa$$word ssh -o StrictHostKeyChecking=no user@host

参考资料:

    Send command for password doesn't work using Expect script in SSH connection How can I disable strict host key checking in ssh? How to disable SSH host key checking scp without known_hosts check pam_mount and sshfs with password authentication

【讨论】:

以上是关于在 Bash 脚本中使用 Expect 为 SSH 命令提供密码的主要内容,如果未能解决你的问题,请参考以下文章

如何通过 bash 脚本将 ssh 密钥添加到主机

使用expect实现ssh免密码登陆

expect实现自动scp和ssh登录后执行命令,并向bash提供返回值

脚本_使用expect自动交互远程主机安装软件

通过 ssh 连接到远程并使用 expect 运行 git 命令

expect实现自动scp和ssh登录后执行命令,并向bash提供返回值