I can’t remember who wrote this first, but my friend and I have been using some version of this shell snippet for years:
ssh_find() {
for agent in "$SSH_AUTH_SOCK" /tmp/ssh-*/agent.* /tmp/com.apple.launchd.*/Listeners* /tmp/keyring-*/ssh; do
SSH_AUTH_SOCK="$agent" ssh-add -l 2>/dev/null
case "$?" in # 0 some keys, 1 no keys, 2 no agent
0) export SSH_AUTH_SOCK="$agent"; echo "Found Keys." ; break ;;
1) export SSH_AUTH_SOCK="$agent"; echo "Found Agent."; ;; # Keep looking
esac
done
}
This function looks through /tmp
for ssh-agent sockets (using various macOS, Linux, openssh naming schemes) and then finds the first one that (a) it has permissions for and (b) have keys unlocked in them (giving precedence to any currently active ones). If it can find some valid agents but none of them have keys then it will use the last one it found.
Later in .bashrc
I have a section for interactive shells and I call it from there:
#
# Interactive only stuff:
#
if [[ "$-" =~ i ]]; then
...snipped...
# ssh related stuff
ssh_find
if [ x$SSH_AUTH_SOCK == x ]; then
exec ssh-agent bash
fi
...snipped...
fi
That looks for an agent and starts one up if there were none running1 2.
How is this actually useful?
Basically it makes it so ever tab I open in my terminal (or in a tmux) latches on to an existing ssh-agent so that ssh just works from anywhere without ever thinking about it.
It’s also useful for cron jobs. I have some cron jobs that look like this:
@hourly . $HOME/.bashrc && ssh_find > /dev/null && rsync -a something:/something/blah /blah/blah
I also have similar ones that do some git operation that needs an ssh key.
It is generally convenient for things that need ssh keys to run, but where you don’t necessarily want the keys sitting around on the disk in unencrypted form. This lets them sit unencrypted in RAM, which is a little better (but at the expense of needing to ssh-add them manually every reboot).
- This is kinda gross, since
exec
throws out the entire bash progress and starts things over but with a new running ssh-agent (more or less doubling the startup time). This only happens on interactive logins so it’s not as bad as you might expect. I do it this way instead ofeval $(ssh-agent)
so that if the agent was started by ssh-ing into the machine then it gets killed when the ssh process dies (ie, on logout). - This is also racy—picture the case where a gui terminal program starts up and remembers that you had 7 tabs open, so it opens those 7 tabs and starts shells in them in parallel. On my Debian box I have a super complicated and impossible to read fix for this that involves the
flock(2)
command. I hate it and am embarrassed by the whole thing so fixing the race is left as an exercise to the reader. 🙂