26

I have an expect script that connects to a few routers through ssh. All these routers have the same password (I know, it's wrong), and the script needs to know that password in order to be able to connect to the routers. Currently, the password is passed to my script as an argument on the command line, but this means that there's a trace of that password in my .bash_history file as well as in the running processes. So instead I would like the user to be prompted for a password, if possible silently.

Do you know whether or not it's possible to prompt the user for a password with expect?

Thank you.

Edit: if I was connecting to servers instead of routers, I would probably use ssh keys instead of passwords. But the routers I'm using just support passwords.

MiniQuark
  • 46,633
  • 36
  • 147
  • 183

3 Answers3

49

Use expect's stty command like this:

# grab the password
stty -echo
send_user -- "Password for $user@$host: "
expect_user -re "(.*)\n"
send_user "\n"
stty echo
set pass $expect_out(1,string)

#... later
send -- "$pass\r"

Note that it's important to call stty -echo before calling send_user -- I'm not sure exactly why: I think it's a timing issue.

expect programmers should all read the book: Exploring Expect by Don Libes

glenn jackman
  • 238,783
  • 38
  • 220
  • 352
  • 2
    Cool thanks! It's amazing how intolerant this language is: I wrote "set pass $expect_out(1, string)", with a space before the word "string", and it bombs. I can't find much documentation either. I wish there was some other solution than expect. Anyway, thanks a lot. – MiniQuark Mar 26 '09 at 08:32
  • 1
    "can't find much documentation" ?!? There's a whole book, which is generally considered so well written that it has not needed a second edition. Seriously, check that book out. – glenn jackman Mar 30 '09 at 13:10
  • 2
    Re: a space between "1," and "string" -- Tcl (and hence expect) does not have multidimensional arrays. The array key is just a string, and "1,string" and "1, string" are different. – glenn jackman Mar 30 '09 at 13:10
  • 3
    The 'ssty -echo' is so the password that you enter isn't echo'ed from the cli. It is emulating the behavior of ssh w/out expect. – frogstarr78 Aug 17 '11 at 18:15
9

OK, merging the 2 answers above (or below or wherever they are now!):

#!/usr/bin/expect
log_user 0
set timeout 10
set userid  "XXXXX"
set pass    "XXXXXX"

### Get two arguments - (1) Host (2) Command to be executed
set host    [lindex $argv 0] 
set command [lindex $argv 1]

# grab the password
stty -echo
send_user -- "Password for $userid@$host: "
expect_user -re "(.*)\n"
send_user "\n"
stty echo
set pass $expect_out(1,string)

spawn /usr/bin/ssh -l $userid $host
match_max [expr 32 * 1024]

expect {
    -re "RSA key fingerprint" {send "yes\r"}
    timeout {puts "Host is known"}
}

expect {
     -re "username: " {send "$userid\r"} 
     -re "(P|p)assword: " {send "$pass\r"}
     -re "Warning:" {send "$pass\r"}
     -re "Connection refused" {puts "Host error -> $expect_out(buffer)";exit}
     -re "Connection closed"  {puts "Host error -> $expect_out(buffer)";exit}
     -re "no address.*" {puts "Host error -> $expect_out(buffer)";exit}

     timeout {puts "Timeout error. Is host down or unreachable?? ssh_expect";exit}
}

expect {
   -re "\[#>]$" {send "term len 0\r"}
   timeout {puts "Error reading prompt -> $expect_out(buffer)";exit}
}


expect {
   -re "\[#>]$" {send "$command\r"}

   timeout {puts "Error reading prompt -> $expect_out(buffer)";exit}
}

expect -re "\[#>]$"
set output $expect_out(buffer)
send "exit\r"
puts "$output\r\n"

Note that I changed the $password variable to $pass to be consistent with the other answer.

dr-jan
  • 2,124
  • 2
  • 21
  • 22
  • This answer can be improved in a number of ways. For starters most people will not be running expect from /usr/local/bin and even fewer people will be running ssh from /usr/local/bin. The set password at the top of the file does nothing. The send_user statement of $user@$host has undefined variables which causes my expect interpreter to fail. Also most people ssh to a "host" not a "device" Happy to see this answer fixed up if not I suppose I could put up a working version. – f3xy Aug 20 '18 at 18:13
0

Alternatively you could let ssh collect the password via X11 using the SSH_ASKPASS environment variable.

From the man page:

> SSH_ASKPASS
>     If ssh needs a passphrase, it will read the passphrase from the
>     current terminal if it was run from a terminal.  If ssh does not
>     have a terminal associated with it but DISPLAY and SSH_ASKPASS
>     are set, it will execute the program specified by SSH_ASKPASS
>     and open an X11 window to read the passphrase.  This is particularly
>     useful when calling ssh from a .xsession or related script.
>     (Note that on some machines it may be necessary to redirect the
>     input from /dev/null to make this work.)
qneill
  • 1,643
  • 14
  • 18