scripting sftp with expect - sftp

I have to script an sftp-access with password-authentification. Unfortunately our customer can't create a key-authentification on the server we have to access.
All the sftp-Commands are generated and written in a temporary file (tmp.txt). Following command works fine on the command-line:
sftp OnlineRegTest_BS#ftp.b-n-c.ch < tmp.txt
The problems starts when I'd like to use "expect" because I have to script it and use a password.
EXPECTSCRIPT=`cat <<EOF
spawn sftp user#server < tmp.txt
expect "password"
send ${pass}
send "\r"
EOF`
expect -c "$EXPECTSCRIPT"
If I remove the "< tmp.txt" the connection will establish. But I need the commands from the tmp.txt. Here's the error-Message:
[root#xxx web]# ./transfer.sh
spawn sftp user#server < tmp.txt
usage: sftp [-1Cv] [-B buffer_size] [-b batchfile] [-F ssh_config]
[-o ssh_option] [-P sftp_server_path] [-R num_requests]
[-S program] [-s subsystem | sftp_server] host
sftp [[user#]host[:file [file]]]
sftp [[user#]host[:dir[/]]]
sftp -b batchfile [user#]host
send: spawn id exp6 not open
while executing
"send password"
Does someone have any ideas?
I also couldn't get it running with sftp -b... :-(
edit for #glenn jackman
EXPECTSCRIPT2=`cat <<EOF
spawn sh -c {sftp user#server < tmp.txt}
expect "password"
send ${pass}
send "\r"
expect "sftp>"
expect "sftp>"
.....
expect "sftp>"
expect "sftp>"
expect "sftp>"
EOF`
the tmp.txt looks like this:
cd d
put d/d1
cd ../e
put e/e1
put e/e2
cd ../f
put f/f1

Try spawning a shell to handle the redirection:
spawn sh -c {sftp user#example.com < tmp.txt}

Related

How to copy files to remote server with a user without privileges?

When I need to copy a file from local server (server A) to remote server(server B) via SSH, using a user with enough privileges, I do this successfuly like below
localpath='/this/is/local/path/file1.txt'
remotepath='/this/is/remote/path/'
mypass='MyPassword123'
sshpass -p $mypass scp username#hostname:$localpath $remotepath
Now, I have to transfer a file from server A to server C with a user that doesn't have enough privileges to copy. Then once
I connected to Server C, I need to send su in order to be able to send commands like cd, ls, etc.
Manually, I access the server C via SSH like this:
[root#ServerA ~]# ssh username#hostname
You are trying to access a restricted zone. Only Authorized Users allowed.
Password:
Last login: Sat Jun 13 10:17:40 2020 from XXX.XXX.XXX.XXX
ServerC ~ $
ServerC ~ $ su
Password:
ServerC /home/myuser #
ServerC /home/myuser # cd /documents/backups/
ServerC /documents/backups #
At this moment myuser has superuser privileges and I can send commands.
Then, how can I automate the task to copy files from server A to server C with the need to send su once I'm connected to Server C?
I've tried so far doing like this:
sshpass -p $mypass ssh -t username#hostname "su -c \"cd /documents/backups/ && ls\""
it requests password for su and I'm able to send cd and ls but with this command, I'm not copying files from Server A to Server C, only semi-automating the access to Server C and sending the su in Server C.
Thanks in advance for any help.
UPDATE
# $TAR | ssh $username#$hostname "$COMMAND"
+ tar -cv -C /this/is/local/path/file1.txt .
+ ssh username#X.X.X.X 'set -x; rm -f /tmp/copy && mknod /tmp/copy p; su - <<< "su_password
set -x; tar -xv -C /this/is/remote/path/ . < /tmp/copy" & cat > /tmp/copy'
tar: /this/is/local/path/file1.txt: Cannot chdir: Not a directory
tar: Error is not recoverable: exiting now
You are trying to access a restricted zone. Only Authorized Users allowed.
Password:
+ rm -f /tmp/copy
+ mknod /tmp/copy p
+ su -
+ cat
Password:
Editorial note: the previous version of this answer used sudo, the current version uses su as requested in the question.
You could use tar and pipes, like so:
TAR="tar -cv -C $localpath ."
UNTAR="tar -xv -C $remotepath ."
PREPARE_PIPE="rm -f /tmp/copy && mknod /tmp/copy p"
NEWLINE=$'\n' # that's the easiest way to get a literal newline
ROOT_PASSWORD=rootpasswordverydangerous
COMMAND="set -x; $PREPARE_PIPE; su - <<< \"${ROOT_PASSWORD}${NEWLINE} set -x; $UNTAR < /tmp/copy\" & cat > /tmp/copy"
$TAR | ssh username#hostname "$COMMAND"
Explanation:
tar -c . archives the current directory into a single file. We aren't passing -f to tar, so that single file is standard output.
tar -x . extracts the content of a single tar archive file to the current directory. We aren't passing -f to tar, so that single file is standard input.
-C <path> tells tar to cd into <path> so that it will be the current directory in which files are copied from/to.
-v just tells tar to list the files tar archives/extracts, for debugging purposes.
Likewise, set -x is just to have bash to emit trace information, for debugging purposes.
So we're archiving $localpath into stdout, and piping it to ssh, which will pipe it to $COMMAND.
If there was a way to give su the password in the command line, we would have used something like:
$TAR | ssh ... su --password ${ROOT_PASSWORD} -c "$UNTAR"
and things would have been simple.
But su doesn't have that. su runs like a shell, reading from stdin. So it will first read the password, and once the password is read and su has established a root session, it reads commands from stdin. That's why we have su - <<< \"${ROOT_PASSWORD}${NEWLINE}${UNTAR}.
But now stdin is used by the password and command, so we can't use it as the archive. We could use another file descriptor, but I prefer not to, because then the solution can be more easily ported to work with sudo instead of su. sudo closes all file descriptors, and sudo -C 200 (only close file descriptors above 200) may not work (didn't work on my test machine).
if we went that direction, we would have used something like
$TAR | ssh ... 'exec 9<&2 && sudo -S <<< $mypass bash -c "$UNTAR <&9"'
Our next option is to do something like cat > /tmp/archive.tar in order to write the entire archive into a file, and then have something like $UNTAR < /tmp/archive.tar. But the archive may be huge and we may run out of disk space.
So the idea is to create a dedicated pipe - that's PREPARE_PIPE. Pipes don't save anything to disk, and don't store the entire stream in memory, so the reader and the writer have to work concurrently (you know, like with a real pipe).
So having redirected su's stdin from $ROOT_PASSWORD, we pull ssh's stdin into our pipe with cat > /tmp/copy, and in parallel (&) having $UNTAR read from the pipe (< /tmp/copy).
Notes:
You could also pass -z to both tar commands to pass it compressed, if your network is too slow.
tar will preserve the source's metadata, e.g. timestamps and ownership.
Passing $ROOT_PASSWORD to commands is not good practice, anyone who runs ps -ef can see the password. There are ways to pass the password to server C in a more secure way, I didn't include it in order to not further complicate this answer.
I would suggest asking the server's owner to install sudo, so that if the password is compromised via ps -ef, at least it's not the root password.

SFTP scripting unable to change directories question

I'm relatively new to using sftp in scripting format (bash shell on Mac OSX High Sierra). I am having issues changing dirs once logged into the remote server. I want to cd to 'FTP PDF (Download) to CR'
Here is my script(edited):
#!/bin/bash
expect -c "
spawn sftp ClaimReturn#8.19.132.155
expect \"Password\"
send \"xxxxxxx\r\"
expect \"sftp>\"
send \"cd CR\ Reports\r\"
#DIR TO CD to "CR REPORTS"
expect \"sftp>\"
send \"bye\r\"
expect \"#\"
"
This is really just an formatted comment expanding on #meuh's comment.
You're having quoting trouble. You could use single quotes or a quoted heredoc to make your life easier
#!/bin/bash
expect <<'END_EXPECT'
spawn sftp ClaimReturn#8.19.132.155
expect "Password"
send "xxxxxxx\r"
expect "sftp>"
send "cd 'CR Reports'\r"
#DIR TO CD to "CR REPORTS"
expect "sftp>"
send "bye\r"
expect "#"
END_EXPECT
Or, just an expect script:
#!/usr/bin/expect -f
spawn sftp ClaimReturn#8.19.132.155
expect "Password"
send "xxxxxxx\r"
expect "sftp>"
send "cd 'CR Reports'\r"
#DIR TO CD to "CR REPORTS"
expect "sftp>"
send "bye\r"
expect "#"

SSH between N number of servers using script

I have n number of servers like c0001.test.cloud.com, c0002.test.cloud.com, c0003.test.cloud.com and I want to do the ssh between these servers like:
from Server: c0001 do the ssh to c0002 and then exit the server.
Come back to c0001 do the ssh to c0003 and then exit the server.
So in this way it will execute the script without entering any input during runtime and we can have n number of servers.
I have written one script :
str1=c0001.test.cloud.com,c0002.test.cloud.com,c0003.test.cloud.com
string="$( cut -d ',' -f 2- <<< "$str1" )"
echo "$string"
for j in $(echo $string | sed "s/,/ /g")
do
ssh appAccount#j
done
But this script is not running fine. I have also checked it by passing parameters
like: -o StrictHostKeyChecking=no and <<'ENDSSH' but it is not working.
Assuming the number of commands you want to run are small, you could:
Create a script of commands that will run from c0001.test.cloud.com to each of the servers. For example, create a file on your local machine called commands.sh with:
hosts="c0002.test.cloud.com c0003.test.cloud.com"
for host in $hosts do
ssh -o StrictHostKeyChecking=no -q appAccount#$host <command 1> && <command 2>
done
On your local machine, ssh to c0001.test.cloud.com and execute the commands in commands.sh:
ssh -o StrictHostKeyChecking=no -q appAccount#c0001.test.cloud.com 'bash -s' < commands.sh
However, if your requirements become more complex, a more robust solution might be to use a cluster administration tool such as ClusterShell

How do I FTP multiple files at the same time ( parallel) from UNIX

We have to transfer 10 files in parallel from a unix using shell script via FTP.
Just put download process in the background appending ampersand:
wget --ftp-user=*** --ftp-password=*** ftp://server/file_A 1> /dev/null 2> /dev/null&
wget --ftp-user=*** --ftp-password=*** ftp://server/file_B 1> /dev/null 2> /dev/null&
wget --ftp-user=*** --ftp-password=*** ftp://server/file_C 1> /dev/null 2> /dev/null&
...
If the ftp server doesn't impose any limits to the number of concurrent connections, you can run many ftp sessions in background. E.g. (note: I'll assume a generic gnu-like ftp client, command line options and input strings may be different):
for i in file1 file2 file3 ... file10; do
echo "get $i" | ftp $ServerHost --user $username --password "$xxx" --binary >/dev/null 2>&1 &
done
wait

Problem with plink output

I'm using plink to run a command on a Unix remote machine.
The command is:
ls -1trd testegrep.txt |tail -1 |xargs tail -f| grep 's';
The way I'm sending this command is by using a file with a set of commands like:
plink.exe -ssh -t -l user -pw pwd tst.url.pt -m commands.out
When I run the command this way the plink does not receive any input. It seems that is waiting for input.
But if I run:
plink.exe -ssh -t -l user -pw pwd tst.url.pt "ls -1trd testegrep.txt |tail -1 |xargs tail -f| grep 's';"
I get the expected result.
I'm not using the plink with a file with the command because I choose so. I'm using a test automation software that allows me to run tests on remote hosts and this is the way the tool works.
Any thoughts on what is going wrong?
I tested the command you provided and it worked without problems.
Maybe the problem is related to:
The server's host key is not cached in the registry.
The path to the file is not correct.
The file is empty.
include server hostkey
most importantly, you need to include the unix profile using the -m paramater
You can include all your commands in the same file where the profile is kept also.
$Output = ((plink.exe -hostkey hostkey -l UNAME -i SSHKEY -P 22 -ssh server -batch -m PROFILE) | ? {$_ -ne ""})

Resources