Date: Tue, 28 Aug 2001 23:21:28 -0700
Subject: ssh tip from Nick
User-Agent: Mutt/1.2.5i

Long lines follow:

Let's say for example that you wanted to be able to run a nightly rsync job to a backup server in another time zone. You need to be able to run it unattended, from cron. You also need to run it as root, so that you can preserve uids and suchforth.

First step: make an ssh key that has no passphrase. This is easy. Just hit return when it asks you for a passphrase. It'll generate public and private keys, but the private key is the *actual secret* used by the client. Ordinarily the private key is something which, when hashed somehow with the passphrase, generates the client-side secret in s00p3r s33kr1t protected core.

Second step: Put the private key in someplace like /root/.ssh/backup.hostname.identity and head on over to the remote machine.

Third step: On your friendly box of the future (or past, as the zone may be), put the that was generated in /root/.ssh/authorized_keys. Also make sure that root logins are set to nopwd rather than no.

Fourth step: by hand, run something like:

rsync -azxS -e ssh --stats --partial --delete /path/to/important/stuff/

Fifth step: check in the auth.log (or whatever ssh logs to) on the Canuck box. You'll see a line like:

23:42:29.213.321.23: UNIMPORTANT LOG MESSAGE: ssh running as root the blahblahblah "rsync --server -logDtprxSz --delete --partial . /usr/local/var/backup/myass/"

Sixth step: now that you have the ability to run the rsync, you need to restrict that key to just your host and just that command.

From="reverse.lookup.mydomain.tld",command="rsync --server -logDtprxSz --delete --partial . /usr/local/var/backup/myass/",no-port-forwarding,no-X11-forwarding,no-agent-forwarding 1024 35 321409218374093218740921865032196GARGARGRGARGRAAGRAGRAGAGRAGAGARGAGAGRAGRAGRAGRAGRAGRAGRAGRAGRAGRAGRAGRAGRAGRGRAGRAGRAGRAGRAGRAGRAGRAGRAGRAGRAGRAGRGRAGRAGRAGRAGRAGRAGRAGRAGRAGRAGRAGRAGRAGR9832170921856243432150643216432109584521020931576432061294360321496504321650921609214360921029810678383849283472401235948362 root@zork

Any number of comma-separated options are allowed before a public key in an authorized_keys file. This lists a particularly paranoid set. It only allows you in if you reverse lookup to reverse.lookup.mydomain.tld, and it will ONLY run the rsync --server... command. No matter WHAT command you specify, it will IGNORE it and run this rsync command.

Seventh step: Create a script like /root/bin/backup-to-canuck-server and put the following in:

ssh-add /root/.ssh/backup.hostname.identity &> /dev/null
rsync -azxS -e ssh --stats --partial --delete /path/to/important/stuff/

then, in /etc/cron.daily/canuck-backup put:

/usr/bin/ssh-agent /root/bin/backup-to-canuck-server

Date: Mon, 26 Jul 1999 16:24:15 -0700
From: Nick Moffitt
Subject: SSH and rsync.

Okay, so David Mandala asked me to write up a short description of the security model for running automated rsync sessions over SSH, and Jim Dennis asked if I might write up a more detailed article for the knowledgebase or something. This is more of the former, partly to assist David Mandala, and partly to document my work to Rick, Ed, and David Welton.

The issue is that you want to run rsync to synchronize one machine's volumes with another. You want to make sure that the session is encrypted, and you want to make sure that you don't open up any unnecessary trust between the two machines. You also want to make this an automated process, so banging in passwords and passphrases by hand is out of the question.

This seems like a difficult task. In the old days you'd just use .rhosts and assume that the machines are equivalent anyway. Nowadays, the world is too hostile for rhosts authentication. SSH provides .shosts functionality, but that's a little too broad for what we want.

So we want to set up an SSH user identity that has no passphrase. This allows us to use the ssh-add command to add the key to our authentication agent (see the ssh-agent man page) from a script. The problem is that this allows anyone who has achieved root access on machine A to ssh over to machine B without having to know the passphrase.

Fortunately, we only want this key to allow us to do one thing. We want it to allow us to use rsync to mirror a single directory tree (let's say /var/www/ in our case). The command from the client side is as follows:

nice rsync -oavzS -e ssh --delete www-dev:/var/www/ /var/www/

The exact meaning of the switches to the rsync command is left as an interesting exercise for the reader. We nice the rsync command because rsync uses 128-bit MD4 hashes to determine differences between two files. It will run these hashes on smaller and smaller portions of a file to determine where the change was made, meaning that you have a number of hashes calculated for each file. This is not a problem when you spend most of your time sending files over the network, but when the remote and local volumes are virtually identical, this can lead to heavy load.

Now, a quick check of the logs shows that when this is run locally, an ssh session is opened up on the remote machine that looks like so (long line wrapped for visual clarity):

Jul 26 06:31:26 www-dev sshd[4948]: log: executing remote command as root: rsync --server --sender -vlogDtprSz --delete . /var/www/

This is the server command that is designed to fetch a list of files and their MD4 hashes to send over to the local machine. Thus, we want this key we have generated to only allow us to run "rsync --server --sender -vlogDtprSz --delete . /var/www/" on the remote machine.

Fortunately, the sshd man page tells of some extra switches in .ssh/authorized_keys that we can use to restrict the power of the key being authorized. One in particular is the "command=" switch, which specifies the exact command line that is run when someone connects with the key listed afterward. This means that even if someone tries to run bash (or some other command) using the key designed for rsyncing /var/www/, the remote machine will run the rsync server process anyway.

There are also some other things we should turn off for security, such as port forwarding, X11 forwarding, and the allocation of a pty for the running program (after all, it's not an interactive session!).

The final entry in /root/.ssh/authorized_keys on the remote machine looks like this (apologies for the long line):

command="rsync --server --sender -vlogDtprSz --delete . /var/www/",no-pty,no-X11-forwarding,no-agent-forwarding,no-port-forwarding 1024 35123491740004821284687051406770121367391847931595262605920868396156297288723805162680928953478117697432491314270431546720341455981363200396633600263595545530493722849546830931775641932930460953547006430925256387203938003411269909804813914988663653971783658926729793633542640561348850537562828453646349561009653 root@www

So, we have the "command=" switch, and the turning off of pty allocation, X11 forwarding, agent forwarding, and port forwarding. These switches are separated by commas, and a single space separates the switches from the key to apply them to.

The problem is that the volume to synchronize is listed in the remotely-run rsync command. This means that we have to repeat the process for every volume we wish to back up. I am currently synchronizing /var/www/, /usr/local/, and /home/, with plans to do more in the future. I created three keys, and wrote a little script, which follows:

ssh-add /root/.ssh/identity.rsync.usr.local
nice rsync -oavzS -e ssh --delete www-dev:/usr/local/ /usr/local/
ssh-add -d /root/.ssh/identity.rsync.usr.local
ssh-add /root/.ssh/identity.rsync.home
nice rsync -oavzS -e ssh --delete www-dev:/home/ /home/
ssh-add -d /root/.ssh/identity.rsync.home
ssh-add /root/.ssh/identity.rsync.var.www
nice rsync -oavzS -e ssh --delete www-dev:/var/www/ /var/www/
ssh-add -d /root/.ssh/identity.rsync.var.www

The ssh-add command requires that the script be run under an ssh authentication agent, so my crontab looks like this:

0-59/5 * * * * /usr/bin/ssh-agent /root/ttt

Ultimately, the script will be moved out of /root/ and named something other than "ttt" (my metasyntactic variable), but right now I am still testing things out.

If anyone has any further questions, I'd be happy to address them. I realize that this is a little hairy, and my terminology was rather loose. I'd be happy to explain this in person if necessary as well.

The same sort of trick for the GPLed rdiff utility instead of rsync ( has been described here:

Date: Wed, 28 May 2003 13:31:23 -0400
From: Adam Ophir Shapira (
Subject: Re: ssh remote command

I had the same problem as you at first. (Obviously, script-tameness wasn't a design-goal for any of the SSH protocols.)

Then I searched, and followed the URL:

I followed the instructions there to the letter, using <ssh-keygen>
to generate the public and private key.

The private key was saved onto "${HOME}/.ssh/id_dsa" on my local machine and the public key was saved as "${HOME}/.ssh/". I did not enter any passphrase (because as we both know, that isn't acceptable for scripting purposes).

Then I went into my home directory on the local machine, and typed the following commands

%> chmod -R g-rwx .ssh
%> chmod -R o-rwx .ssh

After that, I entered the ".ssh" directory, and then used SFTP to upload the "" file into the *remote* machine's ${HOME} directory, labeling it "tmp.txt" using the following command (after logging on via SFTP): > put tmp.txt

Then I connected to the remote machine via SSH (shell access) and typed the following commands:

%> mkdir -p .ssh
%> cd .ssh
%> cat ../tmp.txt >> authorized_keys
%> rm ../tmp.txt

That way, my local "${HOME}/.ssh/" became one line on the remote macine's "${HOME}/.ssh/authorized_keys": And ever since, I've had no problem.

[RM adds: This subject was also the subject of a more-extensive write-up by Brian Hatch:

That was part of a series of articles that also included this follow-on about "authprogs", a custom Perl script that can provide a very secure and easily configured general solution:

It also was extensively detailed in Barry O'Donovan's Linux Gazette article, as revised.]