I've been trying to figure this out for the past week now. (Well, on and off -- I mean, I was in California for a week to attend my cousin's wedding, and as much as I tried, I simply couldn't help yielding to the call of the beach.) Anyway, I'm back at work now and trying to come up with new ways to debug this ish.
I'm trying to set up a cronjob that'd wget a file from the internets everyday and upload it to a server -- via SCP -- where my friend's processing script awaits. [SNIP: Long story about why my friend can't grab this file himself.] The scripting part to grab the file and SCP it to his server was easy to set up. The cronjob to run my script daily was also easy to set up, much to my delight. The automated SCP part, however, proved to be a headache. And as I've not actually seen anything helpful on the internets, I decided to record my process.
Here's the thing: I've been reading over and over again that rsync and ssh cronjobs require ssh keys with empty passphrases... which, as a security practitioner, I simply can't accept in my heart of hearts. I want ssh keys, with passphrases, and I want them usable only for my cronjob. No I don't want to rely on the physical security of my machine, and especially since my machine is one I frequently use to browse the net, I find it hard to believe that physical security is really the only threat I'd be facing.
I'm not going to cover the cronjob creation / verify-it-works process here, although I should mention that I didn't do any mumbo-jumbo other than using
cronjob -e to create and edit my jobs (and
cronjob -l to check what my jobs were). Anyone who needs help with this might
find this useful.
I. Set up SSH keys to work passwordlessly (with a passphrase).
This part of the process is really
all over the net, and there are many variations to these steps depending on the version of SSH, OS environment, etc.. (There is also
a whole lot of fine advice for debugging issues, too.) For the sake of having a complete example set of instructions here...
On the client:
client:~ ev3$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/ev3/.ssh/id_rsa):
Enter passphrase (empty for no passphrase): **********************
Enter same passphrase again: **********************
Your identification has been saved in /home/ev3/.ssh/id_rsa.
Your public key has been saved in /home/ev3/.ssh/id_rsa.pub.
The key fingerprint is:
30:19:f0:4e:e3:24:16:19:fb:00:1c:59:f3:3c:5b:69 ev3@client.local
The key's randomart image is:
+--[ RSA 2048]----+
| .o+_+. |
| o.-B o . |
| ==% E =+ |
| . O O . |
| = S. |
| |
| |
| |
| |
+-----------------+
client:~ ev3$ ls .ssh/
id_rsa id_rsa.pub known_hosts
client:~ ev3$
So now the client has its private key at id_rsa and public key at id_rsa.pub. I've also entered my passphrase (represented in the form of asterisks; you won't see any asterisks, of course). Now to let the server know about the public key... If your client happens to have this handy dandy command, use:
client:~ ev3$ ssh-copy-id -i .ssh/id_rsa.pub ev3@server
which is pretty much the same as doing this on the client:
client:~ ev3$ scp .ssh/id_rsa.pub ev3@server:~/
Password:
id_rsa.pub 100% 397 0.4KB/s 00:00
server:~ ev3$ chmod 700 .ssh/; chmod 600 .ssh/id_rsa; chmod 644 .ssh/id_rsa.pub
client:~ ev3$ ssh ev3@server
Password:
and this on the server:
Last login: Mon Jul 10 22:52:42 2011
server:~ ev3$ cat id_rsa.pub >> .ssh/authorized_keys ; rm id_rsa.pub
server:~ ev3$ chmod 700 .ssh/; chmod 644 .ssh/authorized_keys; chmod 644 .ssh/known_hosts
At this point, you should be able to log in without your password for server. You might need to type in your certificate's password the first time you SSH, but subsequent SSH logins should be all smooth, like this:
client:~ ev3$ ssh ev3@server
Last login: Tue Jul 12 00:07:27 2011 from client
server:~ ev3$
II. Maintaining a persistent passwordless log-in.
This was where I got stuck for a while. With this new capability, I was able to execute my script (say, script.sh in this example) by calling it directly from the commandline interface. But when the cronjob attempted to run, it failed. I poked around at some environment variables and turned on verbose mode in SSH and came away with some important observations:
- The cronjob does not use my user account, although it sort of uses my environment (but not completely). This is important because when we did the chmod 700 .ssh/ on the client earlier, cronjob lacked the privilege to access the keys.
- The cronjob also sought to ask for the passphrase to the key but lacked the interface to do so... Meaning, the cronjob couldn't access the stored passphrase necessary to access my key.
This is where ssh-agent comes in. It caches the login credentials per session but requires that the user 'unlock' the private key for use first. This allows me to use a password with my ssh certificate
and have a passwordless login. (Why that is important to me, I don't know. I have these odd vague security compulsions not dissimilar to exercising or eating healthfully. It is just a "good idea.")
I happen to have had it already, but those who don't may wish to install it. I defer to this document here, which describes the process well:
http://mah.everybody.org/docs/ssh