On demand ssh-add

A couple of months ago I restructured my SSH public and private keys in order to clean the mess I had in my ~/.ssh/ directory. After moving keys from here to there and touching ~/.ssh/config in order to reflect this changes I’ve ended up with a beautiful hierarchical layout with ~/.ssh/ directory on top of it.

To my surprise, gnome-keyring stopped saving my SSH passphrases on its SSH agent. After a bit of research I’ve found the following text which I’m quoting directly from the Gnome keyring wiki:

The SSH agent automatically loads files in ~/.ssh which have
corresponding *.pub paired files. Additional SSH keys can be manually
loaded and managed via the ssh-add command.

Basically what this means is that the Gnome keyring’s SSH agent won’t look recursively in ~/.ssh/ directory and, in consequence, I’ll have to manually add my keys to it. The frustration caused by this assertion is best described in Michael J. Schultz’s solution to a very similar problem. To sum it up, I would have to choose between the following two options:

  • load all my keys when starting my desktop environment
  • every time I try to login for the first time into a host, hit Ctrl-C, run ssh-add and then ssh again to the same host.

For the last couple of months I’ve been living the misery of the second option, so I’ve decided to stop suffering and find a proper solution to this problem. Michael’s solution is great when you only have one SSH key, but it wasn’t suitable for me because I’ve a lot.

It isn’t very difficult to take advantage of the information of ~/.ssh/config file and save in the agent all keys referenced in an IdentityFile line. By using this approach it would be possible to save SSH keys in any directory of the file system, and you wouldn’t have to restrict yourself to the ~/.ssh/ directory. Under this premise I’ve developed a tiny Python script called odsa, acronym of “On demand ssh-add” (yeah, I’m very exotic to imagine names), that receives a host as a parameter and loads in the SSH agent the host’s keys configured in ~/.ssh/config. For the sake of illustration here is the relevant piece of code:

odsa = ODSA()
enabled_keys = odsa.keys_for_host(sys.argv[1])
keys_to_load = filter(lambda k: not odsa.is_key_loaded(k), enabled_keys)
if keys_to_load:
    odsa.load_keys(keys_to_load)

The script depends on the paramiko library. In a Debian system you can find it in the python-paramiko package.

odsa has to be called before making an SSH connection. I’ve unsuccessfully tried to find some sort of SSH-preconnect-hook, so I had to go through the dirty way. Fire up your favourite text editor and add a function and an alias like the following ones to your shell rc file (i.e. ~/.bashrc, ~/.zshrc, etc.):

odsa_ssh() {
    odsa $*
    /usr/bin/ssh $*
}

alias ssh=odsa_ssh

Of course, odsa has to be in your $PATH so you may want to install it in ~/bin/ or /usr/local/bin/. The function odsa_ssh makes an assumption that the host you’re trying to connect is in the first received argument. This way works pretty well for me, but you may need to adapt it to suit your needs.

odsa can also be called as a stand-alone application. This could be useful, for example, as a pre-push hook for git or darcs.

That’s it! Enjoy a painless SSH experience. :-)

Download

You can download odsa from my snippets git repo.


Posted on November 24, 2013 by Raúl Benencia
Tags: ssh, python

Comments? Sure! Just send me an email with it and I'll happily include it here.