How to use 'expect' to copy a public key to a host?

Alex picture Alex · Oct 16, 2013 · Viewed 7.1k times · Source

I am using the following syntax to copy a public key to a host, in order to be able to log in afterwards to the host without password query:

ssh-copy-id $hostname 

in which $hostname is the hostname of the system with the username, e.g. [email protected]. However, this command requires at least one password query and - sometimes - an additional interaction of the type:

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)?

I tried to solve my problem with expect, and here is what I have so far (with all the comments and suggestions incorporated):

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

}

This works so far as it 'catches' the first interaction correctly, but not the second one. It seems, that the correct password (fg4,57e4h) is being used, but when I try to log in to the host machine, I am still asked for a password. I also checked that no entry in .ssh/authorized_hosts have been made. The used password also is absolutely correct, as I can just copy and paste it to log-in. The script does not create any error, but produces the following exp_internal 1 output:

 ./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 }

Although I am neither tcl nor expect expert, it seems expect sends the correct string (i.e. the password) to the ssh-copy-id command. But still, there must be a problem as the above expect command does not copy the public key to the host.

Answer

Basilevs picture Basilevs · Oct 24, 2013

Under normal conditions SSH toolchain asks the password from terminal, not from stdin. You can provide custom SSH_ASKPASS program to push your password with it.

Create a simple script askpass.sh:

#!/bin/sh
echo $PASSWORD

then configure it to be used in ssh:

chmod a+x askpass.sh
export SSH_ASKPASS=askpass.sh

finally run ssh-copy-id (without expect):

export DISPLAY=:0
PASSWORD=mySecurePassword setsid ssh-copy-id -o StrictHostKeyChecking=no hishost.thatwas.secure.com

setsid detaches from terminal (ssh will then panic and look for askpass program) DISPLAY is also checked by ssh (it thinks your askpass is a GUI)

Note that there might be hidden security vulnerabilities with this approach.