Using ssh agent

by Steven J. Owens (unless otherwise attributed)

Generally speaking, you can use ssh to securely login to a remote host by just entering your password when prompted. However, there are times when it's useful or a good idea to set up passwordless access, by generating a set of public and private keys and putting the key files in the right places on both your client machine and the remote host.

I wrote this tutorial because I had a project that required programs to run on thirty or so different linux boxes. Part of how the project ran required programs to be compiled, copied over, started, shut down, results data copied back around, and so forth.

Therefore, I needed scripts to control all of this; to start all of the programs at once, to stop all of the programs at once, and to gather the results all back to a single directory on a single machine for processing. And for each of these steps, I needed to type in the password to ssh into the account on the machine in question. That got old fast.

Here's an outline of the process:

  • generate ssh public key
  • copy ssh public key to remote server, place in ~/.ssh/authorized_keys
  • make sure ~/.ssh/authorized keys is chmod g-w
  • run ssh-agent with ssh-add and an argument to start a bash shell and do everything from there.
  • make sure your ssh commands are not using the -1 argument unless you are using rsa1/dsa1

    At the bottom, I'll have two more sections:

  • Somewhat useful URLs
  • How This Is Supposed To Work

    My Project

    The easiest way to set things up was to have the list of host names in a build file, which constructs a start and stop script for each hostname, with appropriate settings for that machine.

    A central and each contain a list of ssh commands. Each command in the list uses ssh to login to some machine and run the individual scripts for that machine (e.g.,, etc.).

    The central script is pretty straightforward:

    ssh /home/username/myproject/ &
    ssh /home/username/myproject/ &
    ssh /home/username/myproject/ &

    This variation on the ssh command tells ssh (in the first line) to log into the machine and run the command /home/username/myproject/

    The & at the end runs that entire line (the whole command, including ssh) in the background, so it doesn't have to wait for to complete and exit before it invokes

    The script is similar.

    However, with this approach ssh still prompts me for the password - for each of the 30 ssh commands! This is a pain, so I set up ssh key-based, passwordless logins.

    In Detail

    There will be two accounts in this example:

  • the local account, frodo@shire
  • the remote account frodo@mordor

    We are setting up ssh so you can log into frodo@shire normally, with a password, and then do "ssh frodo@mordor" and immediately log into frodo@mordor without any password. (Or so you can do "ssh frodo@mordor" without typing a password.)

    Note that we're setting up so frodo@shire (local) can get into frodo@mordor (remote), but that doesn't mean frodo@mordor can get into frodo@shire. To make it work both ways, just repeat the process with the local and remote accounts swapped.

    This tutorial will also cover how to set up ssh-agent with linux:

    Your private ssh key has to be stored securely, otherwise anybody could just get into your box and then use your private key to get into the rest of your machines. To secure your private key, your private key itself is stored in an old-fashioned password-encrypted file -- actually it's a pass phrase, it can be and is recommended to be much longer than a password. When you run the command, you're prompted for the passphrase to decrypt your key file.

    Since typing the passphrase for your private key file every time you use ssh to login to another box is just as much hassle as typing your remote account password, ssh supports using a tool called ssh-agent. Once you give ssh-agent the passphrase for your key file the first time, when you run ssh again later, ssh will note the ssh-agent environment variable and instead of prompting you for the keystore passphrase, ssh will ask ssh-agent for the already-decrypted private key. This is fairly secure.

    Generate your ssh public key on the local acccount.

    You don't need to do this step if you have already generated a public key. If you want to generate your public key again, that's okay as long as you haven't used the existing public key to encrypt anything or sign anything or set up any other remote access. If so, you'll need to either use your existing public key, or figure out how to juggle public keys, which is beyond the scope of this document.

    First, log into frodo@shire:


    Next we run ssh-keygen with:

    a) the "-t typename" argument specifying type "dsa",

    b) when prompted, pressing enter to select the default file to save the key in, and

    c) for the passphrase, entering a phrase or short sentence that you will be able to remember.

    Here's an entire example, which I'll then dissect further below:

    frodo@shire:~$ ssh-keygen -t dsa
    Generating public/private dsa key pair.
    Enter file in which to save the key (/home/frodo/.ssh/id_dsa):<enter>
    Created directory '/home/frodo/.ssh'.
    Enter passphrase (empty for no passphrase): examplepassphrase<enter>
    Enter same passphrase again: examplepassphrase<enter>
    Your identification has been saved in /home/frodo/.ssh/id_dsa.
    Your public key has been saved in /home/frodo/.ssh/
    The key fingerprint is:
    4f:58:b3:a5:76:16:41:11:d1:c9:96:6e:54:26:c3:22 user@shire

    Let's start with the first line:

    frodo@shire:~$ ssh-keygen -t dsa

    See "man ssh-keygen" for more info, but basically your types can be "rsa1", "rsa" or "dsa". rsa1 is rsa for first-generation ssh. Second-generation ssh is generally recommended. Second-generation "dsa" seems to be what most people recommend.

    Generating public/private dsa key pair.
    Enter file in which to save the key (/home/frodo/.ssh/id_dsa):<enter>
    Created directory '/home/frodo/.ssh'.

    You do not need to create a ~/.ssh directory. If you don't have one, ssh-keygen will create one.

    You do not need to do anything about your existing .ssh directory... unless you need to keep the existing ssh key. If you have an existing ssh key of that type, ssh-keygen will ask you if you want to overwrite it. Don't overwrite if you're already using your existing public key for encrypting stuff, signing stuff or have set up your public key on a remote system. Again, figuring out how to keep your old ssh key and juggle the ssh keys is outside the scope of this document.

    ssh-keygen will generate the key file. See the example below. On most systems this should happen immediately, but if your system is under heavy load, it may take a few seconds.

    When prompted for where to save the generated key, press the <enter> key to take the default.

    Enter passphrase (empty for no passphrase): examplepassphrase<enter>
    Enter same passphrase again: examplepassphrase<enter>

    When prompted for a passphrase, enter a fairly long phrase that you can remember.

    The passphrase will be used to encrypt your saved keys. The passphrase should be significantly longer and harder to guess than a normal password.

    The classic example is the first line of Coleridge's poem, Xanadu:

    "In Xanadu did Kublai Khan a stately pleasure dome decree"

    Of course, the first time somebody used that as an example, the next day 20,000 users entered that as their passphrase. So don't use that.

    Using a blank passphrase is, of course, generally regarded as stupid, since anybody can then copy your private key. Any purpose for which you think you need a blank passphrase is better done using ssh-agent (see below).

    Your identification has been saved in /home/frodo/.ssh/id_dsa.
    Your public key has been saved in /home/frodo/.ssh/
    The key fingerprint is:
    4f:58:b3:a5:76:16:41:11:d1:c9:96:6e:54:26:c3:22 user@shire

    The key fingerprint that is printed is intended to be a more (relatively) human-readable checksum for occasions when you have to manually key in the public key, and need to make certain you did so correctly.

    Check to see that the key file is now in ~/.ssh:

    frodo@shire:~$ ls -l .ssh
    total 8
    -rw------- 1 frodo frodo 668 Feb  4 21:36 id_dsa
    -rw-r--r-- 1 frodo frodo 612 Feb  4 21:36

    Copy ssh public key to remote server, place in ~/.ssh/authorized_keys

    There's plenty of other stuff about setting up ssh out there, but I'm including this bit here just so you don't have to go look for it elsewhere.

    After you run keygen, you'll find two files in ~/.ssh, named id_dsa and id_dsa_pub. Here's an example id_dsa_pub:

    ssh-dss AAAAB3NzaC1kc3MAAACBAOxroI5MRtohI8gWtpEM/+wsY2W8qtpLnPKr1sAMjxZBqb4vmXtJ
    wJe6Hy0WrzRqPox0wxpfEHUqiA== frodo@shire

    Specifically, now you have to get the contents of this file:


    Into this file:


    If you're setting up a really secure setup, you're going to need to manually re-key the public key instead of sending it over a potentially compromised network connection, even using another, already existing public key, because that might potentially be compromised.

    However, odds are you're not setting up a really secure network, or that if you are, you're too lazy to really set it up securely by doing it the hard way and manually re-keying. So there is a simple utility, named ssh-installkeys, written by Eric Raymond, in Python. ssh-installkeys makes it easier to create and copy the ssh keys to the right place in the remote account (~/.ssh/authorized_keys).

    Here's the brute-force obvious, insecure way to get ssh-installkeys. Of course, any real security paranoid would immediately worry about whether or not the ssh-installkeys code downloaded is compromised, or about whether your local python binaries are compromised. But we're being lax today, so:

    frodo@shire:~$ wget
    frodo@shire:~$ gunzip ssh-installkeys-1.4.tar.gz
    frodo@shire:~$ tar -xf ssh-installkeys-1.4.tar
    frodo@shire:~$ cd ssh-installkeys-1.4

    However, before you actually run ssh-installkeys, see step 3.

    make sure remote ~/.ssh/authorized_keys is chmod g-w

    Note: Since I wrote this, this detail may have been fixed already.

    For some systems, you need to make sure that the remote file containining the public key has the correct permissions. If it doesn't, ssh will prompt you for a password even if the public key is correct and in the right place.

    I'm not sure which systems have this requirement, nor how to check yours, other than actually setting it up and trying it. But if your system requires g-w permissions on authorized_keys, you can do it manually or you can edit the ssh-installkeys script to have it do it for you. Near the end of the ssh-installkeys script file you will find this line:

                 session.scp(keyfile, "~/.ssh/authorized_keys")

    Immediately afterward, insert the following line:

                 session.ssh("/bin/chmod u+r,g-w,o-w ~/.ssh/authorized_keys")

    The entire paragraph should look like this:

             # OK, install keys remotely if they don't exist
             if not check and not session.exists("~/.ssh/authorized_keys"):
                 session.scp(keyfile, "~/.ssh/authorized_keys")
                 session.ssh("/bin/chmod u+r,g-w,o-w ~/.ssh/authorized_keys")
        except SystemExit:

    Python uses indentation to indicate block structure, so make certain that the chmod line is indented with spaces to the exact same indentation as the scp line, so python will know they're both in the same block.

    Run ssh-installkeys with the remote username frodo and hostname mordor:

    frodo@shire:~$ ./ssh-installkeys frodo@mordor
    Checking your local configuration...
    I see the following public keys:
    id_dsa corresponding to exists and is readable.
    Local configuration looks correct.
    Starting session to mordor...
    Remote password:
    Remote ~ permissions are drwx------ which is OK.
    Remote ~/.ssh doesn't exist.
    Creating remote ~/.ssh
    Remote ~/.ssh permissions are drwxrwxr-x which is wrong.
    User read should be on, Group and Other write permissions should be off.
    Correcting permissions...
    Keys for frodo on shire have been installed.

    run ssh-agent with ssh-add and an argument to start a bash shell and do everything from there.

    Even though you have a public/private key pair set up, the private key is encrypted with your passphrase. You could simply not use a passphrase if the situation is otherwise secure enough, but in general usage, this is considered stupid. Instead, use ssh-agent.

    ssh-agent acts as a sort of key server; to be specific, ssh-agent doesn't serve your actual private key, it serves operations that can only be done with a copy of the key. When ssh wants to do something that requires your private key, it tries to get ssh-agent to do it first. If it can't, then it asks you for your passphrase so it can decrypt your private key.

    So ssh-agent never hands out your private key, and your private key file is never in an unencrypted state. There's still a potential that somebody could run an evil program that scans memory to get your private key. Some people set ssh-agent to timeout the private key every 5 or 10 minutes.

    There are certain normal ways ssh-agent is used (see below), but here's how I set it up for quick and dirty use. I created a simple bash script:

    ssh-agent sh -c 'ssh-add < /dev/null && bash'

    This starts ssh-agent and tells it to run a shell and in that shell run the command ssh-add. ssh-add prompts me for my passphrase and uses that to decrypt my private key and load it into the ssh-agent daemon. Then ssh-agent starts a bash shell process, and the new bash shell inherits the SSH_AUTH_SOCK environment variable from the ssh-agent process.

    Any subsequent processes I start from that bash shell will also inherit the SSH_AUTH_SOCK. When I ultimately run ssh, the ssh process will also inherit SSH_AUTH_SOCK and will use the socket value from the environment variable to ask the ssh-agent process for help logging me into the remote system.

    I later figured out the right way to set it up for bash shell. I simply added the following line to the end of my .bash_profile file:

    eval `ssh-agent`

    Then, after I logged out and logged back in, my initial bash shell had the SSH_AUTH_SOCK environment variable and the rest of my processes inherited it.

    make sure your ssh commands are not using the -1 argument unless you are using rsa1

    The ssh commands in your scripts have to use the same sort of public/private keys as you've generated, so make sure you are. Generally this means not specifying -1 on your ssh command, unless you generated your ssh keys using rsa1.

    Somewhat useful URLs

    Here are three somewhat useful URLs that I read when putting this all together:

    These next three URLs were extremely helpful, but of course only came up in google hits after I learned enough to ask the right questions. Note that the second paragraph of the snailbook pagef contradicts the developerworks page. I'm kinda leanings towards the snailbook's explanation, mainly because that sounds like just the sort of twisty thing that cryptogeeks would do.

    However, the developerworks URL is handy because it discusses keychain, which I haven't otherwise dealt with.

    How This Is Supposed To Work

    This is actually not that tricky once you understand it, but it's hard to explain clearly. See the URLs above. Instead of describing what could or couldn't happen, I'll describe what happens when everything goes right.

    1. The ssh-agent daemon is started in such a way that the numeric value of the ssh-agent socket gets stored in the starting process' environment variable SSH_AUTH_SOCK.
    2. Normally this is done by using the eval command to start ssh-agent; ssh-agent's output is shell commands that set the environment variables.
    3. Alternatively, as described above, you can run ssh-agent with arguments that tell ssh-agent to start a new shell, which then inherits the environment variables from the ssh-agent process.
    4. Any subsequent processes inherit the SSH_AUTH_SOCK environment variable
    5. At some point you run ssh-add, starting an ssh-add process that inherits the SSH_AUTH_SOCK environment variable.
    6. ssh-add prompts you for your passphrase, uses your passphrase to decrypt your private key, then uses the SSH_AUTH_SOCK environment variable to load your private key into ssh-agent
    7. Eventually you use one of these processes - which already have SSH_AUTH_SOCK - to run ssh, which starts a new ssh process; the new ssh process inherits the SSH_AUTH_SOCK environment variable; the new ssh process uses the socket number in SSH_AUTH_SOCK to ask ssh-agent for your private key.
    8. Your ssh process collaborates with ssh-agent to log you into the remote system without needing you to input your passphrase.

    Note: if you run ssh and ssh-agent does NOT have your private key loaded via ssh-add already, ssh will prompt you for your passphrase but WILL NOT load your private key into ssh-agent.

    Starting ssh-agent Properly

    When you run ssh-agent, you run it with the eval command:

    frodo@shire:~$ eval `ssh-agent`

    This results in your process taking whatever output ssh-agent produces and executing them as shell commands. This output includes commands to set the SSH_AUTH_SOCK environment variable in your process. Now any further processes you spawn will inherit the SSH_AUTH_SOCK environment variable, and know how to talk to the ssh-agent daemon.

    Starting ssh-agent Properly In Xwindows

    In normal usage, usually the "eval `ssh-agent`" command is put in your .xinitrc so it gets started during Xwindows startup, and in turn the rest of the Xwindows related processes inherit the SSH_AUTH_SOCK environment variable.

    Starting ssh-agent Properly In Bash Shell

    Put the "eval `ssh-agent`" line in ~/.bash_profile, and then when you log in, the first thing you do is run ssh-add.

    See original (unformatted) article


    Verification Image:
    Your Email Address:
    Confirm Address:
    Please Post:
    Copyright: By checking the "Please Post" checkbox you agree to having your feedback posted on notablog if the administrator decides it is appropriate content, and grant compilation copyright rights to the administrator.
    Message Content: