Skip to main content

Restricting allowed ssh command with rsync

·508 words·3 mins

It can often be quite useful to restrict ssh access to only allow a single command on the target host. A major use-case for this is rsync. In the one direction for backing up files on the target host. And on the other direction for deploying files on the target host (say, a static website).

Fortunately, OpenSSH already supports restricting the allowed command using command option in the authorized_keys file. Unfortunately, the setup is not as simple as one would wish.

So in this article, I’ll document how the feature works, and how to set it up for rsync.

How command works #

When specifying command="/path/to/command parameters" for a key in authorized_keys this will completely overwrite the command that the source host has provided. The original command is available in the SSH_ORIGINAL_COMMAND environment variable.

This means that either we’re OK with completely overriding the command or we need some kind of wrapper script.

In the following we’ll go the second route since it’s more flexible and makes debugging easier.

Wrapper script restrict_ssh.sh #

In order not to write specific wrapper scripts for different target commands, here’s a version that’s more versatile:

#!/bin/sh

for pattern in "$@"; do
    if echo "$SSH_ORIGINAL_COMMAND" | grep -Eq "^$pattern$"; then
        exec $SSH_ORIGINAL_COMMAND
    fi
done
echo "Access denied for command '$SSH_ORIGINAL_COMMAND'"
# echo "$SSH_ORIGINAL_COMMAND" >> /tmp/original_ssh_command
exit 1

The script will expect one or more POSIX extended regular expressions as parameters and check if the full ssh command line exactly matches one of the patterns. For safety, the ^ and $ are included in the script, so we can’t forget them when invoking the script and cause a security issue that way.

If there’s a match, the script will exec the ssh command line, which replaces the shell by executing the first token of $SSH_SSH_ORIGINAL_COMMAND, passing the rest of the tokens as arguments. This means that tokens like && or ; will just get passed to the program, making it more secure than without exec.

The commented out line is useful for debugging rsync since it will not report errors in its output on the client.

You can just put this script in /usr/local/bin/restrict_ssh.sh (don’t forget to make it executable) and use it for all ssh command restrictions.

Restricting rsync using authorized_keys #

Now that we have our wrapper script we can go about restricting ssh access for rsync.

The general format for the authorized_keys file is:

[options ]keytype base64-key comment

Our restrictions go into the (optional) options field as follows:

restrict,command="/usr/local/bin/restrict_ssh.sh 'rsync --server -[a-zA-Z.]+( --(delete|partial|log-format=X))* [.] /target/'"

The restrict option enables all restrictions at once (for instance it disables port or agent forwarding and pty allocation).

The regular expression rsync --server -[a-zA-Z.]+( --(delete|partial|log-format=X))* [.] /target/ allows all short arguments and some long arguments and requires the last parameter to be a specific directory. If you invoke rsync with more long option you might need to extend the regexp.

Summary #

  • Put the wrapper script in /usr/local/bin/restrict_ssh.sh
  • Use the following in the authorized_keys file:
restrict,command="/usr/local/bin/restrict_ssh.sh 'rsync --server -[a-zA-Z.]+( --(delete|partial|log-format=X))* [.] /<target-dir>/'" <keytype> <base64-key> <comment>