【问题标题】:How to use 'expect' to copy a public key to a host?如何使用“期望”将公钥复制到主机?
【发布时间】:2017-12-22 18:37:46
【问题描述】:

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

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 解决我的问题,这是我目前所拥有的(包含所有 cmets 和建议):

#!/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 命令。但是,由于上述期望命令没有将公钥复制到主机,因此肯定存在问题。

【问题讨论】:

  • 你为什么不用ssh-copy-id?这就是它的用途。
  • 这简化了任务;但它会与我的代码 sn-p 一起工作吗?那么可选的是/否问题呢,这是否也会被处理(我现在无法检查)。
  • 尝试期待脚本末尾的提示。在您的脚本中,生成的过程将在发送密码后立即结束,并且不保证整个 ssh-id-copy 过程完成。

标签: ssh tcl expect


【解决方案1】:

在正常情况下,SSH 工具链从终端询问密码,而不是从标准输入。 可以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 程序) ssh 也检查了 DISPLAY(它认为你的 askpass 是一个 GUI)

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

【讨论】:

    【解决方案2】:

    这应该可以解决您的问题。

    #!/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
        }
    }
    

    【讨论】:

    • 目测、交互和退出 0 已添加到“assword”节中。
    【解决方案3】:

    我想分享我的 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
    

    【讨论】:

    • 哇,我在 4 年前就问过这个问题了。
    • 天哪!中间换了几家公司。我会接受一个答案;-)
    【解决方案4】:

    您看到的错误是由于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"
        }
    }
    

    【讨论】:

    • 您的建议无效。我将spawn 命令替换为使用ssh-copy-id,但expect script asks for a password. Maybe it is the regex? Maybe it is that expect` 等待看到不会出现的“是/否”问题...?
    • exp_internal 1 添加到脚本的顶部,expect 会告诉你发生了什么。
    • 我的想法:期待等待一个模式'*你确定要继续连接'。这种模式并不总是发生。它可能会发生或可能不会发生,这取决于各种因素。脚本需要更改顶部,以便它“预期”只有密码提示或是/否提示和密码提示。如何正确实现这一目标?
    • 还是不行。似乎没有错误,并且密码似乎是正确的('\r' 除外),但是在尝试登录时仍然要求我输入密码。当我手动运行ssh-copy-id 并输入密码时,我可以在之后登录而不会被要求输入密码。所以我猜,脚本还是有问题的……
    • 假设密码是'res,483jf'(不带撇号),我会在脚本中输入:'send "res,483jf\r"'。这是正确的吗?
    【解决方案5】:

    如果您使用expect 的方法失败,您仍然可以尝试sshpass

    【讨论】:

      猜你喜欢
      • 2012-10-19
      • 1970-01-01
      • 1970-01-01
      • 2011-10-22
      • 1970-01-01
      • 2016-01-06
      • 2013-05-14
      • 2020-12-24
      • 1970-01-01
      相关资源
      最近更新 更多