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

15

94

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?

Jolty answered 11/11, 2016 at 13:45 Comment(10)
Try $SSH_AGENT_PID.Forrester
If you want to kill a process after spawning it, you can store its PID into a variable and call it like so: kill -9 $PID_SSH_AGENTAntonetteantoni
I think making the script responsible for starting an agent is the wrong approach. Just assume that an agent is running, and require any user to ensure that they have an agent already (usually started by your initial login shell.)Gonsalez
You could also simply run such a script with ssh-agent my-script to start an agent that exits as soon as my-script exits.Gonsalez
I cannot do that since login to the machine multiple times a day - and not just me, but other people as well.Conspecific
Kill the agent when you logout, or use agent-forwarding to only run the agent on your local machine if you are ssh-ing into the machine.Gonsalez
if you kill the agent process and if the bash session is still open, the variable $SSH_AGENT_PID still holds the pid information. make sure to clear this variable after killing the process.Pammie
Another variant would be to check if there are any agent processes running via ps aux | grep ssh-agent. If you pipe this further to | wc -l and this equals "0", there are no running processes anymore.Pammie
ps -p $SSH_AGENT_PID > /dev/null || eval "$(ssh-agent -s)"Beaker
You'd think the agent itself will have a built-in option to only run a new process if not already running... but no, we have to write some complex scripts!Danialah
H
64

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).

Hetzel answered 29/1, 2018 at 20:26 Comment(6)
This should be the accepted answer. At least the first half, because ssh-add -l is the right way to test for a live agent (I'd rather suggest timeout 0.3 ssh-add -l because ssh-add can hang on starved ssh-connections - i.E. in tmux). Your first script works on KDE or remote sessions with ssh -A. However the second half is more or less useless like all the other answers here, as often there is no ssh-agent running locally. BTW: Why the [s] in grep [s]sh-agent and not grep -F ssh-agent (which spares some cycles).Gradin
"as often there is no ssh-agent running locally" -- except in OP's case where their problem was multiple ssh-agents. "Why the [s]" -- ps ax|grep -F ssh-agent will return the grep process because ps output includes program arguments (try it). Using the character class in the regex will prevent grep from matching itself. See also https://mcmap.net/q/13736/-more-elegant-quot-ps-aux-grep-v-grep-quotHetzel
This is also a cool answer because it tells "how to tell that ssh-agent is dead" or "how to figure out whether ssh-agent is running without knowing the pid". Makes sense when one has screen or tmux in conjunction with agent forwarding.Unscramble
I never considered doing an eval "$(<~/.ssh-agent)" and would rather source ~/.ssh-agent. Just curious if it makes any difference.Dionisio
@chutz: eval is suggested by the ssh-agent docs (likely because they don't use an intermediate file), but I'm not sure if there's a difference. That might make a good question (closest I found was this one).Hetzel
my one-liner in zsh: if [[ $(ssh-add -l &>/dev/null)? -eq 2 ]] ; then eval $(ssh-agent); fiSaddlecloth
O
79

No, really, how to check if ssh-agent is already running in bash?

Answers so far don't appear to answer the original question...

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

Oballa answered 23/8, 2017 at 14:3 Comment(3)
Usually does not work. Neither in graphical sessions (where ssh-agent runs locally or is built into the key manager) or session with ssh -A, where the ssh-agent runs locally. The correct way can be found in the answer of idbriiGradin
Can confirm SSH_AGENT_PID is unreliable. On my Mac (High Sierra) and SSH OpenSSH_7.8p1, when invoking ssh connection directly, e.g. ssh host, the agent is started with SSH_AUTH_SOCK but not SSH_AGENT_PIDLiddle
@AlexanderKucheryuk is right. On Mac M1 on Ventura, this is the case for me. SSH_AUTH_SOCK is always set but SSH_AGENT_PID is always empty or missing. Downvoted the answer as a result.Spiritualty
H
64

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).

Hetzel answered 29/1, 2018 at 20:26 Comment(6)
This should be the accepted answer. At least the first half, because ssh-add -l is the right way to test for a live agent (I'd rather suggest timeout 0.3 ssh-add -l because ssh-add can hang on starved ssh-connections - i.E. in tmux). Your first script works on KDE or remote sessions with ssh -A. However the second half is more or less useless like all the other answers here, as often there is no ssh-agent running locally. BTW: Why the [s] in grep [s]sh-agent and not grep -F ssh-agent (which spares some cycles).Gradin
"as often there is no ssh-agent running locally" -- except in OP's case where their problem was multiple ssh-agents. "Why the [s]" -- ps ax|grep -F ssh-agent will return the grep process because ps output includes program arguments (try it). Using the character class in the regex will prevent grep from matching itself. See also https://mcmap.net/q/13736/-more-elegant-quot-ps-aux-grep-v-grep-quotHetzel
This is also a cool answer because it tells "how to tell that ssh-agent is dead" or "how to figure out whether ssh-agent is running without knowing the pid". Makes sense when one has screen or tmux in conjunction with agent forwarding.Unscramble
I never considered doing an eval "$(<~/.ssh-agent)" and would rather source ~/.ssh-agent. Just curious if it makes any difference.Dionisio
@chutz: eval is suggested by the ssh-agent docs (likely because they don't use an intermediate file), but I'm not sure if there's a difference. That might make a good question (closest I found was this one).Hetzel
my one-liner in zsh: if [[ $(ssh-add -l &>/dev/null)? -eq 2 ]] ; then eval $(ssh-agent); fiSaddlecloth
B
11

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 !!!

Beaker answered 21/4, 2018 at 3:40 Comment(2)
Not bullet proof but quite elegant solution :)Blunder
In some systems you might get a warning if $SSH_AGENT_PID is not set. Also you will get a message after running eval ssh-agent -s. I modified the above to make it completely silent: ps -p "000$SSH_AGENT_PID" > /dev/null || eval "$(ssh-agent -s) > /dev/null"Blunder
S
10

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).

Sparks answered 11/11, 2016 at 14:16 Comment(4)
Why shouldn't I just add kill -9 $SSH_AGENT_PID at the end of my script, as @Antonetteantoni said in his comment on the question?Conspecific
If the script itself gets killed (with an interruptable signal) while running, that command won't be run. With the trap it will.Sparks
Also, kill -9 should never be necessary except for killing a bugging program during development. kill by itself should be sufficient in almost every instance.Gonsalez
Yes. I've just seen that ssh-agent -k kills the process and unsets SSH_AUTH_SOCK and SSH_AGENT_PID variables - I'm using your solution with trap. Thanks!Conspecific
F
6

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

Flaming answered 5/8, 2019 at 3:38 Comment(0)
I
2

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
Incommutable answered 19/9, 2018 at 23:38 Comment(1)
SSH_AGENT_PID is missing on most newer Macs. See my comment on one of answers above.Spiritualty
O
2

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

Optimum answered 14/11, 2018 at 19:17 Comment(0)
K
2

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 $(ls $C /tmp/ssh-*/agent.* 2>/dev/null | sort -u) ; do
  if SSH_AUTH_SOCK=$s ssh-add -l >/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.

Kirkcudbright answered 30/4, 2022 at 16:51 Comment(0)
S
1
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.

Squeal answered 16/12, 2020 at 7:20 Comment(2)
@Wang-Zhang Nice ssh-agent wrapper. I like it :+1:Eellike
pidof is not a builtin command on macSpiritualty
R
1

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)

Ringleader answered 25/8, 2022 at 13:36 Comment(1)
this is the only solution that worked for me (on Mac M1)Spiritualty
S
1

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.

Spiritualty answered 12/2 at 16:7 Comment(0)
P
0

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[@]}"

Pettaway answered 4/11, 2020 at 0:46 Comment(0)
E
0

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.
Eellike answered 26/7, 2021 at 13:12 Comment(2)
This is nice and neat trick that solves the problem to pair agent PID to existing socket! Best answer here.Intercommunicate
SSH_AGENT_PID is not set or missing on newer Macs. see my comment above.Spiritualty
J
0

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
Jolty answered 7/5, 2023 at 13:7 Comment(0)
A
-1

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
Antonetteantoni answered 11/11, 2016 at 14:1 Comment(2)
Firstly, $varname references a variable, it can't be set that way. Secondly, why would you want to do that if eval ssh-agent already sets $SSH_AGENT_PID?Sparks
Sorry, did not know that it sets that variable. And yes, $ should not have been there. Thanks.Antonetteantoni

© 2022 - 2024 — McMap. All rights reserved.