How can I account for connection failure using expect for ssh log-on automation? - ssh

I have a shell script that works fairly well for automating my ssh connections and for anything else that I would like to do via ssh. I'm very unsatisfied with the results, however, when host can't be found or if connection is refused. If the host cannot be found, upon timeout send prints my password onto the screen... no good. I've gotten around this by adding an infinite timeout < set timeout -1 >. When connection is refused; however, I get a message about how connection was refused and that there was an error sending, etc... and my password is printed as well. Is there a way to tell my script that if exact expect is not met then don't proceed to send, to just ctrl+c? The following is the relevant part of my shell script: (used in bash, by the way) Thanks in advance.
expect -c "
spawn ssh $USER#$HOST
expect -exact \"$USER#$HOST's password:\"
send \"$PASS\r\"
interact"

The answer is to expect the timeout keyword. If none of the patterns match, then the timeout condition occurs -- of course, you can't set the timeout value to -1: set it to a reasonable number of seconds.
Instead of cramming a large-ish script into the -c argument, put it into a file
#! /usr/local/bin/expect -f
set host [lindex $argv 0]
set user [lindex $argv 1]
set password [lindex $argv 2]
# or, foreach {host user password} $argv {break}
spawn ssh $user#$host
expect {
-re {password: $} {send "$password\r"}
timeout {error "ssh connection timed out!"}
}
interact

Related

Expect script not working and terminal closes immediately

I don't know what's wrong with the script. I set up a new profile on Iterm terminal to run the script, but it never works and closes immediately. Here's the script:
#!/usr/bin/expect -f
set timeout 120
set secret mysecret
set username asdf
set host {123.456.789.010}
set password password123
log_user 0
spawn oathtool --totp --base32 $secret
expect -re \\d+
sleep 400
set otp $expect_out(0,string)
spawn ssh -2 $username#$host
expect "*assword:*"
send "$password\n"
expect "Enter Google Authenticator code:"
send "$otp\n"
interact
First, test you ssh connection with:
ssh -v <auser>#<apassword>
That will validate the SSH session works.
Make sure to not use ssh -T ..., since you might need a terminal for expect commands to work.
Second, add at least an echo at the beginning of the script, to see if it is called:
puts "Script running\r"
Third, see if a bash script, with part of it using expect as in here, would work better in this case

Expect ssh script (execute command on remote machine)

I'm writing an expect script that executes a command on a remote server using ssh.
Command syntax: ssh <classname> <command>
Code:
set ip "hive28.cs.berkeley.edu"
set class [lindex $argv 0]
// set $user and $ip according to class
set cmd [lindex $argv 1]
spawn ssh "$user\#$ip '$cmd'"
expect "assword"
send "$password\r";
interact
Unfortunately, I get this error:
~/foo> ssh2 162 'pwd'
spawn ssh ***#hive28.cs.berkeley.edu 'pwd'
ssh: Could not resolve hostname hive28.cs.berkeley.edu 'pwd': Name or service not known
send: spawn id exp6 not open
while executing
"send "$password\r""
invoked from within <...>
But when I run the generated command directly, it works (ignore the gdircolors warnings):
~/foo> ssh ***#hive28.cs.berkeley.edu 'pwd'
***#hive28.cs.berkeley.edu's password:
/home/ff/cs162/adm/bashrc.d/70-gnu.sh: line 36: gdircolors: command not found
/home/cc/cs162/sp16/class/***
Try this instead:
set ip "hive28.cs.berkeley.edu"
set class [lindex $argv 0]
set cmd [lindex $argv 1]
spawn ssh "$user#$ip" "$cmd"
expect "assword"
send "$password\r";
interact
The problem seems to be with your quoting. "$user\#$ip" and "$cmd" are two separate arguments.

Tcl Expect fails spawning SSH to server but SSH from command line works

I have some code that I'm using to connect to a server and perform some commands. The code is as follows:
#!/usr/bin/expect
log_file ./log_std.log
proc setPassword {oldPass newPass} {
send -- "passwd\r"
expect "* Old password:"
send -- "$oldPass\r"
expect "* New password:"
send -- "$newPass\r"
expect "* new password again:"
send -- "$newPass\r"
}
set server [lindex $argv 0]
spawn /bin/ssh perfgen#$server
# Increase buffer size to support large text responses
match_max 100000
# Conditionally expects a prompt for host authenticity
expect {
"*The authenticity of host*" {
send -- "yes\r"
}
}
What I find very strange is that when I SSH from my command line the SSH command works no problem. However, when I SSH from the shell script I get the following error:
spawn /bin/ssh perfgen#192.168.80.132
ssh: Could not resolve hostname 192.168.80.132
: Name or service not known
The same script runs against 3 servers, but 2 of the 3 servers always fail. However, if I try logging into the servers manually do do the work all three servers pass.
Any idea what might be happening here? I'm completely stumped. This code was working up until about 2 weeks ago and according to the server administrator nothing has changed on the server-side config.
Trimming any whitespace seemed to solve the issue:
set serverTrimmed [string trim $server]

Password is not supplied for ssh login

I'm trying to login to a remote host using expect & spawn. While automating this it's going till ssh username#host.example.com and password prompt came and it is terminating wihtout supplying the password. What is wrong with this script ?
#!/usr/bin/expect
set timeout 9
passwrd=PASSWORD
username=USER_NAME
host=host.example.com
/usr/bin/expect << EOF
spawn ssh $username#$host
expect "Password:"
send "$passwrd\r"
interact
You're confusing expect syntax and shell syntax. You want:
#!/usr/bin/expect
set timeout 9
set passwrd PASSWORD
set username USER_NAME
set host host.example.com
spawn ssh $username#$host
expect "Password:"
send "$passwrd\r"
interact

How to pass multiple servers to an expect script?

I'm trying to use an expect script to change my password on multiple servers, but I'm a little confused as to how to pass the list of servers through to it.
The script that I'm using is as follows:
#!/usr/bin/expect -f
# wrapper to make passwd(1) be non-interactive
# username is passed as 1st arg, passwd as 2nd
set username [lindex $argv 0]
set password [lindex $argv 1]
set serverid [lindex $argv 2]
set newpassword [lindex $argv 3]
spawn ssh -t $serverid passwd
expect "assword:"
sleep 3
send "$password\r"
expect "UNIX password:"
sleep 3
send "$password\r"
expect "password:"
sleep 3
send "$newpassword\r"
expect "password:"
sleep 3
send "$newpassword\r"
expect eof
And I'm trying to run it as such:
[blah#blah ~]$ ./setkey1 blah password 'cat serverlist' meh
which gives me the following output:
spawn ssh -t cat serverid passwd
ssh: cat serverid: Name or service not known
send: spawn id exp6 not open
while executing
"send "$password\r""
(file "./setkey1" line 13)
So I then tried running the following for loop:
[blah#blah ~]$ for i in serverid; do `cat serverid`; ./setkey1 blah password $i meh; done
Which gave me the following:
-bash: staging01v: command not found
spawn ssh -t serverid passwd
ssh: serverid: Name or service not known
send: spawn id exp6 not open
while executing
"send "$password\r""
(file "./setkey1" line 13)
If I try using the expect script, and just enter in one server name, it works as...um...expected.
What am I doing wrong?
There are many ways to solve this problem. I'd change the order of your arguments to be able to pass in multiple servers.
In the expect program:
foreach {username password newpassword} $argv break
set servers [lrange $argv 3 end]
foreach serverid $servers {
# your existing code goes here
}
Then from the shell, invoke it like this
./setkey1 userid pass newpass $(cat servers.txt)
If you use bash, you can do
./setkey1 userid pass newpass $(<servers.txt)
I did something similar here is my way
set servers "server1 server2 server3"
set users "user1 user2 user3"
set passwords "password1 password2 password3"
set newpasswords "new1 new2 new3"
foreach server $servers user $users password $passwords newpassword $newpasswords
{
Your commands using the variables server/user/password/newpassword
}
this will run the commands in a loop for each element in the sets
so first server with first user with first password in a loop then goes to 2nd etc..