On ssh-agent

Yesterday a friend had an issue with ssh, so after having my dose of sleep, I decided to write about it and share it with the World! :-)

The Problem

If you use ssh, you probably have found an use for RSA/DSA keys. With these, instead of having to type (and send) passwords when you connect to a remote host, you just connect! (more on that later).

But unless your private key is saved unprotected (i.e., without a passphrase) each time you establish a ssh link you will have to type that passphrase. Not much of a gain, right?

But fear not, fellow hackers. ssh-agent to the rescue! This beast will keep an unencrypted copy of our RSA/DSA keys in memory, and to do so we will only have to type the passphrase just once (per session)!

You can even use svn or CVS over ssh, without a hassle.

Now, how does ssh know of the existence of ssh-agent? Well, this is UN*X, right? So, through the environment.

When invoked, ssh will look for this variable in the environment:


So, if this one is set, ssh will try to talk to ssh-agent by means of that socket, and ask for her help to autenticate. This will initiate a Diffie-Hellman handshake, but that’s another story…

Chicken and Egg Problem

When invoked, ssh-agent will become a daemon and provide the following output:

$ ssh-agent
SSH_AUTH_SOCK=/tmp/ssh-BepcqN5028/agent.5028; export SSH_AUTH_SOCK;
echo Agent pid 5029;

So, as of now, if we do nothing more, we well have a ssh-agent running, and nobody will know about it, not even ssh!

We have to affect the environment for everybody to know that we have an agent running, and how to contact it.

But the output of ssh-agent, if properly handed, would do just that!

$ eval `ssh-agent`
Agent pid 5029

Hey, now our environment contains SSH_* variables, that will be inherited by any subshell or subprocess. Including ssh!

If we use our system through a single point of entry, e.g., a single tty, then this setup works for us. But if you use either multiple ttys or a graphical environment with multiple terminal windows, then you have a problem. In these last two scenarios, if you launch a ssh-agent in a terminal, that daemon will not be known in a sibling terminal, so to say. Because there is no way that one process inherits the environment from a sibling process.

If you have to deal with the multiple ttys scenario I recommend the use of ‘keychain’. You will have to tweak a bit your .profile rc files, but otherwise works like a charm. It is a simple wrapper over ssh-agent. RTFM for more on that. (n.b.: F stands for Fine).

Now let’s check the multiple (graphical) terminals scenario I’m talking about rxvt, or xterm, or similar. Well, fortunately, the problem was solved may years ago. The X Window init scripts spawn a ssh-agent for us (if configured to do so), so every shell or process we get in our session is the descendant of an ‘enlightened’ one, i.e., one who got its environment updated to include the SSH_* variables. So we got it! We can right now launch a terminal and type:

$ ssh-add

And dutifully type our passphrase.

Even more, if we use this feature frequently, we can arrange our .xsession to ask for our passphrase just after login.

Sample .xsession:

SSH_ASKPASS=/usr/bin/ssh-askpass ssh-add < /dev/null
exec x-window-manager

This invocation of ssh-add is a little fancy, right? If we install the ssh-askpass software (debian package: ssh-askpass), we can tell ssh-add to use it (via environment) as a means to get the passphrase from us. Otherwise, ssh-add would try to read it from the terminal, which is not connected to the screen/keyboard in this phase of the session setup.

Well, hope that it helps! Feedback always welcome!

pancho horrillo
I do things with computers.