Friday, February 5, 2010

Automating ssh logins (without keys) with 'expect'

You need to automate a task across many machines. You know the user name and password, but for some reason, either by policy or otherwise, you are not allowed to use PKI, or Public Key Infrastructure.

Well, not to give up hope just yet, because there is a nice little tool that is extremely powerful, yet not widely utilized. it's called 'expect'.

Expect does just as you would, uh expect it to do. It will start a process that would normally have interaction, and then you can tell it what to expect in return.

Maybe you have to answer a yes or no question or supply it with a name or password. It's not just limited to that.

You can specify conditions as to what kind of conversation expect would have. Let's use SSH as an example:

Typically you could ssh to a remote server. Simply use the command:

myself@home:~$ ssh myself@somewhere.else.com

You would get this response:

myself@somewhere.else.com's password:

Ok, no problem. I know the password. I type it in and get my shell access. But what if I need to do 200 remote servers? that's a LOT of typing, and I have other things to do.

So, lucily I have expect to the rescue. I would start out writing a shell script, just like other shell scripts, but this time I am going to use expect:

#!/usr/bin/expect

spawn /usr/bin/ssh myself@[lindex $argv 0]

Now we are not finished yet. What we have done so far is to tell expect to run the ssh command as myself to the first argument to the script, which is set up by [lindex $argv 0].We no have to now tell expect that it should get next.

Now if this was our first time connecting to the remote server, ssh may ask us if we want to trust the fingerprint of the remote server to be authentic.

Well, having this across the Internet is a good thing, to keep out pesky man-in-the-middle attacks, but I am am on my cozy private LAN and I trust my machines are where they say they are, so I am going to tell expect that if it sees this query, "Are you sure you want to continue connecting (yes/no)?", to answer 'yes' with a 'enter key' character and continue on:

expect {
-re ".*Are.*.*yes.*no.*" {
send "yes\r"
exp_continue
}

...
}

Now the remote server will prompt me for my password. I will tell expect that once it receives the prompt, to go ahead and send the password, along with sending the ENTER key (r):

"*?assword:*" {
send "MyPaSsWoRd"
send "\r"
interact
}

Once the password is sent, the 'interact' command will then return control of the shell to me and I could take it from there. I could also tell expect to send a command like 'uname -a' for example and just exit. But I will leave it as is for now.

Security note*
If you are using expect for this type of automation, storing passwords in the script itself is dangerous and open to prying eyes.

You may want to keep your password in a separate file. You can tell expect to read from the file for the password

set tmp [open /home/myself/.secret]
set password [read $tmp]
close $tmp

create the file /home/myself/.secret (Changing the path to whatever you desire, as long as you can get to it) and set the perms to 400

echo "MyPaSsWoRd" > /home/myself/.secret
chmod 400 /home/myself/.secret

Then in the expect script, you would

send $password
send "\r"

There are many great examples of different techniques for expect around the Internet so chances are, if you want to automate it, you can find an example on how to do it.

I [I]expect[/I] that you will find this how-to useful.

-Dave

Here is the complete script:

#!/usr/bin/expect

set fid [open /home/user/.secret]
set password [read $fid]
close $fid
spawn /usr/bin/ssh user@[lindex $argv 0]
expect {
-re ".*Are.*.*yes.*no.*" {
send "yes\r"
exp_continue
#look for the password prompt
}

"*?assword:*" {
send $password
send "\r"
interact
#he expect command will now return
}
}

2 comments:

  1. running expect to vyatta router to
    print router status to apache web page.

    Here is my script.

    #!/usr/bin/expect -f

    spawn /usr/bin/ssh root@10.0.0.5

    expect {
    -re ".*Are.*.*yes.*no.*" {
    send "yes\r"
    exp_continue
    #look for the password prompt
    }

    "*?assword:*" {
    send "password\n"
    expect *#
    }
    }

    ###########################################################################

    set output [open "wan_load_balance.txt" "w"]

    send "show wan-load-balance > /tmp/log.tmp\r"
    expect *#


    expect -re "(\\\$ |# )" { send "cat /tmp/log.tmp\r" }

    expect -re "\r\n(.*)\r(.*)(\\\$ |# )"
    set outcome $expect_out(1,string)
    send "\r"
    puts $output $outcome

    close $output

    ###########################################################################

    send "show wan-load-balance status > /tmp/log.tmp\r"
    expect *#

    set output [open "wan_load_balance_status.txt" "w"]
    expect -re "(\\\$ |# )" { send "cat /tmp/log.tmp\r" }

    expect -re "\r\n(.*)\r(.*)(\\\$ |# )"
    set outcome $expect_out(1,string)
    send "\r"
    puts $output $outcome
    close $output

    ###########################################################################

    send "show interfaces system enabled > /tmp/log.tmp\r"
    expect *#


    set output [open "interfaces_system_enabled.txt" "w"]
    expect -re "(\\\$ |# )" { send "cat /tmp/log.tmp\r" }

    expect -re "\r\n(.*)\r(.*)(\\\$ |# )"
    set outcome $expect_out(1,string)
    send "\r"
    puts $output $outcome
    close $output

    ###########################################################################



    send "exit\r"
    expect *



    PHP CODE



    &1');

    shell_exec('/usr/bin/expect /var/www/html/admin/vyatta/vstatus_1.sh');

    echo "###############################################################################"."
    ";
    echo "Vyatta 10.0.0.5 Load Balance - Command - show interfaces system enabled"."
    ";
    echo "###############################################################################"."
    ";

    $output = shell_exec('cat interfaces_system_enabled.txt');
    echo "$output".'
    ';


    echo "###############################################################################"."
    ";
    echo "Vyatta 10.0.0.5 Load Balance - Command - show wan-load-balance status"."
    ";
    echo "###############################################################################"."
    ";


    $output = shell_exec('cat wan_load_balance_status.txt');
    echo "$output"."
    ";

    echo "###############################################################################"."
    ";
    echo "Vyatta 10.0.0.5 Load Balance - Command - show wan-load-balance Uptime"."
    ";
    echo "###############################################################################"."
    ";


    $output = shell_exec('cat wan_load_balance.txt');
    echo "$output"."
    ";


    echo "###############################################################################"."
    ";
    echo "Finished"."
    ";
    echo "###############################################################################"."
    ";


    ?>

    Peter Keegan
    Berkleigh Computer Systems
    Your code helped me. Thanks

    ReplyDelete