Skip to content

Instantly share code, notes, and snippets.

@dayne
Last active September 28, 2024 05:38
Show Gist options
  • Save dayne/a97a258b487ed4d5e9777b61917f0a72 to your computer and use it in GitHub Desktop.
Save dayne/a97a258b487ed4d5e9777b61917f0a72 to your computer and use it in GitHub Desktop.
ssh-agent checker/launcher

ssh-agent checker/launcher

About

A bash script for managing ssh-agent that I use for Ubuntu and OSX.

Started as a variation from this classic on Stack Overflow How to check if ssh-agent is already running and then adapted it to align with behaivor I want.

This version does the following:

  • checks for ssh agent forward (remote login) and if so does not setup ssh-agent or any keys
  • checks if there is an existing agent running and if so uses it
  • otherwise launches a new agent
  • then, if it is an interactive shell, loads keys:
    • priority is to load only $HOME/.ssh/id_rsa-${HOSTNAME} key
    • if that isn't found it loads all public keys: .ssh/*.pub

New features

In a 2024 refactor

  • I cleaned up the function names to better namespace them as the script is sourced.
  • Added ability to ./ssh-agent.sh and detect that mode to trigger a bunch of debug text.
  • Added ability to ./ssh-agent.sh update to pull down latest version and put in .bash.d

Setup

I put the script into ~/.bash.d/ssh-agent.sh and launch the script from my .bashrc using:

if [ -d $HOME/.bash.d ]; then
  for I in $HOME/.bash.d/*.sh; do
    source $I  
  done
fi

or you can put script as $HOME/.ssh-agent.sh do the one liner in .bashrc

curl -o $HOME/.ssh-agent.sh https://gist.githubusercontent.com/dayne/a97a258b487ed4d5e9777b61917f0a72/raw/ssh-agent.sh
echo "source $HOME/.ssh-agent.sh" >> $HOME/.bashrc
test -f $HOME/.ssh-agent.sh && source ${_}

example setup for a new user

mkdir .bash.d
cd .bash.d
curl -o  ssh-agent.sh https://gist.githubusercontent.com/dayne/a97a258b487ed4d5e9777b61917f0a72/raw/ssh-agent.sh
cd ..
nano .bashrc 
# add the if/for loop shown above for .bash.d as needed

Scroll to bottom of .bashrc (mouse down) and insert (paste) the above example.

#!/usr/bin/bash
# Dayne's .bash.d/ssh-agent.sh
# https://gist.github.com/dayne/a97a258b487ed4d5e9777b61917f0a72"
function _drc_update-ssh-agentrc() {
gist_url="https://gist.github.com/dayne/a97a258b487ed4d5e9777b61917f0a72"
file_url="https://gist.githubusercontent.com/dayne/a97a258b487ed4d5e9777b61917f0a72/raw/ssh-agent.sh"
bak_day=$(date +%Y%m%d)
echo gist_url=$gist_url
echo file_url=$file_url
echo bak_day=$bak_day
if [ -f $HOME/.bash.d/ssh-agent.sh ]; then
if [ -f $HOME/.bash.d/ssh-agent.backup.$bak_day ]; then
echo "skipping backup: file exists .bash.d/ssh-agent.backup.$bak_day"
else
cp -v "$HOME/.bash.d/ssh-agent.sh" "$HOME/.bash.d/ssh-agent.backup.$bak_day"
fi
fi
if [ -d $HOME/.bash.d ]; then
echo "Downloading: $file_url"
curl -s -o /tmp/ssh-agent.sh "${file_url}"
if [[ $? -eq 0 ]]; then
cp /tmp/ssh-agent.sh $HOME/.bash.d/ssh-agent.sh
echo "installed lastest .bash.d/ssh-agent.sh"
return 0
else
echo "download failed."
return 1
fi
else
echo "not auto download+installing into .bash.d .. .bash.d doesn't exist"
echo "follow the setup steps on the gist:"
echo " $gist_url"
fi
}
function _drc_debug() {
_drc-sshagent-check_interactive
if [[ $DEBUG == "true" ]] ; then
echo "debug: $1"
fi
}
function _drc-sshagent-check_interactive() {
if [[ $- = *i* ]];then
return 0 # interactive
else
return 1 # non-interactive
fi
}
function _drc-sshagent-check_forward_ssh_agent() {
_drc_debug "check_forward_ssh_agent()"
if [[ ! -S ${SSH_AUTH_SOCK} ]]; then
# no: agent forward detected
debug " check_forward_ssh_agent: none detected"
return 1
else
_drc_debug " detected agent forward"
_drc_debug " SSH_AUTH_SOCK=$SSH_AUTH_SOCK"
# SSH_AUTH_SOCK=/run/user/1000/gnupg/S.gpg-agent.ssh sad
if echo $SSH_AUTH_SOCK | grep -q gpg-agent.ssh; then
_drc_debug " gpg-agent detected in SSH_AUTH_SOCK"
if [[ -f $HOME/.gnupg/gpg-agent.conf ]]; then
if grep -q '^enable-ssh-support' ~/.gnupg/gpg-agent.conf; then
_drc_debug " gpg-agent detected with ssh support enabled"
echo "warning: detected using .gnupg/gpg-agent.conf with ssh-support enabled - recommend disabling"
echo ' # Backup the original gpg-agent.conf'
echo ' cp -v ~/.gnupg/gpg-agent.conf ~/.gnupg/gpg-agent.conf.bak'
echo ' # Remove the enable-ssh-support line'
echo ' sed -i '/^enable-ssh-support/d' ~/.gnupg/gpg-agent.conf'
echo ' # Restart gpg-agent to apply changes'
echo ' gpgconf --kill gpg-agent'
return 0
else
_drc_debug " gpg-agent detected but no ssh support turned on - good thing"
unset SSH_AUTH_SOCK
return 1
fi
fi
fi
return 0
fi
}
function _drc-sshagent-check_ssh_agent() {
_drc_debug "check_ssh_agent()"
if [ -f $HOME/.ssh-agent ]; then
_drc_debug "$HOME/.ssh-agent found ... sourcing"
source $HOME/.ssh-agent > /dev/null
else
# no agent file
_drc_debug " no agent file found: ~/.ssh-agent"
return 1
fi
if ! command -v lsof > /dev/null; then
echo " missing lsof - needed for ssh-agent managment - apt install lsof"
exit 1
fi
# thanks to @craighurley for this improvement
# that works on OSX & Ubuntu fine
lsof -p $SSH_AGENT_PID 2> /dev/null | grep -q ssh-agent
return $?
}
function _drc-sshagent-launch_ssh_agent() {
_drc_debug "lauch_ssh_agent()"
ssh-agent > $HOME/.ssh-agent
source $HOME/.ssh-agent
}
function _drc-sshagent-add_keys_to_agent() {
_drc_debug "add_keys_to_agent()"
ssh-add -l > /dev/null
if [ $? -eq 0 ]; then
# ssh-agent already has keys loaded - skipping scan & load logic
return 0
fi
# add ~/.ssh/id_rsa-${HOSTNAME} otherwise add all keys in .ssh
# echo "adding ssh keys"
test -f $HOME/.ssh/id_rsa-${HOSTNAME}.pub && ssh-add ${_/.pub}
if [ $? -ne 0 ]; then
for I in $HOME/.ssh/*.pub ; do
if [ -f ${I} ]; then
echo "adding key: ${I/.pub/}"
ssh-add ${I/.pub/}
fi
done
fi
}
function _drc-sshagent-find_existing_or_launch_new_agent() {
_drc_debug "find_existing_or_launch_new_agent()"
if _drc-sshagent-check_forward_ssh_agent; then
_drc_debug "_drc-sshagent-check_forward_ssh_agent(): agent found"
else
_drc_debug "no forwarded agent found - looking locally"
if _drc-sshagent-check_ssh_agent; then
_drc_debug " ...found agent locally"
else
_drc_debug "no local or forward agent found, launch one"
#### killall ssh-agent # verify there aren't any error state/lost agents running
_drc-sshagent-launch_ssh_agent
#_drc_debug "find or launch: check_ssh_agent: failed to find"
fi
# add keys (only if interactive terminal)
if [[ $- = *i* ]];then
# interactive terminal .. but make sure it isn't tmux first
if [ "$TERM" = "screen" ] && [ -n "$TMUX" ]; then
# tmux session - lets avoid key adding magic here
return 0
else
_drc-sshagent-add_keys_to_agent
fi
else
# non-interactive
return 0
fi
fi
}
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
# not sourced - we are being run
case $1 in
update)
echo "update for ssh-agent.sh called"
_drc_update-ssh-agentrc
;;
*)
DEBUG=true
;;
esac
fi
_drc-sshagent-find_existing_or_launch_new_agent
@craighurley
Copy link

ps -p $SSH_AGENT_PID > /dev/null
# gotcha: does not verify the PID is actually an ssh-agent
# just that the PID is running
return $?

could be re-written as the following to actually check that the process is indeed ssh-agent:

lsof -p $SSH_AGENT_PID | grep -q ssh-agent
return $?

@craighurley
Copy link

craighurley commented Dec 31, 2018

check_ssh_agent
if [ $? -eq 1 ];then
  launch_ssh_agent
fi

could be re-written as:

check_ssh_agent
if [ $? -ne 0 ];then
  killall ssh-agent
  launch_ssh_agent
fi

This will catch any error code. Also, if the PID check in my previous comment fails and there is an existing ssh-agent running on a different PID, it will kill that existing ssh-agent, start a new ssh-agent and write the correct ssh-agent details (including PID) to $HOME/.ssh-agent.

@dayne
Copy link
Author

dayne commented Jan 15, 2019

@craighurley thanks. Incorporated those changes.

The lsof -p works on both Ubuntu and Mac OSX and allowed consistent/simpler logic. Awesome.

Also updated this to handle non-interactive shells (including tmux related shells) better that it had been.

@dayne
Copy link
Author

dayne commented Jan 17, 2020

Jan 17th, 2020

Updated to include debug messages to help debugging weird situations. Turns out you can get a SSH_AUTH_SOCK that isn't connected to a valid ssh-agent. Added a test for that scenario along with ability to toggle on debug messages to help debug why a new valid agent isn't being kicked off.

@gauravcanon
Copy link

Is there any way to pass SSH key password in the script ...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment