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):
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
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/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 .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
Variations
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.
Warnings!
ssh-agent
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.
paths
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
logging
I haven’t tested rrsync
‘s logging at all.
security
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.