Locking Down rsync Over SSH
For my most recent backup scheme, I needed to lock down rsync over OpenSSH while allowing scheduled syncing. First, my requirements:
- Encrypted file transfer
- Ability to work without interaction
- Secure (i.e. locked down to specific tools/files)
OpenSSH public/private keypairs and rsync over SSH was a logical
starting point, but I was missing a piece to limit the rsync to specific
files: the authorized_keys
command="command"
option.
The Setup
Hostnames have been changed to protect the innocent. For this description:
- coriander is the server, which has some MySQL files we need to back up
- penny is the client, pulling files from coriander using rsync
I regularly SSH to the host I wanted to backup, so this setup needed to
work around a potential key conflict. Easy enough using ~/.ssh/config
on penny:
Host coriander
HostName coriander.example.com
User annika
IdentityFile ~/.ssh/id_dsa
Host mysql-binlog
HostName coriander.example.com
User annika
IdentityFile ~/.ssh/mysql-binlog-key
I run an ssh-agent, so I have to run things through env -i
to prevent
the rsync
from using my normal key.
In this case, I manually copied my key into annika@coriander's
~/.ssh/authorized_keys
file:
ssh-rsa AAAAB3NzaC1yc2EAAAA…BIwAAAQEA2GNx7diU== mysql-binlog-key
Next I prepended some options to this key:
command="~/mysql-backup/rsync-control" ssh-rsa AAAAB3NzaC1yc2EAAAA…BIwAAAQEA2GNx7diU== mysql-binlog-key
My wish is your command="command"
At this point, let's digress to look at the SSH command option. Try this style authorized_keys on your server to echo the environment and exit when the user logs in over SSH:
command="env" ssh-rsa AAAAB3NzaC1yc2EAAAA…BIwAAAQEA2GNx7diU== mysql-binlog-key
SSH to the host and see the output:
ambackstrom@fsck:~:0$ env -i ssh mysql-binlog
SHELL=/bin/bash
SSH_CLIENT=10.0.0.2 56490 22
USER=annika
PATH=/bin:/usr/bin
PWD=/home/annika
SHLVL=1
HOME=/home/annika
SSH_CONNECTION=10.0.0.2 56490 10.0.0.1 22
_=/usr/bin/env
Then once more, specifying a command to run on the server:
ambackstrom@fsck:~:0$ env -i ssh mysql-binlog 'ls -lAF'
SHELL=/bin/bash
SSH_CLIENT=10.0.0.2 56546 22
USER=annika
PATH=/bin:/usr/bin
PWD=/home/annika
SHLVL=1
HOME=/home/annika
SSH_CONNECTION=10.0.0.2 56546 10.0.0.1 22
SSH_ORIGINAL_COMMAND=ls -lAF
_=/usr/bin/env
Our script becomes the middleman between the client and the requested
command, and that command is placed in the $SSH_ORIGINAL_COMMAND
environment variable. We can analyze this command and allow, deny, or
modify it before execution. Given this rsync command:
env -i rsync -avzP mysql-binlogs:/tmp/ /tmp/test/
We get this remote command:
SSH_ORIGINAL_COMMAND=rsync --server --sender -vlogDtprz . /tmp/
Security via Misdirection
Let's place a new middleman script in our authorized_keys file on coriander:
command="~/rsync-control" ssh-rsa AAAAB3NzaC1yc2EAAAA…BIwAAAQEA2GNx7diU== mysql-binlog-key
Within ~/rsync-control
we'll analyze the incoming command and take
some action:
#!/bin/sh
if [ "$SSH_ORIGINAL_COMMAND" = "rsync --server --sender -vlogDtprz . logs/" ] ; then
rsync --server --sender -vlogDtprz . /var/lib/mysql/binlog/
exit $?
fi
exit 1
What we've actually done here is obfuscated the real binary log
directory from the originating command on penny: the client requests
the logs/
directory, but we launch an rsync server for
/var/lib/mysql/binlog/
. If any other remote command is issued, the
script exits with an error code.
I'm sure there are some other great uses for the command option. I know of git server like gitolite that take advantage of this feature, and I'd love to find more.