[git] Start ssh-agent on login

I have a site as a remote Git repo pulling from Bitbucket.com using an SSH alias. I can manually start the ssh-agent on my server but I have to do this every time I login via SSH.

I manually start the ssh-agent:

eval ssh-agent $SHELL

Then I add the agent:

ssh-add ~/.ssh/bitbucket_id

Then it shows up when I do:

ssh-add -l

And I'm good to go. Is there any way to automate this process so I don't have to do it every time I login? The server is running RedHat 6.2 (Santiago).

This question is related to git ssh bitbucket redhat ssh-agent

The answer is


So I used to use the approaches described above, but I kind of prefer the agent to die when my last bash session ends. This is a bit longer than the other solutions, but its my preferred approach. The basic idea is that the first bash session starts the ssh-agent. Then each additional bash session checks for the config file (~/.ssh/.agent_env). If that is there and there is a session running then source the environment and create a hardlink to the socket file in /tmp (needs to be on the same filesystem as the original socket file). As bash sessions shut down each deletes its own hardlink. The last session to close will find that the hardlinks have 2 links (the hardlink and the original), removal of the processes own socket and killing of the process will result in 0, leaving a clean environment after the last bash session closes.

# Start ssh-agent to keep you logged in with keys, use `ssh-add` to log in
agent=`pgrep ssh-agent -u $USER` # get only your agents           
if [[ "$agent" == "" || ! -e ~/.ssh/.agent_env ]]; then
    # if no agents or environment file is missing create a new one
    # remove old agents / environment variable files
    kill $agent running
    rm ~/.ssh/.agent_env 

    # restart
    eval `ssh-agent` 
    echo 'export SSH_AUTH_SOCK'=$SSH_AUTH_SOCK >> ~/.ssh/.agent_env             
    echo 'export SSH_AGENT_PID'=$SSH_AGENT_PID >> ~/.ssh/.agent_env             
fi

# create our own hardlink to the socket (with random name)           
source ~/.ssh/.agent_env                                                    
MYSOCK=/tmp/ssh_agent.${RANDOM}.sock                                        
ln -T $SSH_AUTH_SOCK $MYSOCK                                                
export SSH_AUTH_SOCK=$MYSOCK                                                

end_agent()                                                                     
{
    # if we are the last holder of a hardlink, then kill the agent
    nhard=`ls -l $SSH_AUTH_SOCK | awk '{print $2}'`                             
    if [[ "$nhard" -eq 2 ]]; then                                               
        rm ~/.ssh/.agent_env                                                    
        ssh-agent -k                                                            
    fi                                                                          
    rm $SSH_AUTH_SOCK                                                           
}                                                                               
trap end_agent EXIT                                                             
set +x              

I like your answers a lot. It made working from cygwin / linux hosts a lot easier. I combined start and end functions to make it secure.

SSH_ENV="$HOME/.ssh/.agent_env"

function start_agent {
    echo "Initialising new SSH agent..."

    eval `/usr/bin/ssh-agent`
    echo 'export SSH_AUTH_SOCK'=$SSH_AUTH_SOCK >> ${SSH_ENV}
    echo 'export SSH_AGENT_PID'=$SSH_AGENT_PID >> ${SSH_ENV}

    echo succeeded
    chmod 600 "${SSH_ENV}"
    . "${SSH_ENV}" > /dev/null
    /usr/bin/ssh-add;
}

# Source SSH settings, if applicable
if [ -f "${SSH_ENV}" ]; then
    . "${SSH_ENV}" > /dev/null
    #ps ${SSH_AGENT_PID} doesn't work under cywgin
    ps -ef | grep ${SSH_AGENT_PID} | grep ssh-agent$ > /dev/null || {
        start_agent;
    }
else
    start_agent;
fi

# create our own hardlink to the socket (with random name)
MYSOCK=/tmp/ssh_agent.${RANDOM}.sock
ln -T $SSH_AUTH_SOCK $MYSOCK
export SSH_AUTH_SOCK=$MYSOCK

end_agent()
{
    # if we are the last holder of a hardlink, then kill the agent
    nhard=`ls -l $SSH_AUTH_SOCK | awk '{print $2}'`
    if [[ "$nhard" -eq 2 ]]; then
        rm ${SSH_ENV}
        /usr/bin/ssh-agent -k
    fi
    rm $SSH_AUTH_SOCK
}
trap end_agent EXIT
set +x

I solved it by adding this to the /etc/profile - system wide (or to user local .profile, or _.bash_profile_):

# SSH-AGENT 
#!/usr/bin/env bash
SERVICE='ssh-agent'
WHOAMI=`who am i |awk '{print $1}'`

if pgrep -u $WHOAMI $SERVICE >/dev/null
then
    echo $SERVICE running.
else
    echo $SERVICE not running.
    echo starting
    ssh-agent > ~/.ssh/agent_env
fi
. ~/.ssh/agent_env

This starts a new ssh-agent if not running for the current user, or re-sets the ssh-agent env parameter if running.


Tried couple solutions from many sources but all seemed like too much trouble. Finally I found the easiest one :)

If you're not yet familiar with zsh and oh-my-zsh then install it. You will love it :)

Then edit .zshrc

vim ~/.zshrc

find plugins section and update it to use ssh-agent like so:

plugins=(ssh-agent git)

And that's all! You'll have ssh-agent up and running every time you start your shell


Just to add yet another solution :P, I went with a combination of @spheenik and @collin-anderson 's solutions.

 # Ensure that we have an ssh config with AddKeysToAgent set to true
 if [ ! -f ~/.ssh/config ] || ! cat ~/.ssh/config | grep AddKeysToAgent | grep yes > /dev/null; then
     echo "AddKeysToAgent  yes" >> ~/.ssh/config
 fi
 # Ensure a ssh-agent is running so you only have to enter keys once
 if [ ! -S ~/.ssh/ssh_auth_sock ]; then
   eval `ssh-agent`
   ln -sf "$SSH_AUTH_SOCK" ~/.ssh/ssh_auth_sock
 fi
 export SSH_AUTH_SOCK=~/.ssh/ssh_auth_sock

Could be a little more elegant but its simple and readable. This solution:

  • ensures AddKeysToAgent yes is in your ssh config so keys will be automatically added upon use
  • doesn't prompt you to enter any passphrases at login (again, one-time passphrase entering occurs on first use)
  • silently starts an ssh-agent if it has not already started one

Comments welcome :)


I use the ssh-ident tool for this.

From its man-page:

ssh-ident - Start and use ssh-agent and load identities as necessary.


The accepted solution have following drawbacks:

  • it is complicated to maintain;
  • it evaluates storage file which may lead to errors or security breach;
  • it starts agent but doesn't stop it which is close equivalent to leaving the key in ignition.

If your keys do not require to type password, I suggest following solution. Add the following to your .bash_profile very end (edit key list to your needs):

exec ssh-agent $BASH -s 10<&0 << EOF
    ssh-add ~/.ssh/your_key1.rsa \
            ~/.ssh/your_key2.rsa &> /dev/null
    exec $BASH <&10-
EOF

It have following advantages:

  • much simpler solution;
  • agent session ends when bash session ends.

It have possible disadvantages:

  • interactive ssh-add command will influence only one session, which is in fact an issue only in very untypical circumstances;
  • unusable if typing password is required;
  • started shell becomes non-login (which doesn't influence anything AFAIK).

Note that several ssh-agent processes is not a disadvantage, because they don't take more memory or CPU time.


Add this to your ~/.bashrc, then logout and back in to take effect.

if [ ! -S ~/.ssh/ssh_auth_sock ]; then
  eval `ssh-agent`
  ln -sf "$SSH_AUTH_SOCK" ~/.ssh/ssh_auth_sock
fi
export SSH_AUTH_SOCK=~/.ssh/ssh_auth_sock
ssh-add -l > /dev/null || ssh-add

This should only prompt for a password the first time you login after each reboot. It will keep reusing the same ssh-agent as long as it stays running.


On Arch Linux, the following works really great (should work on all systemd-based distros):

Create a systemd user service, by putting the following to ~/.config/systemd/user/ssh-agent.service:

[Unit]
Description=SSH key agent

[Service]
Type=simple
Environment=SSH_AUTH_SOCK=%t/ssh-agent.socket
ExecStart=/usr/bin/ssh-agent -D -a $SSH_AUTH_SOCK

[Install]
WantedBy=default.target

Setup shell to have an environment variable for the socket (.bash_profile, .zshrc, ...):

export SSH_AUTH_SOCK="$XDG_RUNTIME_DIR/ssh-agent.socket"

Enable the service, so it'll be started automatically on login, and start it:

systemctl --user enable ssh-agent
systemctl --user start ssh-agent

Add the following configuration setting to your local ssh config file ~/.ssh/config (this works since SSH 7.2):

AddKeysToAgent  yes

This will instruct the ssh client to always add the key to a running agent, so there's no need to ssh-add it beforehand.


Old question, but I did come across a similar situation. Don't think the above answer fully achieves what is needed. The missing piece is keychain; install it if it isn't already.

sudo apt-get install keychain

Then add the following line to your ~/.bashrc

eval $(keychain --eval id_rsa)

This will start the ssh-agent if it isn't running, connect to it if it is, load the ssh-agent environment variables into your shell, and load your ssh key.

Change id_rsa to whichever private key in ~/.ssh you want to load.

Some useful options for keychain:

  • -q Quiet mode
  • --noask Don't ask for the password upon start, but on demand when ssh key is actually used.

Reference

https://unix.stackexchange.com/questions/90853/how-can-i-run-ssh-add-automatically-without-password-prompt


Users of the fish shell can use this script to do the same thing.

# content has to be in .config/fish/config.fish
# if it does not exist, create the file
setenv SSH_ENV $HOME/.ssh/environment

function start_agent                                                                                                                                                                    
    echo "Initializing new SSH agent ..."
    ssh-agent -c | sed 's/^echo/#echo/' > $SSH_ENV
    echo "succeeded"
    chmod 600 $SSH_ENV 
    . $SSH_ENV > /dev/null
    ssh-add
end

function test_identities                                                                                                                                                                
    ssh-add -l | grep "The agent has no identities" > /dev/null
    if [ $status -eq 0 ]
        ssh-add
        if [ $status -eq 2 ]
            start_agent
        end
    end
end

if [ -n "$SSH_AGENT_PID" ] 
    ps -ef | grep $SSH_AGENT_PID | grep ssh-agent > /dev/null
    if [ $status -eq 0 ]
        test_identities
    end  
else
    if [ -f $SSH_ENV ]
        . $SSH_ENV > /dev/null
    end  
    ps -ef | grep $SSH_AGENT_PID | grep -v grep | grep ssh-agent > /dev/null
    if [ $status -eq 0 ]
        test_identities
    else 
        start_agent
    end  
end

Examples related to git

Does the target directory for a git clone have to match the repo name? Git fatal: protocol 'https' is not supported Git is not working after macOS Update (xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools) git clone: Authentication failed for <URL> destination path already exists and is not an empty directory SSL_connect: SSL_ERROR_SYSCALL in connection to github.com:443 GitLab remote: HTTP Basic: Access denied and fatal Authentication How can I switch to another branch in git? VS 2017 Git Local Commit DB.lock error on every commit How to remove an unpushed outgoing commit in Visual Studio?

Examples related to ssh

Starting ssh-agent on Windows 10 fails: "unable to start ssh-agent service, error :1058" How to solve "sign_and_send_pubkey: signing failed: agent refused operation"? key_load_public: invalid format ssh connection refused on Raspberry Pi Getting permission denied (public key) on gitlab Verify host key with pysftp Can't connect to Postgresql on port 5432 Checkout Jenkins Pipeline Git SCM with credentials? How to open remote files in sublime text 3 how to setup ssh keys for jenkins to publish via ssh

Examples related to bitbucket

How to markdown nested list items in Bitbucket? Your configuration specifies to merge with the <branch name> from the remote, but no such ref was fetched.? Bitbucket git credentials if signed up with Google What I can do to resolve "1 commit behind master"? Bitbucket fails to authenticate on git pull Change remote repository credentials (authentication) on Intellij IDEA 14 git: updates were rejected because the remote contains work that you do not have locally How do I push a local repo to Bitbucket using SourceTree without creating a repo on bitbucket first? Clone private git repo with dockerfile How to move git repository with all branches from bitbucket to github?

Examples related to redhat

How to fix: fatal error: openssl/opensslv.h: No such file or directory in RedHat 7 Start ssh-agent on login How to permanently export a variable in Linux? How to install maven on redhat linux How to find whether MySQL is installed in Red Hat? How to confirm RedHat Enterprise Linux version? Determining the path that a yum package installed to MySQL config file location - redhat linux server Specify the from user when sending email using the mail command

Examples related to ssh-agent

Start ssh-agent on login Could not open a connection to your authentication agent How to permanently add a private key with ssh-add on Ubuntu?