Ger Cas
Ger Cas

Reputation: 2298

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 [email protected] '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:

Upvotes: 0

Views: 871

Answers (1)

root
root

Reputation: 6048

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.

Upvotes: 1

Related Questions