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

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

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

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.

Script to perform remote login and execute another local script

I have to write a script which performs remote login and authentication and execute a local script on remote.
My original code is like this:
#!/usr/bin/expect
spawn ssh user#ip "bash -ls" < ./script.bash
expect "password"
send "abc123/r"
interact
which gives following on running by ./my_script.sh
spawn ssh user#ip "bash -ls" < ./script.bash
user#ip's password:
./script.bash: No such file or directory
However, if I run the script without argument i.e. just
#!/usr/bin/expect
spawn ssh user#ip
expect "password"
send "abc123/r"
interact
It gets successfully login.
Also if I run directly through terminal without script like
ssh user#ip "bash -ls" < ./script.bash
then local file is getting executed successfully at remote server after taking password through prompt. Hence can you please suggest how to make my original code to work successfully.
That's because expect does not understand the shell's input redirection. Try this:
spawn sh -c {ssh user#ip "bash -ls" < ./script.bash}
If "user" or "ip" is a variable, we'll need different quoting. Such as
spawn sh -c "ssh $user#$ip 'bash -ls' < ./script.bash"
Use the full path to script.bash
#!/usr/bin/expect
spawn ssh user#ip "bash -ls" < /the/full/path/script.bash
expect "password"
send "abc123/r"
interact

Expect not working inside my Bash script

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.

expect auto login sucess, but can't exec any command

the shell code:
~ cat test_longin.sh
#!/usr/bin/expect
set timeout 120
spawn /usr/bin/ssh fuyou#target.server
expect "*assword*"
send "fuyou\n"
interact
When I type ./test_login.sh, it can login target servers successfully, but when I execute ls command, the console doesn't output any text. So I can't exec any commond on target server. Is the interaction problematic?
I think you should expect something after send, but not use command "interact" immediately.
xxx:/home/username/temp # cat t1.exp
#!/usr/bin/expect
spawn ssh serverx
expect "#"
send "ls -l\r"
expect "#"
interact

Resources