Expect not working inside my Bash script - linux

I am trying to execute expect command inside by small bash script to login into servers using key authentication method. My script is as follows:
#!/bin/bash
HOST=$1
/usr/bin/expect -c "
spawn ssh -i /root/.ssh/id_rsa root#$HOST
expect -exact "Enter passphrase for key '/root/.ssh/id_rsa': " ;
send "PASSPHRASE\n" ;
interact
"
Output with error is:
spawn ssh -i /root/.ssh/id_rsa root#server.domain.com
couldn't read file "passphrase": no such file or directory
Can you help to correct this?

There is a quoting issue in your code. Rather than trying to pass commands to expect on the command line save your code as an Expect script. You can then run it from a shell script or otherwise.
script.exp
#!/usr/bin/expect
# usage: ./script.exp host
set HOST [lindex $argv 0]
spawn ssh -i /root/.ssh/id_rsa root#$HOST
expect -exact "Enter passphrase for key '/root/.ssh/id_rsa': "
send "PASSPHRASE\n"
interact
However, if that's all you're doing with Expect in this case I second the suggestion to use ssh-agent instead.

Related

how to use expect in linux? [duplicate]

I'm trying to use expect in a Bash script to provide the SSH password. Providing the password works, but I don't end up in the SSH session as I should. It goes back strait to Bash.
My script:
#!/bin/bash
read -s PWD
/usr/bin/expect <<EOD
spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no usr#$myhost.example.com'
expect "password"
send "$PWD\n"
EOD
echo "you're out"
The output of my script:
spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no usr#$myhost.example.com
usr#$myhost.example.com's password: you're out
I would like to have my SSH session and, only when I exit it, to go back to my Bash script.
The reason why I am using Bash before expect is because I have to use a menu. I can choose which unit/device to connect to.
To those who want to reply that I should use SSH keys, please abstain.
Mixing Bash and Expect is not a good way to achieve the desired effect. I'd try to use only Expect:
#!/usr/bin/expect
eval spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no usr#$myhost.example.com
# Use the correct prompt
set prompt ":|#|\\\$"
interact -o -nobuffer -re $prompt return
send "my_password\r"
interact -o -nobuffer -re $prompt return
send "my_command1\r"
interact -o -nobuffer -re $prompt return
send "my_command2\r"
interact
Sample solution for bash could be:
#!/bin/bash
/usr/bin/expect -c 'expect "\n" { eval spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no usr#$myhost.example.com; interact }'
This will wait for Enter and then return to (for a moment) the interactive session.
The easiest way is to use sshpass. This is available in Ubuntu/Debian repositories and you don't have to deal with integrating expect with Bash.
An example:
sshpass -p<password> ssh <arguments>
sshpass -ptest1324 ssh user#192.168.1.200 ls -l /tmp
The above command can be easily integrated with a Bash script.
Note: Please read the Security Considerations section in man sshpass for a full understanding of the security implications.
Add the 'interact' Expect command just before your EOD:
#!/bin/bash
read -s PWD
/usr/bin/expect <<EOD
spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no usr#$myhost.example.com
expect "password"
send -- "$PWD\r"
interact
EOD
echo "you're out"
This should let you interact with the remote machine until you log out. Then you'll be back in Bash.
After looking for an answer for the question for months, I finally find a really best solution: writing a simple script.
#!/usr/bin/expect
set timeout 20
set cmd [lrange $argv 1 end]
set password [lindex $argv 0]
eval spawn $cmd
expect "assword:" # matches both 'Password' and 'password'
send -- "$password\r"; # -- for passwords starting with -, see https://stackoverflow.com/a/21280372/4575793
interact
Put it to /usr/bin/exp, then you can use:
exp <password> ssh <anything>
exp <password> scp <anysrc> <anydst>
Done!
A simple Expect script:
File Remotelogin.exp
#!/usr/bin/expect
set user [lindex $argv 1]
set ip [lindex $argv 0]
set password [lindex $argv 2]
spawn ssh $user#$ip
expect "password"
send "$password\r"
interact
Example:
./Remotelogin.exp <ip> <user name> <password>
Also make sure to use
send -- "$PWD\r"
instead, as passwords starting with a dash (-) will fail otherwise.
The above won't interpret a string starting with a dash as an option to the send command.
Use the helper tool fd0ssh (from hxtools, source for ubuntu, source for openSUSE, not pmt). It works without having to expect a particular prompt from the ssh program.
It is also "much safer than passing the password on the command line as sshpass does" ( - comment by Charles Duffy).
Another way that I found useful to use a small Expect script from a Bash script is as follows.
...
Bash script start
Bash commands
...
expect - <<EOF
spawn your-command-here
expect "some-pattern"
send "some-command"
...
...
EOF
...
More Bash commands
...
This works because ...If the string "-" is supplied as a filename, standard input is read instead...
sshpass is broken if you try to use it inside a Sublime Text build target, inside a Makefile. Instead of sshpass, you can use passh
With sshpass you would do:
sshpass -p pa$$word ssh user#host
With passh you would do:
passh -p pa$$word ssh user#host
Note: Do not forget to use -o StrictHostKeyChecking=no. Otherwise, the connection will hang on the first time you use it. For example:
passh -p pa$$word ssh -o StrictHostKeyChecking=no user#host
References:
Send command for password doesn't work using Expect script in SSH connection
How can I disable strict host key checking in ssh?
How to disable SSH host key checking
scp without known_hosts check
pam_mount and sshfs with password authentication

bash command redirection in expect script failing with permission denied (spawn ssh user#host command > outfile)

I have this expect script
#!/usr/bin/expect -f
set pass [ exec echo my_phrase | gpg --batch --quiet --yes --passphrase-fd 0 -d /root/.password-store/ssh/my_pgp_store.gpg ]
spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no -i /home/myuser/.ssh/id_rsa ssh-user#remote-host.com /some/remote/script > /home/myuser/ssh.output
expect "*?assword:*"
send -- "$pass\r"
interact
that is supposed to read a gpg encrypted password, and use it to call a remote script via ssh (there are many reasons why I need to do it like that). Now, my problem is that whenever I run this script, the ssh connection is correctly set up, but it fails in writing the output of the ssh call with the error
bash: /home/myuser/ssh.output: Permission denied
I'm running the script as root (because I have to run it as root), and I already tried to change the permissions and ownerships of all the involved files and directories.
The redir char > is not special for expect's spawn command. The command
spawn ssh user#host command > outfile
means the same as
spawn ssh user#host "command > outfile"
and the redir > outfile will be executed on the remote host.
Try like this:
spawn bash -c "ssh user#host command > outfile"
expect "assword:"
send -- "$pass\r"
interact

Expect and Bash script

I'm trying to do an expect script with a loop which tries to connect in ssh and see if it's possible (Good Password and reachable). I tried to put the result into a variable but only the end of the stdout is recorded and not all the stdout. How could I do?
result=$(
(/usr/bin/expect << EOF
spawn ssh $username#$ip -o StrictHostKeyChecking=no
set timeout 2
expect ":"
send -- "$password\r"
expect ">"
send -- "show clock\r"
expect ">"
EOF
) 2>&1)
Thank you.
If you're trying to automate sending a command to another server using ssh:
Generate an ssh key, instead of using passwords:
ssh-keygen -t rsa -b 2048
Copy it to the server:
ssh-copy-id id#server
Now you don't need to worry about the password. ssh keys act as passwords, and generally are much more secure. See these (SSH login without password, Why is using SSH key more secure than using passwords?) for more information about ssh keys.
Then you can just use this command - no expect needed, because you won't be asked for a password. It'll use your ssh key. Mine is located at ~/.ssh/id_rsa. So:
ssh id#server -i ~/.ssh/id_rsa "show clock;"
-i stands for the identity file, i.e., your ssh key file.
will send a command to the SSH server.
Altogether now:
ssh-keygen -t rsa -b 2048
ssh-copy-id id#server
ssh id#server -i ~/.ssh/id_rsa "show clock;"
Three commands, and you've done it!
Calling to expectscript from shell script:
expect "/copySSHKey.exp <machine> <password> </.ssh/id_rsa.pub path>"
Expect script:
#!/usr/bin/expect -f
set machine [lrange $argv 0 0]
set ip [lrange $argv 1 1]
set pass [lrange $argv 2 2]
set path [lrange $argv 3 3]
set timeout -1
spawn ssh-keygen -R ${machine}
spawn ssh-copy-id -i ${path} root#${machine}
match_max 100000
expect {
sleep 10
"password" {
send -- "$pass\r"
send -- "\r"
sleep 1
send_user " SSH key copied to $machine\n"
}
"$machine's" {
send_user " SSH key copied to $machine\n"
}
"password:*" {
send -- "$pass\r"
send -- "\r"
sleep 1
send_user " SSH key copied to $machine\n"
}
"machine" {
sleep 1
send_user " SSH key copied to $machine\n"
}
}
interact
Try using double quotes (") to capture the subshell's output. Maybe you're losing data because of parsing issues:
result="$( ... )"

Using 'expect' command to pass password to SSH running script remotely

I need to create a bash script that will remotely run another script on a batch of machines. To do so I am passing a script through SSH.
ssh -p$port root#$ip 'bash -s' < /path/to/script/test.sh
I thought it would use my RSA keys but I am getting error:
"Enter password: ERROR 1045 (28000): Access denied for user 'root'#'localhost' (using password: YES)"
I tried using sshpass to no avail. So my next solution was using expect. I have never used expect before and I'm positive my syntax is way off.
ssh -p$port root#$ip 'bash -s' < /path/to/script/test.sh
/usr/bin/expect <<EOD
expect "password"
send "$spass\n"
send "\n"
EOD
I have root access to all machines and ANY solution will do as long as the code remains within bash. Just keep in mind that this will be done in a loop with global variables ($spass, $ip, $port, etc) passed from a parent script.
You are doing it wrong in two means:
If you want expect to interact with ssh, you need to start ssh from expect script and not before.
If you put the script (/path/to/script/test.sh) to stdin of ssh, you can't communicate with the ssh process any more.
You should rather copy the script to remote host using scp and then run it.
Expect script might look like this:
/usr/bin/expect <<EOF
spawn ssh -p$port root#$ip
expect "password"
send "$Spass\r"
expect "$ "
send "/path/to/script/on/remote/server/test.sh\r"
expect "$ "
interact
EOF
#!/usr/bin/expect
#Replace with remote username and remote ipaddress
spawn /usr/bin/ssh -o StrictHostKeyChecking=no username#IPAddress
#Replace with remote username and remote ipaddress
expect "username#IPAddress's password: "
#Provide remote system password
send "urpassword\n"
#add commands to be executed. Also possible to execute bash scripts
expect "$ " {send "pwd\n"} # bash command
expect "$ " {send "cd mytest\n"}
expect "$ " {send "./first.sh\n"} # bash scripts
expect "$ " {send "exit\n"}
interact

How do I ssh into a machine, wait for a prompt, and then send a bunch of commands to it?

How do I do the following?
SSH to a machine, probably using expect since this is a script and I can't type the password for it
Wait for a prompt from the machine. The prompt is ->
Then send a bunch of commands from the original host to the machine I ssh'd to. Note that the output from these commands can contain the characters ->
Exit
Here are the contents of some-commands.txt:
first command
second command
third command
Here are the contents of the expect script:
#!/usr/bin/expect
set f [open "some-commands.txt"]
set cmds [split [read $f] "\n"]
close $f
eval spawn ssh -oStrictHostKeyChecking=no -oCheckHostIP=no root#machine
interact -o -nobuffer -re "Password:" return
send "password\r"
# look for the prompt
set prompt "\n-> "
foreach cmd $cmds {
send "$cmd\r";
# The following works, except for the commands
# whose output include ->
interact -o -nobuffer -re "-> " return
}
The problem is that the interact command captures the -> from the command output instead of the prompt, which hasn't yet arrived at that point.
I'm used to accomplish the same thing by doing something like:
ssh -t -t -C user#host 'bash -s' < my_shell_script.sh param1 paramX
Where my_shell_script.sh is an simple shell script.
The trick here is use multiple -t to force pseudo-terminal over ssh and the -s option to bash witch makes it reads the commands from the standard input.

Resources