我使用以下语法将公钥复制到主机,以便能够在没有密码查询的情况下登录到主机:
ssh-copy-id $hostname
其中$hostname
是具有用户名的系统的主机名,例如[email protected]
。但是,此命令至少需要一个密码查询,有时需要另外一种类型的交互:
The authenticity of host 'xxx (xxx)' can't be established.
RSA key fingerprint is xxx.
Are you sure you want to continue connecting (yes/no)?
我试着用expect
来解决我的问题,这是我到目前为止所有的评论和建议:
#!/usr/bin/expect
set timeout 9
set hostname [lindex $argv 0]
spawn ssh-copy-id $hostname
expect {
timeout { send_user "\nFailed to get password prompt\n"; exit 1 }
eof { send_user "\nSSH failure for $hostname\n"; exit 1 }
"*re you sure you want to continue connecting" {
send "yes\r"
exp_continue
}
"*assword*" {
send "fg4,57e4h\r"
}
}
这可以正确地“捕获”第一次交互,但不是第二次交互。似乎正在使用正确的密码(fg4,57e4h),但是当我尝试登录主机时,仍然需要输入密码。我还检查了.ssh/authorized_hosts
没有进入。使用过的密码也是绝对正确的,因为我可以将其复制并粘贴到登录中。该脚本不会产生任何错误,但会生成以下exp_internal 1
输出:
./expect_keygen XXX
spawn ssh-copy-id XXX
parent: waiting for sync byte
parent: telling child to go ahead
parent: now unsynchronized from child
spawn: returns {3602}
expect: does "" (spawn_id exp6) match glob pattern "*re you sure you want to continue connecting"? no
"*assword*"? no
XXX's password:
expect: does "XXX's password: " (spawn_id exp6) match glob pattern "*re you sure you want to continue connecting"? no
"*assword*"? yes
expect: set expect_out(0,string) "XXX's password: "
expect: set expect_out(spawn_id) "exp6"
expect: set expect_out(buffer) "XXX's password: "
send: sending "fg4,57e4h\r" to { exp6 }
虽然我既不是tcl也不是期望专家,但似乎期望将正确的字符串(即密码)发送到ssh-copy-id
命令。但仍然存在问题,因为上述expect命令不会将公钥复制到主机。
我想分享我的tcl / expect脚本。它运作得很好。
#!/usr/bin/env tclsh
package require Expect
set prompt {[$❯#] }
set keyfile "~/.ssh/id_rsa.pub"
set needcopy 0
if {![file exists $keyfile]} {
spawn ssh-keygen
interact
}
spawn ssh $argv
expect {
{continue connecting (yes/no)?} {
send "yes\r"
exp_continue
}
{[Pp]ass*: } {
set needcopy 1
interact "\r" {
send "\r"
exp_continue
}
}
$prompt
}
if {$needcopy} {
set fd [open $keyfile]
gets $fd pubkey
close $fd
send " mkdir -p ~/.ssh\r"
expect $prompt
send " cat >> ~/.ssh/authorized_keys <<EOF\r$pubkey\rEOF\r"
expect $prompt
}
interact
在正常情况下,SSH工具链会从终端询问密码,而不是来自stdin。您可以provide custom SSH_ASKPASS program用它来推送您的密码。
创建一个简单的脚本askpass.sh:
#!/bin/sh
echo $PASSWORD
然后配置它在ssh中使用:
chmod a+x askpass.sh
export SSH_ASKPASS=askpass.sh
最后运行ssh-copy-id(不期望):
export DISPLAY=:0
PASSWORD=mySecurePassword setsid ssh-copy-id -o StrictHostKeyChecking=no hishost.thatwas.secure.com
setsid与终端分离(ssh然后会出现恐慌并查找askpass程序)DISPLAY也会被ssh检查(它认为你的askpass是一个GUI)
请注意,此方法可能存在隐藏的安全漏洞。
这应该可以解决您的问题。
#!/usr/bin/expect
set timeout 9
set hostname [lindex $argv 0]
spawn ssh-copy-id $hostname
expect {
timeout { send_user "\nFailed to get password prompt\n"; exit 1 }
eof { send_user "\nSSH failure for $hostname\n"; exit 1 }
"*re you sure you want to continue connecting" {
send "yes\r"
exp_continue
}
"*assword*" {
send "fg4,57e4h\r"
interact
exit 0
}
}
你看到的错误是因为spawn
没有使用shell来执行命令。如果你想要shell控制字符,你需要生成一个shell:
spawn sh -c "cat $home/.ssh/id_rsa.pub | ssh $hostname 'cat >> $home/.ssh/authorized_keys'"
但是,我认为ssh-copy-id
会问你同样的问题,所以这应该是一个简单的替代品:
spawn ssh-copy-id $hostname
如果您可能看到或未看到“继续连接”提示,则需要使用exp_continue
进行嵌套预期
spawn ssh-copy-id $hostname
expect {
timeout { send_user "\nFailed to get password prompt\n"; exit 1 }
eof { send_user "\nSSH failure for $hostname\n"; exit 1 }
"*re you sure you want to continue connecting" {
send "yes\r"
exp_continue
}
"*assword*" {
send "mysecretpassword\r"
}
}
如果你使用expect
的方法失败,你仍然可以尝试sshpass。