rsync will happy copy files between servers and will keep the ownership and permissions the same. However, if you aren’t the owner of all of the files then ownership sync requires the rsync on the receiving end (which we’ll call server B) to be running as root. Likewise on the sending server (server A) if we don’t own the files we might not be able to read them and again need to be running as root.

root on server A is easy, we can just use sudo:

sudo rsync -av /var/www server-b.example:/var

root on server B requires a little more finesse, we need to override the remote rsync command to include sudo, this is done with the --rsync-path path option:

sudo rsync -av --rsync-path="/usr/bin/sudo /usr/bin/rsync" /var/www server-b.example:/var

(This presumes you can run sudo without a password on server B, things get a little hairy if you can’t.)

Great. Done. But, what if you can’t log into server B with a password? rsync’s underlying SSH command will prompt for one if it’s allowed, however, it’s quite common to allow SSH key authentication only (under the hood, rsync is using SSH).

So, we need to add SSH Authentication Forwarding to them mix. I’ve covered authentication forwarding before, but in short it allows an SSH client on a server to access the SSH keys store on your local computer. For our transfer to work gracefully, server A needs an SSH private key that will let it in to server B. Storing your SSH private keys on a server is just plain bad. However, if we connect to the server from our local computer with ssh -A, any keys we have added to the ssh-agent with ssh-add will be available to SSH on the server, temporarily giving us the same effect.

A quick security aside, don’t make ssh -A your default behavior. While it’s pretty unlikely, it’s not impossible for your keys to be stolen if you were to SSH into a compromised server. Use Authentication Forwarding with care.

Why is Authentication Forwarding a complication? It depends on an environment variable, SSH_AUTH_SOCK to work. But, by default sudo sanitizes the environment. This is to prevent a whole host of exploits that involve manipulating the environment of a program that runs as root.

Fortunately, you can tell sudo to preserve the environment with the -E which will pass SSH_AUTH_SOCK through. What, you ask, doesn’t that defeat the purpose of sanitization? No. Sanitization is there to prevent people with limited privileges from escalating them via sudo. It’s also on by default to prevent accidents. -E only works if you have sudo ALL privilege. Someone with sudo ALL can do anything they want anyway, so there is low risk in allowing it to be overridden.

So, that’s the long way around to:

sudo -E rsync -av --rsync-path="/usr/bin/sudo /usr/bin/rsync" /var/www server-b.example:/var

Problem solved.