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! :-)
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; SSH_AGENT_PID=5029; export SSH_AGENT_PID; 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:
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.
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!