Secure passwordless root backups with rrsync

The Problem: You need to access a remote system with rsync to back it up. But some of the files or directories you need to back up need root permissions to read. You need to automate the backups, so you can’t use a password or passphrase. But you really don’t want to allow passwordless logins to the remote, and especially not as root! So what to do?

Introducing rrsync

Joe Smith wrote a Perl script called rrsync that leverages the “forced command” mechanism in ssh to provide a way to lock down that root login. Or any login that needs to use rsync, but doesn’t/shouldn’t need anything else.

rrsync lets you specify the specific directory that the client can access, and optionally makes it read-only.

Background: Forced commands

The sshd daemon allows you to impose a forced command on any user logging in. The user is allowed to log in, but only the forced command will be executed – regardless of what the user provided as a command. The forced command is specified by adding “command=some_command” at the front of the user’s entry in the authorized_keys file on the remote host.

To allow the user only to see what files are in their home directory on the server, you might have this entry in their authorized_keys file:

command="ls -la" ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCq5kIUsZ
[... snipped ...]
1+SXasbebkuHCLQYu5R user@host

When a user logs in and a forced command is set up, sshd sets a couple of useful environment variables on the remote host:

SSH_ORIGINAL_COMMAND contains the original command the user tried to run. So if you for example tried to run “ls -la /opt“, but a forced command was set up, this environment variable would contain “ls -la /opt“.

SSH_CONNECTION contains the ssh connection details – the IP address of the client, the port the connection is coming from and the port that was connected to on the server. None of these are particularly useful in a world full of NAT and port forwarding, but you never know. rrsync logs this information, but doesn’t otherwise use it.

sshd on the remote host

It’s good practice to not let root log in at all via ssh, so you probably have PermitRootLogin set to “no”. But for root to be able to carry out any command, even a forced command, it has to be allowed to log in.

The good news is that you can restrict it to only being allowed to log in to carry out forced commands, by setting PermitRootLogin to “forced-command-only” and this is what I strongly recommend doing.

Edit /etc/ssh/sshd_config and look for the line with PermitRootLogin. Uncomment it if it is commented out, and change the line so it looks like this:

PermitRootLogin forced-commands-only

Installing rrsync

rrsync needs to be installed on the remote host, so the following steps should be carried out there.

rrsync is part of the standard rsync install package on most modern systems and obviously you’ll need rsync itself on the remote host, so install rsync if it is not already installed. On Ubuntu:

sudo apt install rsync

On some distributions rrsync is delivered as a gzipped file. If so, locate the file and if necessary take an uncompressed copy. This is for Ubuntu (the cd makes sure we are in our home directory for this):

gunzip -c /usr/share/doc/rsync/scripts/rrsync.gz > rrsync

Copy the program to a suitable location in the default path that the user will get when they log in. The rrsync authors suggest putting the program in /usr/local/bin and putting a symbolic link to it in /usr/bin,so that’s what we’ll do:

sudo cp rrsync /usr/local/bin
sudo ln -s /usr/local/bin/rrsync /usr/bin/rrsync

Make sure the program is executable!

sudo chmod u+x,go-x /usr/local/bin/rrsync

rrsync is written in Perl, so if you don’t have Perl installed, now is the time to install it. For Ubuntu it is just:

sudo apt install perl

Configure rrsync

There are two items in the rrsync program you may wish or need to change. Near the top of the file (it’s a Perl script, so it’s just text) there are two constants, RSYNC and LOGFILE. Change the first if rsync is somewhere different to normal; change the second if you’d like your rrsync sessions logged somewhere useful.

Using rrsync

With everything set up, we are ready to use rrsync to provide restricted rsync access for our backup. For this exercise, lets assume we need to copy the contents of remote:/var/backup to a local directory /var/remote_backup. If we were using rsync without rrsync on the other end, we would do something like this on our local host:

sudo rsync -av remote:/var/backup/ /var/remote_backup

But our aim here is to restrict rsync to accessing ONLY /var/backup on the remote.

We start by organising a passwordless login for root.

Create a separate key, with no passphrase, for root to use when backing up. Run this command and accept all the defaults:

sudo ssh-keygen -f ~root/.ssh/backup_key

Prepend command="rrsync -ro /var/backup" to the public key (.ssh/, so that it looks something like this:

command="rrsync -ro /var/backup" ssh-rsa AAAAB3NzaC1yc2EAAAADAQ
[... snipped ...]
1+SXasbebkuHCLQYu5R root@local

Append the public key to root’s authorized_keys file on the remote host.

Optionally set up an alias in root’s .ssh/config file on the local host to make sure that the right details are used for these connections. Add this to root’s local .ssh/config file:

Host alias
    User root
    Hostname name_or_IP_of_remote_host
    IdentityFile /root/.ssh/name_of_private_key_file

… where “alias” is the name you want to use for the remote host. It doesn’t have to be the actual name of the remote host (that’s what Hostname is for), but it gets confusing if it’s not at least similar 🙂 You can add in any other options you may need too.

From now on, when using rsync on your local host to copy to or from the remote host, all remote paths are relative to /var/backup. And if you use ssh or scp with the same key, they won’t work – you will get an error saying something like “Not invoked by sshd”.

So the rsync command above becomes this (assuming “remote” is the alias you set up in .ssh/config):

sudo rsync -av remote:/ /var/remote_backup


The “-ro” option to the rrsync command prevents root from writing to the specified directory. This is useful when your backup should only ever read from the directory.

If you wanted rsync to be able to write files into the directory on the remote host, you would leave out the “-ro” option.

rrsync ties the login to one directory only. To access multiple directories they must either all be below the one that rrsync permits, or you will need multiple rrsync setups.

To provide access to the entire filesystem, allow “/“. Doing this pretty much makes using rrsync pointless except that you can provide read-only access to the filesystem with rrsync‘s “-ro” option.

rrsync works with any users of course, it’s just especially useful with root.



If you use ssh-agent (and who doesn’t?) be careful that your agent does not bypass rrsync by providing a different key for the connection!  As a general rule it’s safest to disallow agent forwarding by default, and enable it only when you need it.

If you do have agent forwarding enabled by default, consider disabling it when logging in to the local system in the above setup. You can use “-a” on the ssh command line or put “ForwardAgent no” in your .ssh/config stanza for the local host.


If you have a command that uses a source path of “/” in expectation of rrsync being on the other side of the connection, but rrsync is not in fact limiting you to a specific directory, you could end up copying an entire file system!

This is a simple test you can use in scripts to check if rrsync is in play. It tries to run a harmless command (exit) and looks for “rrsync” in any error messages:

if ssh alias_or_name_of_remote exit 2>&1 | grep -q rrsync ; then
   # rrsync is active for this user on that remote
   # rsync is not active for this user on that remote


I haven’t tested rrsync‘s logging at all.


rrsync seems pretty secure to me, but there may be exploitable weaknesses. If you can do your backups without using passwordless root access, you should.

Leave a Reply

Your email address will not be published. Required fields are marked *