Introduction 

This article describes how you can use rsync and rsync --server, to allow specific ssh-keys to download files - without allowing full access to the server - using ssh-force commands and matching ssh-keys.

Forcing SSH-Command 

In the .ssh/authorized\_keys-file in the relevant HOME-directory add this line:

command=/path/to/script ssh-key

This will cause the given script to be executed instead of the original target. rsync essentially works by passing a command for execution on the remote server to ssh, for example:

ssh HOST -t rsync --sender --server`

The rsync-sender on the server will then open a port, to which the local will client connect and receive the files from. Being an rsync-sender implies, that rsync is only able to write out to the client and will only open files for reading on the server itself. A rsync-sender can therefore not create or modify files.

The original command passed to SSH by the client, either by using rsync or calling ssh HOST -t command, will then automatically be stored in an environment variable called SSH_ORIGINAL_COMMAND, which can be used within the script specified in the authorized_keys-file on the server. SSH does not check the contents of this command, so we must ensure that we only execute our script if the original command isn’t malformed in any way. We can do this by defining and checking against a regex-pattern. You may replace the final * with a more specific path.

PATTERN=^rsync\ \-\-server\ \-\-sender.*
if [[ "$SSH_ORIGINAL_COMMAND" =~ $PATTERN ]] ; then
    # do something
fi

This is still open to potential quoting and general escaping exploits. Since we have ensured the command starts with the rsync-binary, we can use exec to replace the process running the script with a process running $SSH_ORIGINAL_COMMAND within the if-block.

exec $SSH_ORIGINAL_COMMAND

Because exec replaces the currently running process, the execution will inevitably end after the first command, meaing that, even if a ; or && happened to be in the original command, anything behind it will still not be executed. You can now use the rsync command locally as you normally would.