Zlatan Omerovic
Zlatan Omerovic

Reputation: 4097

How to check if ssh-agent is already running in bash?

I have a sample sh script on my Linux environment, which basically run's the ssh-agent for the current shell, adds a key to it and runs two git commands:

#!/bin/bash
eval "$(ssh-agent -s)"
ssh-add /home/duvdevan/.ssh/id_rsa

git -C /var/www/duvdevan/ reset --hard origin/master
git -C /var/www/duvdevan/ pull origin master

Script actually works fine, but every time I run it I get a new process so I think it might become a performance issue and I might end up having useless processes out there.

An example of the output:

Agent pid 12109
Identity added: /home/duvdevan/.ssh/custom_rsa (rsa w/o comment)

Also, along with all this, is it possible to find an existing ssh-agent process and add my keys into it?

Upvotes: 98

Views: 169283

Answers (15)

Grwlf
Grwlf

Reputation: 956

Regarding finding running ssh-agents, previous answers either don't work or rely on a magic file like $HOME/.ssh_agent. These approaches require us to believe that user never run agents without saving their output to this file.

My approach instead relies on a rarely changed default UNIX domain socket template to find an accessible ssh-agent among available possibilities.

# (Paste the below code to your ~/.bash_profile and ~/.bashrc files)
C=$SSH_AUTH_SOCK
R=n/a
unset SSH_AUTH_SOCK
for s in $(find $C /tmp/ssh-*/agent.* -user "${USER}" 2>/dev/null | sort -u) ; do
  if SSH_AUTH_SOCK=$s ssh-add -l 2>/dev/null >/dev/null ; then R=$? ; else R=$? ; fi
  case "$R" in
    0|1) export SSH_AUTH_SOCK=$s ; break ;;
  esac
done
if ! test -S "$SSH_AUTH_SOCK" ; then
  eval $(ssh-agent -s)
  unset SSH_AGENT_PID
  R=1
fi
echo "Using $SSH_AUTH_SOCK"
if test "$R" = "1" ; then
  ssh-add
fi

In this approach, SSH_AGENT_PID remains unknown, since it is hard to deduce it for non-roots. I assume it is actually not required for users since they don't normally want to stop agents. On my system, setting SSH_AUTH_SOCK is enough to communicate with agent for e.g. passwordless authentication. The code should work with any shell-compatible shell.

Upvotes: 3

AndrewD
AndrewD

Reputation: 5228

To check if ssh-agent is already running in bash?

Here's what works for me:

if ps -p $SSH_AGENT_PID > /dev/null
then
   echo "ssh-agent is already running"
   # Do something knowing the pid exists, i.e. the process with $PID is running
else
eval `ssh-agent -s`
fi

This was taken from here

Upvotes: 80

Wizard.Ritvik
Wizard.Ritvik

Reputation: 11710

On Mac, I had the same issue as the OP, and I had multiple identities set up and when I was running git push (even with no local changes) I saw an error message:

$ git push
ERROR: Write access to repository not granted. fatal: Could not read from remote repository.

Turns out, the issues that I had were twofold:

  1. When I ran ssh-add -l, I could see there was an extraneous identity - i.e. one which didn't exist anymore. That is, I thought I deleted it earlier, but it was still registered on ssh-agent.
  2. I had too many running ssh-agent processes (normally there should only be 1-2 running) when I ran pgrep ssh-agent.

The fix it, the solution was rather simple. First, it is helpful to authorize the SSH key on GitHub.

If using Enterprise Cloud, you might need to authorize the SSH key for use with SAML.

Under Settings > SSH and GPG keys on your target GitHub account, find your SSH key and ensure SSO is enabled.

Choose Configure SSO and Authorize - see image below.

Configure SSO for SSH Key

Next, to be safe I closed most if not all open terminal windows.

Then to kill all those running ssh-agent processes, I ran:

kill $(pgrep ssh-agent)

Then, I started a new window and when it said:

$ ssh-add -l
The agent has no identities

If SSH_AUTH_SOCK is unset, you might need to first start up ssh-agent. Note tthat I didn't need to run this command myself.

test -z "$SSH_AUTH_SOCK" && eval "$(ssh-agent -s)"

I just add them back with ssh-add:

ssh-add ~/.ssh/id_ed25519_<user>

I can confirm that ssh-add -l now lists all identities after restarting a new terminal window.


If anyone still runs into issues, there could be a problem with the clone or push URL for a project. For example, using HTTPS instead of SSH.

Note that insteadOf in git config does not chain. This post (along with its answer) might be helpful.

Upvotes: 1

Zlatan Omerovic
Zlatan Omerovic

Reputation: 4097

The answer from AndrewD was the most helpful for me in most cases, but I found out that when $SSH_AGENT_PID is not set or present, the ps command will respond with the error:

error: list of process IDs must follow -p

To fix this, we need to check the existence of the $SSH_AGENT_PID variable so that the condition case will now be:

if [ -n "$SSH_AGENT_PID" ] && ps -p "$SSH_AGENT_PID" > /dev/null

The entire modified snippet:

if [ -n "$SSH_AGENT_PID" ] && ps -p "$SSH_AGENT_PID" > /dev/null
then
   echo "ssh-agent is already running"
   # Do something knowing the pid exists, i.e. the process with $PID is running
else
eval `ssh-agent -s`
fi

Shorter and revised version:

if ! [ -n "$SSH_AGENT_PID" ] || ! ps -p "$SSH_AGENT_PID" > /dev/null
then
    eval "$(ssh-agent -s)"
fi

ssh-add ~/.ssh/github_ed25519

Upvotes: 0

Bathinda Helper
Bathinda Helper

Reputation: 207

Very simple command to check how many processes are running for ssh-agent (or any other program): pidof ssh-agent or: pgrep ssh-agent

And very simple command to kill all processes of ssh-agent (or any program): kill $(pidof ssh-agent)

Upvotes: 2

SebMa
SebMa

Reputation: 4757

I've noticed that having a running agent is not enough because sometimes, the SSH_AUTH_SOCK variable is set or pointing to a socket file that does not exist anymore.

Therefore, to connect to an already running ssh-agent on your machine, you can do this :

$ pgrep -u $USER -n ssh-agent -a
1906647 ssh-agent -s
$ ssh-add -l
Could not open a connection to your authentication agent.
$ test -z "$SSH_AGENT_PID" && export SSH_AGENT_PID=$(pgrep -u $USER -n ssh-agent)
$ test -z "$SSH_AUTH_SOCK" && export SSH_AUTH_SOCK=$(ls /tmp/ssh-*/agent.$(($SSH_AGENT_PID-1)))
$ ssh-add -l
The agent has no identities.

Upvotes: 1

Friek
Friek

Reputation: 1541

If you want it to be killed right after the script exits, you can just add this after the eval line:

trap "kill $SSH_AGENT_PID" exit

Or:

trap "ssh-agent -k" exit

$SSH_AGENT_PID gets set in the eval of ssh-agent -s.

You should be able to find running ssh-agents by scanning through /tmp/ssh-* and reconstruct the SSH_AGENT variables from it (SSH_AUTH_SOCK and SSH_AGENT_PID).

Upvotes: 10

idbrii
idbrii

Reputation: 11966

Also, along with all this, is it possible to find an existing ssh-agent process and add my keys into it?

Yes. We can store the connection info in a file:

# Ensure agent is running
ssh-add -l &>/dev/null
if [ "$?" == 2 ]; then
    # Could not open a connection to your authentication agent.

    # Load stored agent connection info.
    test -r ~/.ssh-agent && \
        eval "$(<~/.ssh-agent)" >/dev/null

    ssh-add -l &>/dev/null
    if [ "$?" == 2 ]; then
        # Start agent and store agent connection info.
        (umask 066; ssh-agent > ~/.ssh-agent)
        eval "$(<~/.ssh-agent)" >/dev/null
    fi
fi

# Load identities
ssh-add -l &>/dev/null
if [ "$?" == 1 ]; then
    # The agent has no identities.
    # Time to add one.
    ssh-add -t 4h
fi

This code is from pitfalls of ssh agents which describes both the pitfalls of what you're currently doing, of this approach, and how you should use ssh-ident to do this for you.


If you only want to run ssh-agent if it's not running and do nothing otherwise:

if [ $(ps ax | grep [s]sh-agent | wc -l) -gt 0 ] ; then
    echo "ssh-agent is already running"
else
    eval $(ssh-agent -s)
    if [ "$(ssh-add -l)" == "The agent has no identities." ] ; then
        ssh-add ~/.ssh/id_rsa
    fi

    # Don't leave extra agents around: kill it on exit. You may not want this part.
    trap "ssh-agent -k" exit
fi

However, this doesn't ensure ssh-agent will be accessible (just because it's running doesn't mean we have $SSH_AGENT_PID for ssh-add to connect to).

Upvotes: 70

zwtop
zwtop

Reputation: 338

cat /usr/local/bin/ssh-agent-pro << 'EOF'
#!/usr/bin/env bash
SSH_AUTH_CONST_SOCK="/var/run/ssh-agent.sock"

if [[ x$(wc -w <<< $(pidof ssh-agent)) != x1 ]] || [[ ! -e ${SSH_AUTH_CONST_SOCK} ]]; then
  kill -9 $(pidof ssh-agent) 2>/dev/null
  rm -rf ${SSH_AUTH_CONST_SOCK}
  ssh-agent -s -a ${SSH_AUTH_CONST_SOCK} 1>/dev/null
fi

echo "export SSH_AUTH_SOCK=${SSH_AUTH_CONST_SOCK}"
echo "export SSH_AGENT_PID=$(pidof ssh-agent)"
EOF
echo "eval \$(/usr/local/bin/ssh-agent-pro)" >> /etc/profile
. /etc/profile

then you can ssh-add xxxx once, you can use ssh-agent everytime when you login.

Upvotes: 1

trollo974
trollo974

Reputation: 1

I made this bash function to count and return the number of running ssh-agent processes... it searches ssh-agent process using procfs instead of using $ ps -p $SSH_AGENT_PID:cmd or $SSH_AUTH_SOCK:var ... (these ENV-var. can still be set with old values while ssh-agent's process is already killed: if $ ssh-agent -k or $ $(ssh-agent -k) instead of $ eval $(ssh-agent -k))

function count_agent_procfs(){
    declare -a agent_list=( ) 
    for folders in $(ls -d /proc/*[[:digit:]] | grep -v /proc/1$);do
        fichier="${folders}/stat"
        pid=${folders/\/proc\//}
        [[ -f ${fichier} ]] && [[ $(cat ${fichier} | cut -d " " -f2) == "(ssh-agent)" ]] && agent_list+=(${pid})
    done
    return ${#agent_list[@]}
}

..and then if there is a lot of ssh-agent process running you get their PID with this list..."${agent_list[@]}"

Upvotes: 0

eQ19
eQ19

Reputation: 10711

Using $SSH_AGENT_PID can only test the ssh-agent but miss identities when it is not yet added

$ eval `ssh-agent`
Agent pid 9906
$ echo $SSH_AGENT_PID
9906
$ ssh-add -l
The agent has no identities.

So it would be save to check it with ssh-add -l with an expect script like example below:

$ eval `ssh-agent -k`
Agent pid 9906 killed
$ ssh-add -l
Could not open a connection to your authentication agent.
$ ssh-add -l &>/dev/null
$ [[ "$?" == 2 ]] && eval `ssh-agent`
Agent pid 9547
$ ssh-add -l &>/dev/null
$ [[ "$?" == 1 ]] && expect $HOME/.ssh/agent
spawn ssh-add /home/user/.ssh/id_rsa
Enter passphrase for /home/user/.ssh/id_rsa: 
Identity added: /home/user/.ssh/id_rsa (/home/user/.ssh/id_rsa)
$ ssh-add -l
4096 SHA256:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX /home/user/.ssh/id_rsa (RSA)

So when both ssh-agent and ssh-add -l are put to run on a bash script:

#!/bin/bash
ssh-add -l &>/dev/null
[[ "$?" == 2 ]] && eval `ssh-agent`
ssh-add -l &>/dev/null
[[ "$?" == 1 ]] && expect $HOME/.ssh/agent

then it would always check and assuring that the connection is running:

$ ssh-add -l
4096 SHA256:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX /home/user/.ssh/id_rsa (RSA)

You can also emulate the repeating of commands on above script with do while

Upvotes: 8

Dayne Broderson
Dayne Broderson

Reputation: 21

Thanks to all the answers here. I've used this thread a few times over the years to tweak my approach. Wanted to share my current ssh-agent.sh checker/launcher script that works for me on Linux and OSX.

The following block is my $HOME/.bash.d/ssh-agent.sh

function check_ssh_agent() {
  if [ -f $HOME/.ssh-agent ]; then
    source $HOME/.ssh-agent > /dev/null
  else
    # no agent file
    return 1
  fi

  if [[ ${OSTYPE//[0-9.]/} == 'darwin' ]]; then
    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 $?
  fi

  if [ -d /proc/$SSH_AGENT_PID/ ]; then
    # verify PID dir is actually an agent
    grep ssh-agent /proc/$SSH_AGENT_PID/cmdline  > /dev/null  2> /dev/null; 
    if [ $? -eq 0 ]; then
      # yep - that is an agent
      return 0
    else
      # nope - that is something else reusing the PID
      return 1
    fi
  else
    # agent PID dir does not exist - dead agent
    return 1
  fi 
}

function launch_ssh_agent() {
  ssh-agent > $HOME/.ssh-agent
  source $HOME/.ssh-agent
  # load up all the pub keys
  for I in $HOME/.ssh/*.pub ; do
    echo adding ${I/.pub/}
    ssh-add ${I/.pub/}
  done
}

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

I launch the above from my .bashrc using:

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

Hope this helps others get up and going quickly.

Created a public gist if you want to hack/improve this with me: https://gist.github.com/dayne/a97a258b487ed4d5e9777b61917f0a72

Upvotes: 2

SveborK
SveborK

Reputation: 55

The accepted answer did not work for me under Ubuntu 14.04.

The test to check if the ssh-agent is running I have to use is:

[[ ! -z ${SSH_AGENT_PID+x} ]]

And I am starting the ssh-agent with:

exec ssh-agent bash

Otherwise the SSH_AGENT_PID is not set.

The following seems to work under both Ubuntu 14.04 and 18.04.

#!/bin/bash
sshkey=id_rsa
# Check ssh-agent
if [[ ! -z ${SSH_AGENT_PID+x} ]]
then
    echo "[OK] ssh-agent is already running with pid: "${SSH_AGENT_PID}
else
    echo "Starting new ssh-agent..."
    `exec ssh-agent bash`
    echo "Started agent with pid: "${SSH_AGENT_PID}
fi
# Check ssh-key
if [[ $(ssh-add -L | grep ${sshkey} | wc -l) -gt 0 ]]
then
    echo "[OK] SSH key already added to ssh-agent"
else
    echo "Need to add SSH key to ssh-agent..."
    # This should prompt for your passphrase
    ssh-add ~/.ssh/${sshkey}
fi

Upvotes: 2

iceman
iceman

Reputation: 301

ps -p $SSH_AGENT_PID > /dev/null || eval "$(ssh-agent -s)"

Single line command. Run for the first time will start ssh-agent. Run for the second time will not start the ssh-agent. Simple and Elegant Mate !!!

Upvotes: 12

alok
alok

Reputation: 510

You can modify line #1 to:

PID_SSH_AGENT=`eval ssh-agent -s | grep -Po "(?<=pid\ ).*(?=\;)"`

And then at the end of the script you can do:

kill -9 $PID_SSH_AGENT

Upvotes: -1

Related Questions