Friday, January 2, 2015

Invoking ssh-agent Correctly on bash Log-In

I have a confession to make. I like Cygwin. For those of you who are unfamiliar with it, Cygwin provides *NIX support Windows systems, including the bash shell and all those useful UNIX-derived command line utilities (find, grep, cut, sed, tr, awk, etc) which I can't live without even on Windows.

I occasionally use ssh (in particular, OpenSSH) to log onto remote websites (although I admit I just as often use PuTTY now), and also use scp to move files between remote systems. To make this easier, I use ssh-agent to hold private keys (added each session using ssh-add). With this set-up, after I have added each required private key (and entered its password - once), ssh retrieves the password for the site from ssh-agent and no longer requires me to provide login credentials.

By default, ssh-agent creates a socket which it uses to interact with ssh in the tmp dir. I only want to enter each password once, regardless of how many bash sessions I have open, so I specify the location of the socket as ~/..ssh/sshagent.

My first attempt at this was to add the following line in bash_profile:

eval $(ssh-agent -a ~/.ssh/sshagent)

I have been doing this for quite a few years, but, unfortunately, it has two main problems.

First, after the socket is created, the call to ssh-agent fails with a "Permission Denied error". Second, and even worse, bash on Cygwin does not have any mechanism allowing clean-up to take place on shutdown, so subsequent calls to ssh-agent in future sessions (after rebooting) also fail (with an "Address in use" error).


Fortunately, a little bit of bash magic fixes both these problems. Here's the snippet I ended up with.

declare -r fn_ssh_agent=$(cygpath ~/.ssh/sshagent)
declare -r ssh_agent_pid=$(ps |grep ssh-agent | tr -s " " | cut "-d " -f 2)

if [[ -z ssh_agent_pid && -S $fn_ssh_agent ]]
then
    rm $fn_ssh_agent
fi

if [[ ! -S $fn_ssh_agent ]]
then
    eval $(ssh-agent -a $fn_ssh_agent)
else
    export SSH_AUTH_SOCK=$fn_ssh_agent
    export SSH_AGENT_PID=$ssh_agent_pid
fi

This is pretty straightforward. It:
  • Defines a local constant holding the name and path for the ssh-agent socket
  • Defines another local constant with the current PID of the ssh-agent task (if it is running)
  • If the ssh-agent task was not found and the ssh-agent socket exists, delete the socket
  • Finally, if the ssh-agent socket does not exist, create it (the eval portion sets the two bash environment variables SSH_AUTH_SOCK and SSH_AGENT_ID). Otherwise, set these environment variables based on the constants we defined earlier.

No comments:

Post a Comment