Expect script does not work with multiple password requests - expect

I'm trying to execute a perl script to connect to linux hosts generate a snap and then I need to SCP this snap to my local server. So I have to put password 2 times 1 - to login on the servers and 2 - to SCP the file previously generated.
The problem is, my expect script is only working for the first password, would anyone have any idea?
[linuxserver]$ ./mul.sh
spawn perl perlscript.pl
Cleaning up /tmp/logs
Getting logs from server 9.x.x.x using snap
Password: <------ THIS ITERATION WORKS FINE WITH EXPECT
Password: <------ THE 2nd ITERATION DOES NOT WORK AND I HAVE TO ADD THE PASSWORD MANUALLY
[linuxserver]$
export PASSWORD='mypassword'
/usr/bin/expect -c '
spawn perl perlscript.pl
expect {
-nocase "*assword*" {
send "$env(PASSWORD)\r"
exp_continue
}
expect -c "
expect "*assword*"
send \$env(PASSWORD)\r
interact"
}
interact
'

First, use a quoted heredoc when putting an expect script inside a bash script, it reduces quoting hell significantly.
It seems you need to enter the passwords and then just wait for the perl program to end:
put multiple patterns in the same expect command
use the special eof pattern to expect the end of the spawned program.
/usr/bin/expect <<'END_EXPECT'
set timeout -1
spawn perl perlscript.pl
expect {
-nocase "*assword*" {
send "$env(PASSWORD)\r"
exp_continue
}
eof
}
END_EXPECT

Related

Automate ssh connection with expect in a bash script

Im trying to do a shell script using expect to automate connection because it's not allow to use ssh keys. Its my first script with expect and I find a little bit confuse for me. In this case I can connect by ssh but I can't close the connection, I don't know how to do, any idea ??
#!/bin/bash
user=pepe
host=server
pass=`cat /tmp/password.txt`
COMMAND=df
PASSWORD=$pass expect -c "
spawn ssh $user#$host
expect "*assword*"
send \$env(PASSWORD)\r
expect {
"*be*" {send $COMMAND\r;interact}}
"
The immediate fix is to specify the command as an argument to ssh -- then the connection will be made and the command will run and the connection will close
#!/bin/bash
user=pepe
host=server
pass=`cat /tmp/password.txt`
COMMAND=df
PASSWORD=$pass expect -c "
spawn ssh $user#$host $COMMAND
expect \"*assword*\"
send \$env(PASSWORD)\r
expect eof
"
A couple of things to note:
expect eof will gracefully wait for the spawned command to exit
I've escaped the internal double quotes.
Next step is to tidy up the quoting/escaping. When embedding code for another language in a shell script, using a quoted heredoc is the best way to avoid quoting hell. This means that all the shell variables need to passed to expect via the environment
#!/bin/bash
export user=pepe
export host=server
export pass=`cat /tmp/password.txt`
export cmd=df
expect << 'END_EXPECT'
spawn ssh $env(user)#$env(host) $env(cmd)
expect "*assword*"
send $env(pass)\r
expect eof
END_EXPECT
Last thing I'd do is to reduce the exposure of the password. Expect is built on the tcl language, which is fully capable of reading files:
#!/bin/bash
export user=pepe
export host=server
export pass=`cat /tmp/password.txt`
export cmd=df
expect << 'END_EXPECT'
set fh [open /tmp/password.txt]
gets $fh pass
close $fh
spawn ssh $env(user)#$env(host) $env(cmd)
expect "*assword*"
send $pass\r
expect eof
END_EXPECT
And make sure the permissions on that password file are as restrictive as possible:
mv /tmp/password.txt ~/.local/share/pass.txt
chmod 0400 ~/.local/share/pass.txt

Bash scripting with expect. Set parameters to multiple test boards

I'm working on a small project for school. I'm using 15 or so tuners to emulate a Cell network. I'm by no means well versed in scripting yet. I'm an EE who usually googles until I have some frankencode capable of my purposes.
The goal is the set up all the modules quickly so I thought to automate the process with a script. This requires ssh, and so far I have to manually type in the password each time. This morning I set up a basic test with both Expect and sshpass. In either case I can correctly log in, but not give instructions to the remote machine.
I was reading that sshpass has difficulty with sending remote instruction, correct me if I'm wrong.
/usr/bin/expect << EOF
spawn ssh root#<IP>
expect "(yes/no)?" #Are you sure you want to connect nonsense
send "yes\r"
expect "password"
send "$pass\r"
I tried a few things here to get the device to receive instruction
interact
cat /pathto/config.txt
#or
send "cat /pathto/config.txt
#the real goal is to send this instruction
sqlite3 /database.db "update table set param=X"
EOF
You might as well make it an expect script, not a shell script
#!/usr/bin/expect -f
and then pass the IP address to the script as a command line argument
expect myloginscript.exp 128.0.0.1 the_password
In the expect script, you'll grab that IP address from the arguments list
set ip [lindex $argv 0]
set pass [lindex $argv 1]
(Putting the password on the command line is not good security practice. You can research better methods of passing the password to your expect script.)
To use ssh, you'll be asked "are you sure" only the first time to connect, so let's make that conditional. That is done by letting the expect command wait for several patterns:
spawn ssh root#$ip
expect {
"(yes/no)?" {
send "yes\r"
# continue to wait for the password prompt
exp_continue
}
"password" {
send "$pass\r"
}
}
Once that is sent, you should expect to see your shell prompt. The pattern for this is up to your own configuration but it typically ends with a hash and a space.
expect -re {# $}
Now you can automate the rest of the commands:
send "cat /pathto/config.txt\r"
expect -re {# $}
# note the quoting
send "sqlite3 /database.db \"update table set param='X'\"\r"
expect -re {# $}
At this point, you'll want to log off:
send "exit\r"
expect eof
On the other hand, if you set up ssh private key authentication (see ssh-keygen and ssh-copy-id), you can just do this:
ssh root#IP sqlite3 /database.db "update table set param='$X'"
and not have to get into expect at all.

Why does this expect code not work?

Not sure if this is more of a scripting/unix question or a programming one, but I tried on the unix stackexchange and got no responses, so:
The following expect code seems to work; that is, it appears to enter text in answer to the password prompt. However, the device never actually mounts.
But if I simply enter the command into a shell and type the password in by hand the device mounts successfully.
So I'm curious where the input is actually ending up, as it never seems to 'catch' the password yet doesn't present an error message either? In fact the output looks exactly the same in both instances, but only in the case of running the command and typing the password manually do I see my files appear over the network.
Here is the code:
#!/usr/bin/expect
spawn sudo mount.cifs "//WinPC/My Pictures" /home/LinPC/Desktop/Pictures -o user=Me
expect "Password: " {
set send_slow {1 .1}
send -s "a_password"
}
UPDATE: Got the help I needed to make it work: had to insert 'expect eof' after sending the password so that it doesn't end prematurely. However I now wish to progress to changing it to 'expect_background' so that I can have the same trigger response to issuing multiple mount commands. The following ends prematurely, and 'expect eof' at the end causes an error:
expect_background "Password:" {
send "a_password\r"
expect eof
}
spawn sudo mount.cifs "//WinPC/My Pictures" /home/LinPC/Desktop/Pictures -o user=Me
expect eof
What should it look like?
UPDATE: the following code block illustrates the current problem:
expect_background "Password: " {
send "a_password\r"
expect eof
}
spawn sudo mount.cifs "//WinPC/My Pictures" /home/LinPC/Desktop/Pictures -o user=someone
expect "Password: " {
send "a_password\r"
expect eof
}
#The password prompt gets answered by 'expect' but not 'expect_background'.
#If I delete the last 'expect' and insert 'expect eof' it hangs for a short
#while at the password prompt (around 3 seconds) then exits.
#Why?
Add one more expect statement after sending the password.
send -s "a_password\r"
expect eof
The eof will make the Expect to wait till the end of program.

including conditional statements in expect

I am trying to write a bash script that uses expect to scp a file to remote systems. The expect block that I have so far looks like this
expect -c "
set timeout -1
spawn scp $file user#host:$file
expect "\Are you sure you want to continue connection (yes/no)\"
send -- \"$password\r\"
expect eof
"
The problem is that this handles the case in which the host is not a known host and it asks if I want to continue connecting. I would like to add a an option for the case in which the host is already known and it simply wants the password.
The other issue is that I would like to handle the event in which the password the user entered is not correct. In that case, I would like to have the user reenter the password.
What would be the best way of accomplishing this using bash and expect?
Many thanks in advance!
host is not a known host and it asks if I want to continue connecting
Use: scp -o "StrictHostKeyChecking no" <...>
event in which the password the user entered is not correct
If your script is considered to be interactive, why you are using expect at all? scp can ask and re-ask a password by itself.
Like this:
expect -c "
set timeout -1
spawn scp $file user#host:$file
expect
{Are you sure you want to continue connection (yes/no)} {
send \"yes\r\"
exp_continue
}
{Password: } {
send -- \"$password\r\"
}
}
expect eof
"
The exp_continue command loops back to the containing expect command so that other patterns have a change to be matched.

bash: does expect work with multiple password prompts?

I am currently using expect to pass in passwords so my scripts can run automatically without me having to sit around and type in the same password over and over again.
Important: Please don't comment about how big of a security risk this is or how I should be using ssh keys, I would use those if I could, but the setup I have to work with doesn't allow it.
My code looks like the following:
#!/bin/sh
PASS=mypassword
/usr/bin/expect -c "
spawn python Tools/python/install.py
expect -nocase \"password:\" {send \"$PASS\r\"; interact}
"
The problem I have is that install.py prompts for the same password a dozen times, it appears expect only auto fills the password for the first prompt.
Is there a way to modify the behavior so that it fills in the password all 12 times and not just the first time?
You have to do a little programming. Fortunately the answer is pretty simple:
#!/bin/sh
PASS=MyPassword
export PASS
/usr/bin/expect -c '
spawn python Tools/python/install.py
expect {
-nocase "password:" {
send "$env(PASS)\r"
exp_continue
}
"somthing_else_that_indicates_you're_ready_to_interact"
}
interact
'
Cleaned up the shell quoting a little too.

Resources