OpenSSH public-keys, ssh-agent and Keychain

I have always though that ssh-agent has some limitations. One of those limitations is that when invoked from .bashrc or .zshrc in the following way:

`eval ssh-agent`

will cause one ssh-agent instance to be spawned for every shell, which is a waste of resources. An easy solution is to use Keychain, which is also described here.

Basically, Keychain is a wrapper for ssh-agent. Keychain will start a ssh-agent and tell it to load one or several private keys. Additionally, Keychain will create two shell scripts into ${HOME}/.keychains named ${HOST}-sh (for SH-compatible shells) and ${HOST}-csh (for CSH-compatible shells) that can be sourced, for example, from within .bashrc, .zshrc or .cshrc, in order to set up the environment variables required for ssh-agent to be usable by other tools like ssh.

A typical ${HOME}/.keychains/${HOST}-sh file looks like this:

SSH_AUTH_SOCK=/tmp/ssh-AIVkg1MfHH/agent.942; export SSH_AUTH_SOCK;
SSH_AGENT_PID=943; export SSH_AGENT_PID;

Adding the following lines at the end of .bashrc or .zshrc will get Keychain invoked automatically by the shell:

### KEYCHAIN ###
/opt/local/bin/keychain ~/.ssh/id_dsa
source ~/.keychain/${HOST}-sh

Keychain will search for an existing ssh-agent process. If no existing ssh-agent process exists, Keychain will spawn one telling it to load one or several private keys (passed as parameters to Keychain). Next, Keychain will update ${HOME}/.keychain/${HOST}-sh and ${HOME}/.keychain/${HOST}-csh to set up the proper environment variables and their corresponding values.

Kudos to Daniel Robbins — the original author — and Aron Griffis — the current Gentoo mantainer. This neat piece of software is extremely useful to me and I use it every day 🙂

Reusing existing OpenSSH v4 connections

Reusing existing OpenSSH v4 connections comments a very interesting feature of OpenSSH 4: reusing open connections.

  • ControlMaster

    Enables the sharing of multiple sessions over a single network connection. When set to “yes” ssh will listen for connections on a control socket specified using the ControlPath argument.

  • ControlPath

    Specify the path to the control socket used for connection sharing as described in the ControlMaster section above or the string “none” to disable connection sharing. In the path, ‘%h’ will be substituted by the target host name, ‘%p’ the port and ‘%r’ by the remote login username. It is recommended that any ControlPath used for opportunistic connection sharing include all three of these escape sequences.

Assume that you’re on the host itchy and you wish to connect multiple times to the host scratchy.

Connect the first time with :

ssh scratchy  -M -S /tmp/%r@%h:%p

Here we’ve set two options:

  • -M

    This is setting the “ControlMaster” option.

  • -S /tmp/%r@%h:%p

    This is the setting for the ControlPath specifying that we should save the master socket as /tmp/user@hostname:port.

Now that we’ve setup the master connection we can connect a second time with:

ssh scratchy -S /tmp/%r@%h:%p

This time the connection is immediate. There is no option negotiation, etc, taking place. We can verify this by adding a

-v

flag:

skx@itchy:~$ ssh -v  scratchy -S /tmp/%r@%h:%p
OpenSSH_4.2p1 Debian-5, OpenSSL 0.9.8a 11 Oct 2005
debug1: Reading configuration data /home/skx/.ssh/config
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: Applying options for *

Linux scratchy.my.flat 2.6.8-1-386 #1 ...

The programs included with the Debian GNU/Linux system are free software;
...
...
snip

There we see the connection just occurs almost immediately, with none of the usual OpenSSH negotiation taking place.

Rather than messing around upon the command line we can setup these options within the configuration file .ssh/config, simply add a new stanza reading:

Host *
  ControlPath /tmp/%r@%h:%p

Now we can connect as normal, so long as we make the first connection to any host with

-M

(for “Master”) all subsequent connections will be much faster.

Cool, huh?

If you don’t think you can remember to specify the

-M

flag for the first one then you can also force this by setting your options to:

Host *
  ControlMaster auto
  ControlPath /tmp/%r@%h:%p

(Using autoask instead of auto will force the connection to prompt you whether you wish to setup a socket)