101

I have a sample sh script in my Linux environment, which basically runs 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 

The 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?

10
  • 4
    Try $SSH_AGENT_PID. Commented Nov 11, 2016 at 13:50
  • 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_AGENT Commented Nov 11, 2016 at 13:57
  • 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.) Commented Nov 11, 2016 at 14:28
  • 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. Commented Nov 11, 2016 at 14:29
  • 1
    I cannot do that since login to the machine multiple times a day - and not just me, but other people as well. Commented Nov 11, 2016 at 14:29

15 Answers 15

81

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

Sign up to request clarification or add additional context in comments.

3 Comments

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 idbrii
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_PID
@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.
72

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

8 Comments

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).
"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 stackoverflow.com/a/9375940/79125
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.
@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).
@Cody this starts new agent with each ssh connection (ssh, exit, ssh again and you have two agents)
|
13

Use:

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

It is a single-line command. Run for the first time, it will start ssh-agent. Run for the second time, it will not start the ssh-agent. It is simple and elegant, mate!

3 Comments

Not bullet proof but quite elegant solution :)
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"
Nice, and works for me.
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).

4 Comments

Why shouldn't I just add kill -9 $SSH_AGENT_PID at the end of my script, as @alok said in his comment on the question?
If the script itself gets killed (with an interruptable signal) while running, that command won't be run. With the trap it will.
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.
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!
8

Using $SSH_AGENT_PID can only test the ssh-agent, but it misses identities when they are 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 safe to check it with ssh-add -l with an expect script, like the 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 used to run in 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 in the above script with do while.

Comments

3

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.

Comments

2

The accepted answer did not work for me under Ubuntu 14.04 (Trusty Tahr).

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 

1 Comment

SSH_AGENT_PID is missing on most newer Macs. See my comment on one of answers above.
2

Here is my current ssh-agent.sh checker/launcher script that works for me on Linux and OS X.

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

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 the PID directory 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 directory 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 public 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 file file using:

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

I hope this helps others get up and going quickly.

I created a public gist if you want to hack/improve this with me: ssh-agent checker/launcher

Comments

2

Here is a 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 a very simple command to kill all processes of ssh-agent (or any program):

kill $(pidof ssh-agent) 

1 Comment

this is the only solution that worked for me (on Mac M1)
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.

3 Comments

@Wang-Zhang Nice ssh-agent wrapper. I like it :+1:
pidof is not a builtin command on mac
An explanation would be in order. E.g., why is the second line "#!/usr/bin/env bash"?
1

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. 

2 Comments

This is nice and neat trick that solves the problem to pair agent PID to existing socket! Best answer here.
SSH_AGENT_PID is not set or missing on newer Macs. see my comment above.
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 without any local changes) I saw an error message:

git push 

Output:

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

It 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 fact is 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 SettingsSSH 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 

Output:

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 the Git configuration does not chain. This post (along with its answer) might be helpful.

Comments

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

Comments

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 

Comments

-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 

2 Comments

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?
Sorry, did not know that it sets that variable. And yes, $ should not have been there. Thanks.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.