Ssh 如何使用';期望';要将公钥复制到主机?

Ssh 如何使用';期望';要将公钥复制到主机?,ssh,tcl,expect,Ssh,Tcl,Expect,我正在使用以下语法将公钥复制到主机,以便以后能够登录到主机而无需密码查询: ssh-copy-id $hostname 其中,$hostname是带有用户名的系统的主机名,例如root@123.456.789.100。但是,此命令至少需要一个密码查询,有时还需要以下类型的额外交互: The authenticity of host 'xxx (xxx)' can't be established. RSA key fingerprint is xxx. Are you sure you wa

我正在使用以下语法将公钥复制到主机,以便以后能够登录到主机而无需密码查询:

ssh-copy-id $hostname 
其中,
$hostname
是带有用户名的系统的主机名,例如
root@123.456.789.100
。但是,此命令至少需要一个密码查询,有时还需要以下类型的额外交互:

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
解决我的问题,以下是我到目前为止得到的(包括所有评论和建议):

只要它正确地“捕获”第一个交互,而不是第二个交互,它就可以工作。似乎正在使用正确的密码(fg4,57e4h),但当我尝试登录到主机时,仍会要求我输入密码。我还检查了
.ssh/authorized_hosts
中是否没有任何条目。使用的密码也是绝对正确的,因为我可以复制并粘贴它来登录。脚本不会创建任何错误,但会生成以下
exp\u 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专家,也不是expect专家,但expect似乎向
ssh copy id
命令发送了正确的字符串(即密码)。但是,由于上面的expect命令没有将公钥复制到主机上,因此肯定存在问题。

您看到的错误是由于
生成的
没有使用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\u 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"
    }
}

在正常情况下,SSH工具链从终端而不是从stdin请求密码。 你可以用它来推送你的密码

创建一个简单的脚本askpass.sh:

#!/bin/sh
echo $PASSWORD
然后将其配置为在ssh中使用:

chmod a+x askpass.sh
export SSH_ASKPASS=askpass.sh
最后运行ssh copy id(无需预期):

setsid从终端分离(ssh随后将死机并查找askpass程序) 显示也由ssh检查(它认为您的askpass是一个GUI)


请注意,此方法可能存在隐藏的安全漏洞。

如果使用
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"
        interact
        exit 0
    }
}

我想分享我的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
#/usr/bin/env tclsh
包需要期望
设置提示{[$❯#] }
设置密钥文件“~/.ssh/id\u rsa.pub”
设置需要复制0
如果{![file exists$keyfile]}{
生成ssh密钥
互动
}
生成ssh$argv
期待{
{是否继续连接(是/否)}{
发送“是”\r\n
exp\u继续
}
{[Pp]ass*:}{
设置所需副本1
交互“\r”{
发送“\r”
exp\u继续
}
}
$prompt
}
如果{$needcopy}{
设置fd[打开$keyfile]
获取$fd pubkey
收盘价$fd
发送“mkdir-p~/.ssh\r”
期望$prompt

发送“cat>~/.ssh/authorized_key为什么不使用?这就是它的用途。这简化了任务;但它能在不改变的情况下与我的代码片段一起工作吗?关于可选的是/否问题,这也会被处理吗(我现在无法检查)。请尝试在脚本结尾处出现提示。在您的脚本中,生成的过程将在发送密码后立即结束,并且整个ssh id复制过程不保证完成。您的建议无效。我将
spawn
命令替换为使用
ssh copy id
,但
expect脚本要求提供密码d、 也许是正则表达式?也许是因为
expect`等待看到“是/否”问题不会出现…?将
exp_internal 1
添加到脚本顶部,expect将告诉您发生了什么。我的想法是:expect wait for a pattern'*您确定要继续连接吗?这种模式并不总是发生。它可能会发生发生或可能不发生,取决于各种因素。脚本需要更改,以便它“只需要”密码提示或“是/否”提示和密码提示。如何才能正确实现?它仍然无法工作。似乎没有错误,密码似乎正确(除“\r”外),但仍然在尝试登录时,我被要求输入密码。当我手动运行
ssh copy id
并输入密码时,我可以在以后登录,而不需要输入密码。因此,我猜,脚本仍然存在问题……假设密码为'res,483jf'(不带撇号),我会在脚本中输入:“send”res,483jf\r“”。这是正确的吗?在“assword”一节中添加了“眼球观察”、“交互”和“退出0”。哇,我在4年多前问过这个问题。哦,天哪!我在这中间换了几家公司。我会设法接受一个答案;-)
#!/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
    }
}
#!/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