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?
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
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
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.
/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:
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):
cd 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
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,
LOGFILE. Change the first if
rsync is somewhere different to normal; change the second if you’d like your
rrsync sessions logged somewhere useful.
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
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
command="rrsync -ro /var/backup" to the public key (
.ssh/backup_key.pub), 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
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
scp with the same key, they won’t work – you will get an error saying something like “Not invoked by sshd”.
rsync command above becomes this (assuming “remote” is the alias you set up in
sudo rsync -av remote:/ /var/remote_backup
-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 “
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
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 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 ... else # rsync is not active for this user on that remote ... fi
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.