How can I run ssh-add automatically, without a password prompt?

  • I want to communicate between several computers on my network (static Ethernet), through SSH. In order to do that I need to run ssh-add every time I log in on a specific machine, how can I do it so that it's set up once and it doesn't ask me for the passphrase every time I log in or reboot my machine?

    I know that there is a way that you should add some lines to the bash_profile file, but I still need to type the password every time I reboot/log in to a specific machine.

    if [ -z "$SSH_AUTH_SOCK" ] ; then
        eval `ssh-agent -s`
        ssh-add
    fi
    

    https://stackoverflow.com/a/52671286/217408 solved it for me to pass the passphrase from bitwarden

  • This is a typical example of a trade-off between security and convenience. Luckily there are a number of options. The most appropriate solution depends on the usage scenario and desired level of security.

    ssh-key with passphrase, no ssh-agent

    Now the passphrase has to be entered every time the key is used for authentication. While this is the best option from a security standpoint, it offers the worst usability. This may also lead to a weak passphrase being chosen in-order-to lessen the burden of entering it repeatedly.

    ssh-key with passphrase, with ssh-agent

    Adding the following to ~/.bash_profile will automatically start ssh-agent and load the ssh-key(s) on login:

    if [ -z "$SSH_AUTH_SOCK" ] ; then
      eval `ssh-agent -s`
      ssh-add
    fi
    

    Now the passphrase must be entered upon every login. While slightly better from a usability perspective, this has the drawback that ssh-agent prompts for the passphrase regardless of if the key is to be used or not during the login session. Each new login also spawns a distinct ssh-agent instance which remains running with the added keys in memory even after logout, unless explicitly killed.

    To kill ssh_agent on logout, add the following to ~/.bash_logout

    if [ -n "$SSH_AUTH_SOCK" ] ; then
      eval `/usr/bin/ssh-agent -k`
    fi
    

    or the following to ~/.bash_profile

    trap 'test -n "$SSH_AUTH_SOCK" && eval `/usr/bin/ssh-agent -k`' 0
    

    Creating multiple ssh-agent instances can be avoided by creating a persistent communication socket to the agent at a fixed location in the file system, such as in Collin Anderson's answer. This is an improvement over spawning multiple agents instances, however, unless explicitly killed the decrypted key still remains in memory after logout.

    On desktops, ssh-agents included with the desktop environment, such as the Gnome Keyring SSH Agent, can be a better approach as they typically can be made to prompt for the passphrase the first time the ssh-key is used during a login session and store the decrypted private key in memory until the end of the session.

    ssh-key with passphrase, with ssh-ident

    ssh-ident is a utility that can manage ssh-agent on your behalf and load identities as necessary. It adds keys only once as they are needed, regardless of how many terminals, ssh or login sessions that require access to an ssh-agent. It can also add and use a different agent and different set of keys depending on the host being connected to, or the directory ssh is invoked from. This allows for isolating keys when using agent forwarding with different hosts. It also allows to use multiple accounts on sites like GitHub.

    To enable ssh-ident, install it and add the following alias to your ~/bash_profile:

    alias ssh='/path/to/ssh-ident'
    

    ssh-key with passphrase, with keychain

    keychain is a small utility which manages ssh-agent on your behalf and allows the ssh-agent to remain running when the login session ends. On subsequent logins, keychain will connect to the existing ssh-agent instance. In practice, this means that the passphrase must be be entered only during the first login after a reboot. On subsequent logins, the unencrypted key from the existing ssh-agent instance is used. This can also be useful for allowing passwordless RSA/DSA authentication in cron jobs without passwordless ssh-keys.

    To enable keychain, install it and add something like the following to ~/.bash_profile:

    eval `keychain --agents ssh --eval id_rsa`
    

    From a security point of view, ssh-ident and keychain are worse than ssh-agent instances limited to the lifetime of a particular session, but they offer a high level of convenience. To improve the security of keychain, some people add the --clear option to their ~/.bash_profile keychain invocation. By doing this passphrases must be re-entered on login as above, but cron jobs will still have access to the unencrypted keys after the user logs out. The keychain wiki page has more information and examples.

    ssh-key without passphrase

    From a security standpoint, this is the worst option since the private key is entirely unprotected in case it is exposed. This is, however, the only way to make sure that the passphrase need not be re-entered after a reboot.

    ssh-key with passphrase, with ssh-agent, passing passphrase to ssh-add from script

    While it might seem like a straightforward idea to pass the passphrase to ssh-add from a script, e.g. echo "passphrase\n" | ssh-add, this is not as straighforward as it seems as ssh-add does not read the passphrase from stdin, but opens /dev/tty directly for reading.

    This can be worked around with expect, a tool for automating interactive applications. Below is an example of a script which adds a ssh-key using a passphrase stored in the script:

    #!/usr/bin/expect -f
    spawn ssh-add /home/user/.ssh/id_rsa
    expect "Enter passphrase for /home/user/.ssh/id_rsa:"
    send "passphrase\n";
    expect "Identity added: /home/user/.ssh/id_rsa (/home/user/.ssh/id_rsa)"
    interact
    

    Note that as the passphrase is stored in plaintext in the script, from a security perspective, this is hardly better than having a passwordless ssh-key. If this approach is to be used, it is important to make sure that the expect script containing the passphrase has proper permissions set to it, making it readable, writable and runnable only by the key owner.

    Okay, but when I put your code to ~/.bash_profile I have to type in password every time I login, I don't want that either. I am not concerned about security at all. echo "pass\n" | ssh-add doesn't work

    @user1607072 Yeah, that is how the `ssh-agent` snippet in `~/.bash_profile` behaves as explained in the answer. You might want to look at the `keychain` utility. With `keychain` you need to enter the password on first login after reboot, but on subsequent logins `keychain` will connect to an existing `ssh-agent` instance with the decrypted key in memory. Apart from that there's the option of generating a ssh-key without a passphrase, but this is of course not recommended.

    So there is no other way to do it than to leave my passphrase empty? Hmm that is sad. Anyway thanks a lot for your answer, it's the most comprehensive answer I've ever received!

    @user1607072 While I would strongly suggest one of the more secure approaches, there is a way to pass the passphrase to `ssh-add` from a script. The reason `echo "pass\n" | ssh-add` does not work is that `ssh-add` does not read the password from `stdin`, but opens `/dev/tty` directly for reading. Updated the answer to include a workaround for this, using an utility called `expect`.

    Thanks I will try that. I know that it's not secure, but I don't see any other way. I need to communicate with my computers on static network through ssh and I don't want to type in password every time I reboot my network.

    @user1607072 It might be a bit overkill for your use case, but Kerberos) in combination with ssh GSSAPI support can also be used for passwordless ssh logins. The corresponding authentication method in ssh is called `gssapi-with-mic`. This is usually used in larger networks, but of course if you have interest in this it might be worth looking into.

    I am running zsh with Prezto and would like to start ssh-agent every time I open terminal. I do have a ssh password. Is it possible?

    @onegrz Typically you'd want only one instance of ssh-agent running. The `$SSH_AUTH_SOCK` environment variable will point each of your shells to that agent instance. As to how to configure it via Prezto, that sounds like a new question. The snippet in the original Q does apply to `zsh` as well, although you'd want to put it in either `.profile` or a `zsh` specific configuration file.

    Sorry, a bit late, what about those GUI tools like `Password and Keys` in Ubuntu or OS X's `Keychain Access`, only the very first time I login into a SSH server they ask me the passphrase and to check something like "*Automatically unlock this key on subsequent logins*". I check it, login into the server, then I reboot, login with my account's password (no passphrase is asked) and this time when I open the SSH connection no passphrase is needed. How do these tools manage this? Does the passphrase become bounded with my account's password so that when I login the key is automatically decrypted?

    is there a way to run "expect" somehow from command line without leaving a file around with the passphrase?

    @tofutim I'd take the password as an argument to the expect script, or possibly pass in as an environment variable. @Thomas Nyman: Excellent `expect` snippet there. I would however add `expect "Identity added: /home/user/.ssh/id_rsa (/home/user/.ssh/id_rsa)"` before the interact, otherwise it would fail in some circumstances.

    keychain requires additional parameters http://www.funtoo.org/Keychain

    @DmitriyDokshin Can you be more specific? `eval \`keychain --eval id_rsa\`` is a valid usage example also listed on the wiki page you referred to, assuming your private key is in `~/.ssh/id_rsa`.

    @ThomasNyman I'm using `eval 'keychain --eval --agents ssh --inherit any ~/.ssh/my_rsa'`

    I'm curious about making use of systemd to accomplish this and how would that fit into your response? Also, his is perhaps the most complete answer I've come across! Thank you for spending the time writing it.

    @ErickBrown: Already answered here. The SSH Agent unit should be stopped on logout if you have user lingering disabled in the systemd login manager. If user lingering is enabled, the systemd user instance and the SSH Agent unit are kept running even after the last login session is closed.

    @cmc I reject your edit, but find it meaningfull, you should add it as an answer of it's own.

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

    very neat, this way you only have one ssh-agent running (: Multiple agents as in @thomasNyman's second solution seems a security risk to me...

    After researching in various sites and reading various solutions, this one here seems to be the clearest, straight to the point. Very nice. +1

    better to do this: `alias ssh=ssh-check-agent", and have the check-agent version do the above. that way: a) you only get one agent and b) you only get the agent if you need it

    What is the reason you aren't starting the ssh-agent in the background with the `-s` param? Just curious, I'm still a newbie with Linux (and in my case Debian).

    I think -s is the default, so we're already doing that.

    `ssh-add -l` returns an exit code of 0 when the agent has identities and 1 when it does not so you can cut grep out of the last command and use `ssh-add -l > '/dev/null' || ssh-add`

    This is a good answer, however users would be wise to use the `-t` switch of `ssh-agent`. There will be a lifetime on all the keys you loaded into the agent.

    Note that `ssh-add` without arguments adds `~/.ssh/id_rsa`. You might want to pass `ssh-add` arguments if your private keys are in another file.

  • Not closely related to the OP's question, but it might be useful to others: since 7.2.0 ssh(1) has an option that allows adding a key to ssh-agent upon first authentication; the option is AddKeysToAgent and can be set to yes, no, ask, or confirm, systemwide or on your personal .ssh/config file.

    Reference: https://www.openssh.com/txt/release-7.2

    Applicable to those who are new to the `.ssh/config` file: this applies to `ssh` and anything that uses `ssh` behind it, for example `scp`, and can be done on a per-host basis.

    It still asks me for password each time I login and try to git pull for example.

    @trainosis The issue is that you probably do not have an ssh-agent instance running to hold the decrypted key(s) in memory for future use. You should only need to enter the password for a given key once per login session when using ssh-agent.

  • ssh-agent caches various unlocked ssh-keys, so you can have ssh-keys protected by passwords, but without having to type them every single time.

    In order to cache unlocked keys, it obviously needs to unlock those keys. For unlocking keys that are locked with a passphrase, it obviously needs to know these passphrases.

    Any method that does not require authorization from a human being (e.g. "typing in a password") will not only make your system insecure; it will also render the entire purpose of the ssh-agent meaningless.

    Having said all this, you can simply use ssh-keys that are not password protected (hit Enter when asked for a password during key-generation). Since there isn't any password, ssh-agent doesn't need to ask you for one in order to (not) cache it.

    I agree, as long as your keys are properly user-only-permissioned, there is little advantage to ssh-agent over permissionless keys. i like to ssh into a login server, and then, that server has a bunch of permssionless keys, each of which can only be used to unlock one other server. the login server does nothing else, so it's much harder to hack/spoof, etc... the other servers have no password access, are key-only.

  • Here is a workaround to automate your SSH passphrase.

    1. Create a one-liner script which prints your passphrase to standard output, e.g.:

       echo 'echo MY_SSH_PASSWORD' > ~/.print_ssh_password && chmod 700 ~/.print_ssh_password
      

      Important: Ensure you copy the leading space to prevent storing your password to your history.

    And use one of the below methods.

    • using a standard input approach:

      cat ~/.ssh/id_rsa | SSH_ASKPASS=~/.print_ssh_password ssh-add -
      
    • or named pipe approach:

      1. Create a named pipe (you could also try a process substitution):

        mkfifo --mode 0600 ~/.ssh_fifo
        
      2. Run ssh-add by specifying the program used for the authentication:

        cat ~/.ssh/id_rsa >~/.ssh_fifo | SSH_ASKPASS=~/.print_ssh_password ssh-add ~/.ssh_fifo
        

      See: man ssh-add to read more about SSH_ASKPASS.

    The `echo my_passphrase` is a big security hole. First after you have typed it, the password is in clear text in the history file of what ever shell you use. And second command line arguments are world readable on Unix (`ps -ef`). **Never put passwords in command line arguments!**

    @ceving Adding extra leading space solves the problem with the history file. Added extra info.

    @kenorb: That doesn't solve the bigger problem of the password visible in `ps` output. The history file is typically only readable by the owning user anyway, but the command lines are readable by all users on a system.

  • I won't recommend you ssh-add (which need to open a ssh-agent) at login. This is because you can't control when the ssh-agent section ends, and can create security risk when you need not use the keyfiles at one login section.

    Rather, I recommend to write a script which opens a ssh-agent's section sub-shell, with all keyfiles auto added, and be called when needed to use ssh. If you could adopt so, read on.

    You would have two choices:

    1. Remove all passphrases for your keys, which have weak security if your key files are stolen. (thus not recommended)

    2. Use the same passphrase for your keys. Then when you ssh-add keyfile1 keyfile2 ..., you will only need to type the passphrase once, per section.

    In both cases, you could write such script file "ssh_keys_section.sh" as below:

    #!/bin/bash
    # This script run a ssh-agent on a sub-shell and automatically ssh-add all keyfiles at once.
    # This agent ends when you type `exit` to close the sub-shell.
    exec ssh-agent bash -c "ssh-add /path/to/keyfile1 /path/to/keyfile2 ...; exec bash"
    

    Remarks:

    • Command to change or delete passphrase: ssh-keygen -p -f keyfile
    • Within the sub-shell, you might even fork more terminals which share the same unlocked keys, by using maybe a command like /path/to/yourterminal & (depends on OS)

    E.g. On Windows within Cygwin, `/path/to/yourterminal &` ==> `mintty &`

    Remark: after use, close the session with `ctrl-d` or `exit`, just like you have invoked a nested `bash` shell and need to close it.

  • I used to use the script mentioned by steampowered, I've made the below one now, because it doesn't leave files lying around.

    Working on zsh shell only.

    #!/bin/sh
    
    AGENT_BIN=`which ssh-agent`
    AGENT_ADD_BIN=`which ssh-add`
    AGENT_PID=`ps -fe | grep ${AGENT_BIN} | awk -vuser=$USER -vcmd="$AGENT_BIN" '$1==user && $8==cmd{print $2;exit;}'`
    if [ -z "$AGENT_BIN" ]; then
        echo "no ssh agent found!";
        return
    fi
    if [ "" -eq "$AGENT_PID" ]; then
        if read -sq "YN?Do you want to unlock your ssh keys?"; then
            echo ""
            output=`$AGENT_BIN | sed 's/echo/#echo/g'`
            eval $output
            $AGENT_ADD_BIN
        fi
    else
        for f in "/proc/"*
        do
            cmdline=`cat "$f/cmdline"`
            if [ "${AGENT_BIN}" -ef "${cmdline}" ]; then
                export SSH_AUTH_SOCK=`cat $f/net/unix | grep --binary-file=text -oP '((/[^/]*?)+/ssh-[^/]+/agent\.\d+$)'`
                export SSH_AGENT_PID=${f##*/}
                break;
            fi
        done
    fi
    
  • if [ ! -S ${HOME}/.ssh/ssh_auth_sock ]; then
      eval $(ssh-agent)
      ln -sf "${SSH_AUTH_SOCK}" ${HOME}/.ssh/ssh_auth_sock
    fi
    export SSH_AUTH_SOCK=${HOME}/.ssh/ssh_auth_sock
    
    ssh_keys=$(find -E ~/.ssh -type f -regex '.*(rsa$|pem)')
    ssh_agent_keys=$(ssh-add -l | awk '{key=NF-1; print $key}')
    
    for k in "${ssh_keys}"; do
        for l in "${ssh_agent_keys}"; do
            if [[ ! "${k}" = "${l}" ]]; then
                ssh-add "${k}" > /dev/null 2>&1
            fi
        done
    done
    
  • SSH_ENV="$HOME/.ssh/environment"
    
    function start_agent {
         echo "Initialising new SSH agent..."
         /usr/bin/ssh-agent | sed 's/^echo/#echo/' > "${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
    

    Giving credit here: https://www.cygwin.com/ml/cygwin/2001-06/msg00537.html

    This solution is also endorsed here: http://mah.everybody.org/docs/ssh

  • Single sign on solution for SSH could lead me to pam_ssh.

    According to this article, the concept is:

    If you work with multiple *nix-based machines via ssh, you are probably tired of constantly having to enter your password every time you want to access another box. There is a secure way to allow you to access every machine, that you have ssh access to, without having to enter another password (other than the one you signed on with originally.)


    This is actually quite simple to do, you basically just create a public/private key pair to authenticate yourself to your other machines, then have PAM spawn an agent to load your keys after you logon, providing a single signon solution to accessing all your remote machines. This guide will walk you through setting this up.

    I have not verified this would actually work.

License under CC-BY-SA with attribution


Content dated before 6/26/2020 9:53 AM