Key Management and Agents

Your SSH private key is a precious thing. When you use public-key authentication, your key proves your identity to SSH servers. We've encountered several programs related to keys:
ssh-keygen
Creates key pairs
ssh-agent
Holds private keys in memory, saving you from typing your passphrase repeatedly
ssh-add
Loads private keys into the agent
However, we haven't gone into much depth, covering only the most basic operations with keys. Now it's time to examine these concepts and programs in detail.
We begin with an overview of SSH identities and the keys that represent them. After that, we thoroughly cover SSH agents and their many features. Finally, we extol the virtues of having multiple SSH identities. If you've been getting by with a single key and only light agent use, we have a lot of cool stuff in store for you. Figure 6-1 summarizes the role of key
management in the overall configuration process.
This chapter is the first in a sequence on advanced SSH for end users, as opposed to system administrators. Once you've covered key management in this chapter, we'll take you through client configuration, server configuration, and forwarding.
Figure 6.1. SSH user key and agent configuration (highlighted parts)
Book: SSH, The Secure Shell: The Definitive Guide Section: Chapter 6. Key Management and Agents
6.1 What Is an Identity?
An SSH identity is a sequence of bits that says, "I am really me." It is a mathematical construct that permits an SSH client to prove itself to an SSH server, so the SSH server will say, "Ah, I see, it's really you. You hereby are authenticated. Come in."
An identity consists of two parts, called the private key and the public key. Together, they are known as a key pair.
The private key represents your identity for outgoing SSH connections. When you run an SSH client in your account, such as ssh or scp, and it requests a connection with an SSH server, the client uses this private key to prove your identity to the server.
The public key represents your identity for incoming connections to your account. When an SSH client requests access to your account, using a private key as proof of identity, the SSH server examines the corresponding public key. If the keys "match" (according to a cryptographic test [Section 3.4.2.2]), authentication succeeds and the connection proceeds.
Public keys don't need to be secret; they can't be used to break into an account.
[1]
A key pair is typically stored in a pair of files with related names. In SSH, the public key filename is formed by adding the suffix .pub to the private key filename. For example, if
[2]
You may have as many SSH identities as you like. Most SSH-1 and SSH-2 implementations let you specify a default identity clients use unless told otherwise. To use an alternative identity, you must change a setting by command-line argument, configuration file, or some other configuration tool.
The structure of identity files differs for SSH1, SSH2, and OpenSSH, so we will explain them separately. Their locations in the filesystem are shown in Figure 6-2 (private keys)
and Figure 6-3 (public keys).
Figure 6.2. SSH identity files (private keys) and the programs that use them
page232image19088
Private keys must be kept secret. An intruder with your private key can access your account as easily as you can.
the file mykey holds a private key, its corresponding public key is found in mykey.pub.
Figure 6.3. SSH authorization files (public keys) and the programs that use them
6.1.1 SSH1 Identities
An SSH1 identity is stored in two files. By default in SSH1, the private key is stored in the file identity and the public key in identity.pub. This key pair, which is kept in your ~/.ssh directory, is your default identity clients use unless told otherwise.
The .pub file containing your public key has no function by itself. Before it can be used for authentication, this public key must be copied into an authorization file on an SSH-1 server
machine, such as ~/.ssh/authorized_keys for SSH1 or OpenSSH. Thereafter, when an SSH- 1 client requests a connection to your server account using a private key as proof of identity, the SSH1 server consults your authorized_keys file to find the matching public key. [Section 3.4.2.2]
6.1.2 SSH2 Identities
An SSH2 key pair, like its SSH1 ancestor, is stored in two files with the same relationship between their names (i.e., the private key filename plus .pub yields the public key filename). SSH2 key files are often named based on the key's cryptographic properties. For example, a 1024-bit, DSA-encrypted key is generated by default in the SSH2 files id_dsa_1024_a and id_dsa_1024_a.pub.
Unlike SSH1, however, an SSH2 identity is not a single key but a collection of keys. When an SSH2 client tries to authenticate, it may use all keys in the collection. If the first key fails to authenticate, the SSH2 client automatically tries the second, and so forth, until it succeeds or fails completely.
To create an identity in SSH2, private keys must be listed in a file called an identification [3]
file. Your default identity is stored in ~/.ssh2/identification. Inside the file, private keys are listed one per line. For public-key authentication, a line begins with the keyword IdKey, followed by the name of the private key file:
# SSH2 identification file
# The following names are relative to ~/.ssh2
IdKey id_dsa_1024_a
IdKey my-other-ssh2-key
# Absolute paths work for SSH2 2.1.0 and later
IdKey /usr/local/etc/third-key
You may recall that SSH2 supports PGP key authentication. [Section 5.5.1.6] The identification file may also contain PGP-related keywords:
# SSH2 identification file
PgpSecretKeyFile my-file.pgp
IdPgpKeyName my-key-name
Using a separate identification file might seem cumbersome, but it provides flexibility SSH1 doesn't. As we've said, it permits an identity to contain multiple keys, any one of which may authenticate you. Another advantage of the SSH2 system is ease of deletion. To remove an SSH2 private key from consideration, simply delete or comment out its line from the identification file. To accomplish the same task with SSH1, you have to rename the private key file.
Like SSH1, SSH2 has an authorization file for incoming connections but with a difference. Instead of containing copies of public keys, the SSH2 authorization file merely lists the public key files using the Key keyword:
# SSH2 authorization file
Key id_dsa_1024_a.pub
Key something-else.pub
This is easier to maintain than SSH1's authorized_keys file because only one copy of each public key exists. For SSH1 and OpenSSH, in contrast, there are separate copies in the .pub and authorized_keys files. [Section 8.2.2]
6.1.3 OpenSSH Identities
For SSH-1 connections, OpenSSH uses exactly the same identity and authorization files as SSH1. For SSH-2 connections, your default identity is stored in the file ~/.ssh/id_dsa (private key) and ~/.ssh/id_dsa.pub (public key). The SSH-2 authorization file for OpenSSH is ~/.ssh/authorized_keys2, and its format is similar to that of ~/.ssh/ authorized_keys. [Section 8.2.1]
[1]
In contrast, some Windows implementations such as F-Secure SSH Client store keys in the
Windows Registry.
[2]
In fact, in SSH1, the so-called "private key file" contains the public key as well, for
completeness, and only the part of the file containing the private key is encrypted with the passphrase. But the private key file is in a private binary format; the public key file is there for human convenience, to make it easy to add the public key to an authorized_keys file with a text editor, for example.
[3] This default may be changed with the IdentityFile keyword. [Section 7.4.2]
Book: SSH, The Secure Shell: The Definitive Guide Section: Chapter 6. Key Management and Agents
6.2 Creating an Identity
Most SSH implementations include a program for creating key pairs. We will cover ssh-keygen from SSH1, SSH2, and OpenSSH.
6.2.1 Generating RSA Keys for SSH1
SSH1 and its derivatives use the program ssh-keygen1 to create key pairs. [Section 2.4.2] The program might also be called ssh- keygen, depending on how SSH1 was installed. Let's go into more detail about this program than we have before. Appendix B summarizes the ssh-keygen options.
ssh-keygen1 can create new keys or modify existing keys. When creating a new key, you may indicate the following with command- line options:
  • ●  The number of bits in the key, using -b. The default is 1024 bits. $ ssh-keygen1 -b 2048
  • ●  The name of the private key file to be generated, using -f. The name is relative to your current directory. Recall that the public key file is named after the private one with .pub appended. If you omit this option, you are prompted for the information.
    $ ssh-keygen1 -f mykey Creates
    mykey and mykey.pub
    $ ssh-keygen1
    Enter file in which to save the key (/home/barrett/.ssh/identity): mykey

  • ●  The passphrase to decode the key, using -N. If you omit this option, you'll be prompted after generation.
    $ ssh-keygen1 -N secretword
    $ ssh-keygen1
    Enter passphrase:
    [nothing is echoed] Enter the same passphrase again: [nothing is echoed]
  • ●  A textual comment associated with the key, using -C. If you omit this option, the comment will be "username@host", where username is your username on the local machine and host is the fully qualified domain name of the local machine:
         $ ssh-keygen1 -C "my favorite key"
    
    If you specify both -f (specify output file) and -N (specify password), ssh-keygen1 issues no prompts. Therefore, you can automate key generation using these options (and perhaps redirecting output to /dev/null ) :
    $ ssh-keygen1 -f mykey -N secretword
    
    You might use this technique to automate generation of a large number of keys for some purpose. Use it carefully, though, on a secure machine. The password on the command line is probably visible to other users on the same Unix machine via ps or similar programs, and if you're scripting with this technique, obviously the passphrases shouldn't be kept in files for long.
    In addition to creating keys, ssh-keygen1 can modify existing keys in the following ways:
By changing the passphrase of an existing key, using -p. You can specify the filename with -f and the old and new
passphrases with -P and -N, respectively:
$ ssh-keygen1 -p -f mykey -P secretword -N newword but if you omit them, you are prompted:
$ ssh-keygen1 -p
Enter file key is in (/home/barrett/.ssh/identity): mykey Enter old passphrase:
[nothing is echoed]
Key has comment 'my favorite key'
Enter new passphrase:
[nothing is echoed]
Enter the same passphrase again:
Note that this changes the passphrase but doesn't change the key, it just re-encrypts the key with the new passphrase. So, the corresponding public key file doesn't change or need to be replaced on the SSH server machines to which you've copied it.
page237image5232
Before using any option that places your passphrase on the shell command line, such as -N or -P, carefully consider the security implications. Because the passphrase appears on your screen, it may be visible to onlookers, and while running, it may be visible in the machine's process list. Because it is on the command line, it is visible to other users on the same host using the ps command. In addition, if your shell creates history files of the commands you type, the passphrase is inserted into a history file where it can be read by a third party.
Also, if you think have a good reason to just type Return and give your key no passphrase, think again. Doing that is essentially equivalent to putting your password in a file in your home directory named MY-PASSWORD.PLEASE-STEAL-ME. If you don't want to have to type a passphrase, the right thing to do is to use ssh-agent, trusted-host authentication, or Kerberos. There are very limited circumstances having to do with unattended usage (e.g., cron jobs) where a plaintext, passphrase-less client key might be acceptable. [Section 11.1]
  • ●  By changing the comment of an existing key, using -c. You may specify the filename, passphrase, and new comment with -f, -P, and -C, respectively, or you are prompted for them:
    $ ssh-keygen -c -f mykey -P secretword -C "my second-favorite key" $ ssh-keygen -c
    Enter file key is in (/home/barrett/.ssh/identity): mykey
    Enter passphrase:
    [nothing is echoed]
         Key now has comment 'my favorite key'
         Enter new comment: my second-favorite key
         The comment in your key file has been changed.
    
  • ●  By upgrading an old SSH1 key to work with the current version of SSH1, with -u. Older versions of SSH1 used the IDEA algorithm to encrypt a key with its passphrase, but nowadays SSH1 uses 3DES for this purpose, rendering these older keys unusable. The -u option causes ssh-keygen1 to decrypt the key and reencrypt it with SSH1's default algorithm (normally 3DES) to use with the current version of SSH1:
    $ ssh-keygen1 -u -f mykey -P secretword
    $ ssh-keygen1 -u
    Enter file key is in (/home/barrett/.ssh/identity): mykey Enter passphrase:
    [nothing is echoed]
    Key's cipher has been updated.
    When you make changes to a key, such as its passphrase or comment, the changes are applied to the key file only. If you have keys loaded into an SSH agent, the copies in the agents aren't changed. For instance, if you list the keys in the agent with ssh-add1 -l (lowercase L) after changing the comment, you still see the old comment in the agent. To make the changes take effect in the agent, unload and reload the affected keys.
    6.2.2 Generating RSA/DSA Keys for SSH2
    SSH2 and its derivatives use the cleverly named program ssh-keygen2 to create key pairs. The program might also be called ssh- keygen, depending on how SSH2 was installed. As with ssh-keygen1, you can create new keys or modify existing ones; however, the command-line options are significantly different. ssh-keygen2 also has a few other options for printing diagnostics.
When creating a new key, you may choose the name of the private key file to be generated, by specifying the name at the end of the
command line:
$ ssh-keygen2 mykey creates mykey and mykey.pub
The name is relative to your current directory, and as usual, the public key file is named after the private one with .pub appended. If you omit this option, the key is saved in the directory ~/.ssh2, in a file whose name indicates the encryption algorithm and number of bits. An example is id_dsa_1024_a, which was generated by the DSA algorithm with 1024 bits.
You also may indicate the following with command-line options:
The number of bits in the key, using -b. The default is 1024 bits. $ ssh-keygen2 -b 2048
[4]
  • ●  If you omit this option, the generated comment describes how and by whom the key was generated. For example:
         "1024-bit dsa, barrett@server.example.com, Tue Feb 22 2000 02:03:36"
    
  • ●  The passphrase to decode the key, using -p. If you omit this option, you are prompted after generation. $ ssh-keygen2 -p secretword
    You can also designate an empty password using -P. This shouldn't be done in general but is appropriate in some special cases. [Section 11.1.2.2]
         $ ssh-keygen2 -P
    
    In addition to creating keys, ssh-keygen2 can operate on existing keys in the following ways:
By changing the passphrase and comment of an existing key, using -e. This option causes ssh-keygen2 to become interactive, prompting for the new information. This interactive mode is primitive and annoying, requiring nearly 10 user responses to change the passphrase and comment, but it does the job:
$ ssh-keygen2 -e mykey
Passphrase needed for key "my favorite SSH2 key"
Passphrase :
[nothing is echoed]
Do you want to edit key "my favorite SSH2 key" (yes or no)? yes Your key comment is "my favorite SSH2 key".
Do you want to edit it (yes or no)? yes
New key comment: this is tedious
Do you want to edit passphrase (yes or no)? yes
New passphrase :
[nothing is echoed]
Again : [nothing is echoed]
Do you want to continue editing key "this is tedious" (yes or no)? god no (yes or no)? no
Do you want to save key "this is tedious" to file mykey (yes or no)? yes

As with ssh-keygen1, changes are applied to the key files but not propagated to the copies currently loaded in an agent. (So if you do an ssh-add2 -l to list the keys, for example, you see the old comment.)
  • ●  The key type, such as DSA or RSA, using -t. The default-and only option-for SSH2 is DSA (given as "dsa"): $ ssh-keygen2 -t dsa
  • ●  A textual comment associated with the key, using -c:
    $ ssh-keygen2 -c "my favorite SSH2 key"
By printing the public key, deriving it from the private key, with -D, in case you ever lose your private key file:
     $ ssh-keygen2 -D mykeyfile
     Passphrase : ********
     Public key saved to mykeyfile.pub
By converting an SSH-1 format key to SSH-2 format, using -1 (that's the digit "one", not a lowercase L). This isn't currently implemented.
     $ ssh-keygen2 -1 ssh1key
ssh-keygen2 also gives you some control over input, output and diagnostics:
  • ●  By printing the fingerprint of a given key file, with -F. See Sidebar "Key Fingerprints" for more information. The fingerprint
    can be calculated from the public key:
         # SSH2 only
         $ ssh-keygen2 -F stevekey.pub
         Fingerprint for key:
         xitot-larit-gumet-fyfim-sozev-vyned-cigeb-sariv-tekuk-badus-bexax
    
  • ●  By printing the program version number, with -V: $ ssh-keygen2 -V
         ssh2: SSH Secure Shell 2.1.0 (noncommercial version)
    
  • ●  By printing a help message, with -h or -?. Most Unix shells require you to escape the question mark, to prevent the shell from interpreting it as a wildcard.
    $ ssh-keygen2 -h
    $ ssh-keygen2 -\?
    Escaping the question mark
  • ●  By suppressing the progress indicator, using -q. The progress indicator is a sequence of O's and periods that displays while ssh-keygen2 runs, like this: .oOo.oOo.oOo.oOo.
         $ ssh-keygen2
         Generating 1024-bit dsa key pair
         .oOo.oOo.oOo.oOo
         Key generated.
    
         $ ssh-keygen2 -q
         Generating 1024-bit dsa key pair
         Key generated.
    
  • ●  By displaying information about an existing key, using -i: $ ssh-keygen2 -i mykey
    This isn't currently implemented.
    Finally, ssh-keygen2 has one guru-level advanced option, -r, for affecting the random numbers used for key generation. It causes ssh- keygen2 to modify ~/.ssh2/random_seed using data you enter on standard input. [Section 3.7] The SSH2 manpages call this "stirring data into the random pool." Note that the program doesn't prompt you to enter data; it just sits there looking like it's hung. When this occurs, type as much data as you like and press the EOF character (Control-D in most shells).
    $ ssh-keygen2 -r
    I am stirring the random pool.
    blah blah blah
    ^D
    Stirred in 46 bytes.
    
6.2.3 Generating RSA/DSA Keys for OpenSSH
OpenSSH's ssh-keygen program supports all the same features and options of its SSH1 counterpart. It also adds the capability to generate DSA keys for SSH-2 connections and a few more options:
  • ●  -d generates a DSA key instead of an RSA key: # OpenSSH only
         $ ssh-keygen -d
    
  • ●  -x, -X, and -y convert between SSH2 and OpenSSH key-storage formats. The following table illustrates this:
Option
Extract/Convert from...
To...
-x
OpenSSH DSA private key file
SSH2 public key
-X
SSH2 public key file
OpenSSH DSA public key
-y
OpenSSH DSA private key file
OpenSSH DSA public key
An OpenSSH "private" key file actually contains both the public and private keys of a pair, so the -x and -y options simply extract the public key and print it out in the desired format. Use -x to add an OpenSSH public key to your ~/.ssh2/ authorization file on an SSH2 server host and -X to do the opposite. The -y option is useful if you accidentally delete your OpenSSH public key file and need to restore it.
A function that's missing is converting the private keys as well. This is useful if you have an OpenSSH server host on which you also want to also run SSH2, and you want the two SSH servers to share a host key.
-l prints the fingerprint of a given key file. See Sidebar "Key Fingerprints" for more information. The fingerprint can be calculated from the public key:
  # OpenSSH only
  $ ssh-keygen -l -f stevekey.pub
  1024 5c:f6:e2:15:39:14:1a:8b:4c:93:44:57:6b:c6:f4:17 steve@sshbook.com
Key Fingerprints
Fingerprints are a common cryptographic feature for checking that two keys in different places are the same, when comparing them literally-bit by bit-is infeasible. OpenSSH and SSH2 can compute fingerprints.
Suppose Steve wants SSH access to Judy's account. He sends his public key to Judy by email, and she installs it in her SSH authorization file. While this key exchange seems straightforward, it is insecure: a hostile third party could intercept Steve's key and substitute his own, gaining access to Judy's account.
To prevent this risk, Judy needs some way to verify that the key she receives is Steve's. She could call Steve on the telephone and check, but reading a 500-byte encrypted public key over the phone is annoying and error-prone. This is why fingerprints exist.
A fingerprint is a short value computed from a key. It's analogous to a checksum, verifying that a string of data is unaltered-in our case, a key. To check the validity of a key using fingerprints, Steve and Judy can do the following:
1. Judy receives a public key that is supposed to be Steve's, storing it in the file stevekey.pub. 2. Separately, Judy and Steve view the fingerprint of the key:
page240image40304 page240image40464 page240image40624 page240image40784 page240image40944 page240image41104
page241image416 page241image576 page241image736 page241image896
           # OpenSSH only
           $ ssh-add -l stevekey.pub
           1024 5c:f6:e2:15:39:14:1a:8b:4c:93:44:57:6b:c6:f4:17 Steve@sshbook.com
           # SSH2 only
           $ ssh-keygen2 -F stevekey.pub
           Fingerprint for key:
           xitot-larit-gumet-fyfim-sozev-vyned-cigeb-sariv-tekuk-badus-bexax
3. Judy calls Steve on the telephone and asks him to read the fingerprint over the phone. Judy verifies that it matches the fingerprint of the key she received. Fingerprints aren't unique, but for any two keys, the probability that their fingerprints are identical is small. Therefore, keys are a quick and convenient method for checking that a key is unaltered.
As you can see, OpenSSH and SSH2 use different output formats for fingerprints. OpenSSH's numeric format is more traditional and should be familiar to users of PGP. SSH2 uses a textual format called "Bubble Babble" which is claimed to be easier to read and remember.
Fingerprints also surface when you connect to an SSH server whose host key has changed. In this case, OpenSSH prints a warning message and the fingerprint of the new key, which may be conveniently compared with the fingerprint of the real host key, should you have it.
-R detects whether OpenSSH supports RSA keys or not. Because RSA was patented technology until September 2000, a particular installation of OpenSSH may or may not include this algorithm. [Section 3.9.1.1] If you invoke ssh-keygen with this option, it immediately exits with a code of 0 if RSA is supported, or 1 if it isn't.
     # OpenSSH only, with RSA support
     $ ssh-keygen -R; echo $?
     0
     # OpenSSH only, without RSA support
     $ ssh-keygen -R; echo $?
     1
6.2.4 Selecting a Passphrase
Choose your passphrases carefully. Make them at least 10 characters long, containing a mix of uppercase and lowercase letters, digits, and nonalphanumeric symbols. At the same time, you want the passphrase to be easy to remember, but hard for others to guess. Don't use your name, username, phone number, or other easily guessed information in the passphrase. Coming up with an effective passphrase can be a chore, but the added security is worth it.
If you forget a passphrase, you are out of luck: the corresponding SSH private key becomes unusable because you can't decrypt it. The same encryption that makes SSH so secure also makes passphrases impossible to recover. You have to abandon your SSH key, generate a new one, and choose a new passphrase for it. You must also install the new public key on every machine that had your original.
[4]
F-Secure SSH2 Server adds support for RSA (argument "rsa") but only in a limited sense. [Section 3.9]
page241image23200 page241image23360
Book: SSH, The Secure Shell: The Definitive Guide Section: Chapter 6. Key Management and Agents
6.3 SSH Agents
An SSH agent is a program that caches private keys and responds to authentication-related queries from SSH clients. [Section 2.5] They are terrific labor-saving devices, handling all key-related operations and eliminating the need to retype your passphrase.
The programs related to agents are ssh-agent and ssh-add. ssh-agent runs an agent, and ssh-add inserts and removes keys from the agent's key cache. A typical use might look like this:
# Start the agent
$ ssh-agent $SHELL
# Load your default identity
$ ssh-add
Need passphrase for /home/barrett/.ssh/identity (barrett@example.com).
Enter passphrase: ********
By typing your passphrase a single time, you decrypt the private key which is then stored in memory by the agent. From now on, until you terminate the agent or log out, SSH clients automatically contact the agent for all key-related operations. You needn't type your passphrase again.
We now briefly discuss how agents work. After that we get practical and illustrate the two ways to start an agent, various configuration options, and several techniques for automatically loading your keys into the agent. Finally, we cover agent security, agent forwarding, and compatibility between SSH-1 and SSH-2 agents.
6.3.1 Agents Don't Expose Keys
Agents perform two tasks:
  • ●  Store your private keys in memory
  • ●  Answer questions (from SSH clients) about those keys
    Agents don't, however, send your private keys anywhere. This is important to understand. Once loaded, private keys remain within an agent, unseen by SSH clients. To access a key, a client says, "Hey agent! I need your help. Please perform a key-related operation for me." The agent obeys and sends the results to the client (see Figure 6-4).
    Figure 6.4. How an SSH agent works with its clients
    For example, if ssh needs to sign an authenticator, it sends the agent a signing request containing the authenticator data and an indication of which key to use. The agent performs the cryptographic operation itself and returns the signature.
page242image18840
In this manner, SSH clients use the agent without ever seeing the agent's private keys. This technique is more secure
[5]
than handing out keys to clients. The fewer places that private keys get stored or sent, the harder it is to steal them.
6.3.2 Starting an Agent
There are two ways to invoke an agent in your login account:
  • ●  The single-shell method that uses your current login shell
  • ●  The subshell method that forks a subshell to facilitate the inheritance of some environment variables
    6.3.2.1 Single-shell method
    The single-shell method runs an agent in your current login shell. This is most convenient if you're running a login shell on a single terminal, as opposed to a Unix window system such as X. Type:
    # SSH1, SSH2, OpenSSH
    $ eval `ssh-agent`
    
    and an ssh-agent process is forked in the background. The process detaches itself from your terminal, returning a prompt to you, so you needn't run it in the background manually (i.e., with an ampersand on the end). Note that the quotes around ssh-agent are backquotes, not apostrophes.
    What purpose does the eval serve? Well, when ssh-agent runs, it not only forks itself in the background, it also outputs some shell commands to set several environment variables necessary for using the agent. The variables are SSH_AUTH_SOCK (for SSH1 and OpenSSH) or SSH2_AUTH_SOCK (SSH2), and SSH_AGENT_PID (SSH1,
    [6]
    OpenSSH) or SSH2_AGENT_PID (SSH2). The eval command causes the current shell to interpret the commands output by ssh-agent, setting the environment variables. If you omit the eval, these commands are printed on standard output as ssh-agent is invoked. For example:
    $ ssh-agent
    SSH_AUTH_SOCK=/tmp/ssh-barrett/ssh-22841-agent; export SSH_AUTH_SOCK;
    SSH_AGENT_PID=22842; export SSH_AGENT_PID;
    echo Agent pid 22842;
    
    Now you've got an agent running but inaccessible to the shell. You can either kill it using the pid printed in the previous output:
    $ kill 22842
    or connect your shell manually by setting the environment variables exactly as given:
page243image19064
Don't invoke an agent with the "obvious" but wrong command:
$ ssh-agent
Although the agent runs without complaint, SSH clients can't contact it, and the termination command (ssh-agent -k) doesn't kill it, because some environment variables aren't properly set.
$ SSH_AUTH_SOCK=/tmp/ssh-barrett/ssh-22841-agent; export SSH_AUTH_SOCK;
$ SSH_AGENT_PID=22842; export SSH_AGENT_PID;
[7]
Nevertheless, it's easier to use the single-shell form of the command so everything is set up for you.
To terminate the agent, kill its pid:
# SSH1, SSH2, OpenSSH
$ kill 22842
and unset the environment variables:
$ unset SSH_AUTH_SOCK
# SSH2 uses SSH2_AUTH_SOCK instead
$ unset SSH_AGENT_PID
Or for SSH1 and OpenSSH, use the more convenient -k command-line option: # SSH1, OpenSSH
$ eval `ssh-agent -k`
This prints termination commands on standard output so the eval can invoke them. If you eliminate the eval, the agent is still killed, but your environment variables don't unset automatically:
# SSH1, OpenSSH
$ ssh-agent1 -k
unset SSH_AUTH_SOCK
unset,
unset SSH_AGENT_PID
this,
echo Agent pid 22848 killed
# This won't get
# and neither will
# but the agent gets killed.
Running an agent in a single shell, as opposed to the method we cover next (spawning a subshell), has one problem. When your login session ends, the ssh-agent process doesn't die. After several logins, you see many agents running,
[8]
serving no purpose.
$ /usr/ucb/ps uax | grep ssh-agent
barrett   7833  0.4  0.4  828  608 pts/1    S 21:06:10  0:00 grep agent
barrett   4189  0.0  0.6 1460  844 ?
barrett   6134  0.0  0.6 1448  828 ?
barrett   6167  0.0  0.6 1448  828 ?
barrett   7719  0.0  0.6 1456  840 ?
S   Feb 21  0:06 ssh-agent
S 23:11:41  0:00 ssh-agent
S 23:24:19  0:00 ssh-agent
S 20:42:25  0:02 ssh-agent
You can get around this problem by running ssh-agent -k automatically when you log out. In Bourne style shells (sh, ksh, bash), this may be done with a trap of Unix signal at the top of ~/.profile :
# ~/.profile
trap '
  test -n "$SSH_AGENT_PID"  && eval `ssh-agent1 -k` ;
test -n "$SSH2_AGENT_PID" && kill $SSH2_AGENT_PID '0
For C shell and tcsh, terminate the agent in your ~/.logout file:
# ~/.logout
if ( "$SSH_AGENT_PID" != "" ) then
  eval `ssh-agent -k`
endif
if ( "$SSH2_AGENT_PID" != "" ) then
  kill $SSH2_AGENT_PID
endif
Once this trap is set, your ssh-agent process is killed automatically when you log out, printing a message like:
Agent pid 8090 killed
6.3.2.2 Subshell method
The second way to invoke an agent spawns a subshell. You provide an argument to ssh-agent, which is a path to a shell or shell script. Examples are:
$ ssh-agent /bin/sh
$ ssh-agent /bin/csh
$ ssh-agent $SHELL
$ ssh-agent my-shell-script
# Run a shell script instead of a shell
This time, instead of forking a background process, ssh-agent runs in the foreground, spawning a subshell and setting the aforementioned environment variables automatically. The rest of your login session runs within this subshell, and when you terminate it, ssh-agent terminates as well. This method, as you will see later, is most convenient if you run a window system such as X and invoke the agent in your initialization file (e.g., ~/.xsession). However, the method is also perfectly reasonable for single-terminal logins.
When using the subshell method, invoke it at an appropriate time. We recommend the last line of your login initialization file (e.g., ~/.profile or ~/.login) or the first typed command after you log in. Otherwise, if you first run some background processes in your shell, and then invoke the agent, those initial background processes become inaccessible until you terminate the agent's subshell. For example, if you run the vi editor, suspend it, and then run the agent, you lose access to the editor session until you terminate the agent.
$ vi myfile
^Z
$ jobs
processes
[1] + Stopped (SIGTSTP) vi $ ssh-agent $SHELL subshell
$ jobs
in the parent shell
$ exit
subshell
$ jobs
processes again
[1] + Stopped (SIGTSTP) vi
# Run your editor
# Suspend it
# View your background
# Run a
# No jobs here! They're
# Terminate the agent's
# Now we can see our
The advantages and disadvantages of the two methods are shown in Table 6-1.
Table 6.1. Pros and Cons of Invoking an Agent
Method
Pros
Cons
eval `ssh-agent`
Simple, intuitive
Must be terminated manually
ssh-agent $SHELL
Agent's environment variables are propagated automatically; terminates on logout
Your login shell becomes dependent on the agent's health; if the agent dies, your login shell may die
6.3.2.3 Format of environment variable commands
As we've said, ssh-agent prints a sequence of shell commands to set several environment variables. The syntax of these commands differs depending on which shell is being used. You can force the commands to use Bourne-style or C shell-style syntax with the -s and -c options, respectively:
# Bourne-shell style commands
$ ssh-agent -s
SSH_AUTH_SOCK=/tmp/ssh-barrett/ssh-3654-agent; export SSH_AUTH_SOCK;
SSH_AGENT_PID=3655; export SSH_AGENT_PID;
echo Agent pid 3655;
# C-shell style commands
$ ssh-agent -c
setenv SSH_AUTH_SOCK /tmp/ssh-barrett/ssh-3654-agent;
setenv SSH_AGENT_PID 3655;
echo Agent pid 3655;
Normally ssh-agent detects your login shell and prints the appropriate lines, so you don't need -c or -s. One situation where you need these options is if you invoke ssh-agent within a shell script, but the script's shell is not the same type as your login shell. For example, if your login shell is /bin/csh, and you invoke this script:
#!/bin/sh
`ssh-agent`
ssh-agent outputs C shell-style commands, which will fail. So you should use: #!/bin/sh
`ssh-agent -s`
This is particularly important if you run an agent under X, and your ~/.xsession file (or other startup file) is executed by a shell different from your login shell.
6.3.2.4 SSH-1 and SSH-2 agent compatibility
An SSH-1 agent can't service requests from SSH-2 clients. The reverse, however, is supported by SSH2. If ssh- agent2 is invoked with the -1 option (that's a numeral one, not a lowercase L), the agent services requests from SSH- 1 clients, even from ssh-add1. This works only for SSH-2 implementations that support RSA, because SSH-1 uses RSA keys. At press time, only F-Secure SSH2 Server is capable of SSH-1 agent compatibility.
# Invoke an SSH2 agent in SSH1 compatibility mode
$ eval `ssh-agent2 -1`
# Add an SSH1 key
$ ssh-add1
Need passphrase for /home/smith/.ssh/identity (smith SSH1 key).
Enter passphrase: ****
Identity added (smith SSH1 key).
# Add an SSH2 key
$ ssh-add2
Adding identity: /home/smith/.ssh2/id_dsa_1024_a.pub
Need passphrase for /home/smith/.ssh2/id_dsa_1024_a
 (1024-bit dsa, smith SSH2 key, Thu Dec 02 1999 22:25:09-0500).
Enter passphrase: ********
# ssh-add1 lists only the SSH1 key
$ ssh-add1 -l
1024 37 1425047358166328978851045774063877571270
... and so forth
# ssh-add2 lists both keys
# F-Secure SSH Server only
$ ssh-add2 -l
Listing identities.
The authorization agent has 2 keys:
id_dsa_1024_a: 1024-bit dsa, smith SSH2 key, Thu Dec 02 1999 22:25:09-0500
smith SSH1 key
Now an SSH-1 client contacts ssh-agent2 transparently, believing it to be an SSH-1 agent: $ ssh1 server.example.com
[no passphrase prompt appears]
ssh-agent2 achieves compatibility by setting the same environment variables normally set by ssh-agent1: SSH_AUTH_SOCK and SSH_AGENT_PID. Therefore, any SSH-1 agent requests are directed to ssh-agent2.
Agent compatibility works only if the SSH2 distribution is compiled with the flag -with-ssh-agent1-compat. [Section 4.1.5.13] It also depends on the value of the client configuration keyword Ssh1AgentCompatibility. [Section 7.4.14]
6.3.3 Loading Keys with ssh-add
The program ssh-add is your personal communication channel to an ssh-agent process. (Again, this command may be ssh-add1 under SSH1 and ssh-add2 under SSH2, with ssh-add a link to one program or the other.)
When you first invoke an SSH agent, it contains no keys. ssh-add, as you might guess from its name, can add private keys to an SSH agent. But the name is misleading, because it also controls the agent in other ways, such as listing keys, deleting keys, and locking the agent from accepting further keys.
If you invoke ssh-add with no arguments, your default SSH key is loaded into the agent, once you have typed its passphrase. For example:
page247image18296
If you have an ssh-agent1 process running, and you invoke ssh-agent2 -1, your old ssh- agent1 process becomes inaccessible as ssh-agent2 overwrites its environment variables.
$ ssh-add1
Need passphrase for /home/smith/.ssh/identity (smith@client).
Enter passphrase: ********
Identity added: /home/smith/.ssh/identity (smith@client).
$ ssh-add2
Adding identity: /home/smith/.ssh2/id_dsa_1024_a.pub
Need passphrase for /home/smith/.ssh2/id_dsa_1024_a
 (1024-bit dsa, smith@client, Thu Dec 02 1999 22:25:09-0500).
Enter passphrase: ********
Normally, ssh-add reads the passphrase from the user's terminal. If the standard input isn't a terminal, however, and
the DISPLAY environment variable is set, ssh-add instead invokes an X window graphical program called ssh-
[9]
askpass that pops up a window to read your passphrase. This is especially convenient in xdm start-up scripts. Both ssh-add1 and ssh-add2 support the following command-line options for listing and deleting keys, and for
reading the passphrase:
  • ●  List all identities loaded in the agent, with -l:
    $ ssh-add1 -l
    1024 35 1604921766775161379181745950571099412502846
    ... and so forth
    1024 37 1236194621955474376584658921922152150472844... and so forth
         $ ssh-add2 -l
         Listing identities.
         The authorization agent has one key:
         id_dsa_1024_a: 1024-bit dsa, smith@client, Thu Dec 02 1999 22:25:09-0500
    
    For OpenSSH, the -l option operates differently, printing the key's fingerprint rather than the public key (see Sidebar "Key Fingerprints" earlier for more detail):
         # OpenSSH only
         $ ssh-add -l
         1024 1c:3d:cc:1a:db:74:f8:e6:46:6f:55:57:9e:ec:d5:fc smith@client
    
    To print the public key with OpenSSH, use -L instead:
    # OpenSSH only
    $ ssh-add -L
    1024 35 1604921766775161379181745950571099412502846
    ... and so forth
    1024 37 1236194621955474376584658921922152150472844... and so forth
  • ●  Delete an identity from the agent, with -d:
         $ ssh-add -d ~/.ssh/second_id
         Identity removed: /home/smith/.ssh/second_id (my alternative key)
    
     $ ssh-add2 -d ~/.ssh2/id_dsa_1024_a
     Deleting identity: id_dsa_1024_a.pub
If you don't specify a key file, ssh-add1 deletes your default identity from the agent: $ ssh-add -d
     Identity removed: /home/smith/.ssh/identity (smith@client)
ssh-add2, on the other hand, requires you to specify a key file: $ ssh-add2 -d
     (nothing happens)
  • ●  Delete all identities from the agent, with -D. This unloads every currently loaded key but leaves the agent running:
         $ ssh-add -D
         All identities removed.
    
         $ ssh-add2 -D
         Deleting all identities.
    
  • ●  Read the passphrase from standard input, with -p, as opposed to reading directly from your tty. This is useful if you want to send your passphrase to ssh-add in a program, as in this Perl fragment:
         open(SSHADD,"|ssh-add -p") || die "can't start ssh-add";
         print SSHADD $passphrase;
         close(SSHADD);
    
    In addition, ssh-add2 has further features controlled by command-line options:
Lock and unlock the agent with a password using -L and -U. A locked agent refuses all ssh-add2 operations
except an unlock request. Specifically:
  • ❍  If you try to modify the state of the agent (adding or deleting keys, etc.), you are told:
              The requested operation was denied.
    
  • ❍  If you try to list the keys in the agent, you are told:
              The authorization agent has no
              keys.
    
    To lock:
         $ ssh-add2 -L
         Enter lock password: ****
         Again: ****
    
    and to unlock:
$ ssh-add2 -U
Enter lock password: ****
Locking is a convenient way to protect the agent if you step away from your computer but leave yourself logged in. You can unload all your keys with ssh-add -D, but then you'd have to reload them again when you return. If you have only one key, there's no difference, but if you use several, it's a pain. Unfortunately, the locking mechanism isn't tremendously secure. ssh-agent2 simply stores the lock password in memory, refusing to honor any more requests until it receives an unlock message containing the same password. The locked agent is still vulnerable to attack: if an intruder gains access to your account (or the root account), he can dump the agent's process address space and extract your keys. The lock feature certainly deters casual misuse, but the potential for an attack is real. If you're seriously concerned about key disclosure, think twice before relying on locking. We prefer to see this feature implemented by encrypting all the agent's loaded keys with the lock password. This gives the same user convenience and provides better protection.
  • ●  Set a timeout on a key, with -t. Normally when you add a key, it remains loaded in the agent indefinitely, until the agent terminates or you unload the key manually. The -t option indicates the lifetime of a key, measured in minutes. After this time has passed, the agent automatically unloads the key.
      # Unload this key after 30 minutes
      $ ssh-add2 -t 30 mykey
    
  • ●  Place limits on agent forwarding with -f and -F. (Agent forwarding, which we'll cover soon, transmits agent requests between hosts.) The -f option lets you limit, for a given key, the distance that requests for this key may traverse. If a request is made from too far away, measured in hops from machine to machine, the request fails. A hop count of zero disables forwarding for this key alone:
      # Load a key that may be used only  locally
      $ ssh-agent2 -f 0 mykey
    
      # Load a key and accept requests from up to 3 hops away
      $ ssh-agent2 -f 3 mykey
    
    The -F option lets you limit the set of hosts that may make requests relating to this key. It takes as an argument a set of hostnames, domains, and IP addresses that may make or forward requests. The argument is a comma-separated list of wildcard patterns, as for the serverwide configuration keywords AllowHosts and DenyHosts. [Section 5.5.2.3]
      # Permit request forwarding for a key only in the example.com domain
      $ ssh-agent2 -F '*.example.com' mykey
    
      # Permit forwarding from server.example.com and the harvard.edu domain
      $ ssh-agent2 -F 'server.example.com,*.harvard.edu' mykey
    
      # Same as the preceding command, but limit forwarding to 2 hops
      $ ssh-agent2 -F 'server.example.com,*.harvard.edu' -f 2 mykey
    
  • ●  Make the given key invisible to SSH-1 client requests if ssh-agent2 is running in SSH1 compatibility mode, with -1 (that's a one, not a lowercase L). It must be an RSA key, since all SSH1 public keys are RSA, and the only SSH-2 implementation that supports RSA keys (at press time) is F-Secure SSH2 Server. We demonstrate this feature by example:
page250image27416
SSH1 agents don't support this feature. If you use an SSH2 agent in SSH1 compatibility mode, these forwarding features won't necessarily work.
1. Generate an SSH2 RSA key, my-rsa-key: $ ssh-keygen2 -t rsa my-rsa-key
2. Run an agent in SSH1 compatibility mode:
       $ eval `ssh-agent2 -1`
3. Load the key into the agent normally:
       $ ssh-add2 my-rsa-key
       Enter passphrase: ********
As the agent is running in SSH1 compatibility mode, notice that the key is visible to both SSH1 clients:
  $ ssh-add1 -l
  1023 33 753030143250178784431763590... my-rsa-key ...
and SSH2 clients:
  $ ssh-add2 -l
  Listing identities.
  The authorization agent has one key:
  my-rsa-key: 1024-bit rsa, smith@client, Mon Jun 05 2000 23:37:19 -040
Now let's unload the key and repeat the experiment:
  $ ssh-add2 -D
  Deleting all identities.
This time, load the key using the -1 option, so SSH1 clients don't see it: $ ssh-add2 -1 my-rsa-key
  Enter passphrase: ********
Notice that the key is still visible to SSH2 clients:
  $ ssh-add2 -l
  Listing identities.
  The authorization agent has one key:
  my-rsa-key: 1024-bit rsa, smith@client, Mon Jun 05 2000 23:37:19 -040
But SSH1 clients can't see it:
  $ ssh-add1 -l
  The agent has no identities.
Perform PGP key operations. The ssh-add2 manpage documents the options -R, -N, -P, and -F for OpenPGP keyring operations, but at press time they aren't implemented.
6.3.3.1 Automatic agent loading (single-shell method)
It's a pain to invoke ssh-agent and/or ssh-add manually each time you log in. With some clever lines in your login initialization file, you can automatically invoke an agent and load your default identity. We demonstrate this with both methods of agent invocation, single-shell and subshell.
With the single-shell method, here are the major steps:
1. Make sure you're not already running an agent, by testing environment variable SSH_AUTH_SOCK or SSH2_AUTH_SOCK.
2. Run the agent, ssh-agent1 or ssh-agent2, using eval.
3. If your shell is attached to a tty, load your default identity with ssh-add1 or ssh-add2.
For the Bourne shell and its derivatives (
ksh, bash), the following lines can be placed into ~/.profile :
# Make sure ssh-agent1 and ssh-agent2 die on logout
trap '
  test -n "$SSH_AGENT_PID"  && eval `ssh-agent1 -k` ;
test -n "$SSH2_AGENT_PID" && kill $SSH2_AGENT_PID '0
# If no agent is running and we have a terminal, run ssh-agent and ssh-add.
# (For SSH2, change this to use SSH2_AUTH_SOCK, ssh-agent2 and ssh-add2.)
if [ "$SSH_AUTH_SOCK" = "" ]
then
  eval `ssh-agent`
  /usr/bin/tty > /dev/null && ssh-add
fi
For the C shell and tcsh, the following lines can be placed into ~/.login:
# Use SSH2_AUTH_SOCK instead for SSH2
if ( ! $?SSH_AUTH_SOCK  ) then
  eval `ssh-agent`
  /usr/bin/tty > /dev/null && ssh-add
endif
and termination code in ~/.logout :
# ~/.logout
if ( "$SSH_AGENT_PID" != "" ) eval `ssh-agent -k`
if ( "$SSH2_AGENT_PID" != "" ) kill $SSH2_AGENT_PID
6.3.3.2 Automatic agent loading (subshell method)
The second way to load an agent on login uses the subshell method to invoke the agent. This time, you need to add lines to both your login initialization file (~/.profile or ~/.login), an optional second file of your choice, and your shell initialization file (~/.cshrc, ~/.bashrc, etc.). This method doesn't work for the Bourne shell, which has no shell initialization file.
1. In your login initialization file, make sure you're not already running an agent, by testing environment variable SSH_AUTH_SOCK or SSH2_AUTH_SOCK.
2. As the last line of your login initialization file, exec ssh-agent, which spawns a subshell. Optionally run a second initialization file to configure aspects of the subshell.
3. In your shell initialization file, check whether the shell is attached to a tty and that the agent has no identities loaded yet. If so, load your default identity with ssh-add1 or ssh-add2.
Now let's see how to do this with Bourne shell and C shell families. For derivatives of Bourne shell (ksh, bash), put the following lines at the end of ~/.profile :
test -n "$SSH_AUTH_SOCK" && exec ssh-agent $SHELL
This runs the agent, spawning a subshell. If you want to tailor the environment of the subshell, create a script (say, ~/. profile2) to do so, and use this instead:
test -n "$SSH_AUTH_SOCK" && exec ssh-agent $SHELL $HOME/.profile2
Next, in your shell initialization file ($ENV for ksh, or ~/.bashrc for bash), place the following lines to load your default identity only if it's not loaded already:
# Make sure we are attached to a tty
if /usr/bin/tty > /dev/null
then
  # Check the output of "ssh-add -l" for identities.
  # For SSH2, use the line:
  #  ssh-add2 -l | grep 'no keys' > /dev/null
  #
  ssh-add1 -l | grep 'no identities' > /dev/null
  if [ $? -eq 0 ]
  then
    # Load your default identity.  Use ssh-add2 for SSH2.
ssh-add1 fi
fi
6.3.3.3 Automatic agent loading (X Window System)
If you're using X and want to run an agent and load your default identity automatically, it's simple. Just use the single- shell method. For example, in your X startup file, usually ~/.xsession, you can use these two lines:
eval `ssh-agent`
ssh-add
6.3.4 Agents and Security
As we mentioned earlier, agents don't expose private keys to SSH clients. Instead, they answer requests from clients about the keys. This approach is more secure than passing keys around, but it still has some security concerns. It is important to understand these concerns before completely trusting the agent model:
  • ●  Agents rely on external access control mechanisms.
  • ●  Agents can be cracked.
6.3.4.1 Access control
When your agent is loaded with private keys, a potential security issue arises. How does your agent distinguish between legitimate requests from your SSH clients and illegitimate requests from unauthorized sources? Surprisingly, the agent does not distinguish at all. Agents don't authenticate their clients. They will respond to any well-formed request received over their IPC channel, which is a Unix domain socket.
How is agent security maintained then? The host operating system is responsible for protecting the IPC channel from unauthorized access. For Unix, this protection is accomplished by the file permissions on the socket. SSH1 and SSH2 keep your agent sockets in a protected directory, /tmp/ssh-USERNAME, where USENRAME is your login name, while OpenSSH names the directory /tmp/ssh-STRING, where STRING is random text based on the agent's pid. In either case, the directory is protected from all other users (mode 700) and owned by you:
$ ls -la /tmp/ssh-smith/
drwx------   2 smith    smith
drwxrwxrwt   9 root     root
srwx------   1 smith    smith
s-w--w--w-   1 root     root
srw-------   1 smith    smith
1024 Feb 17 18:18 .
1024 Feb 17 18:01 ..
   0 May 14  1999 agent-socket-328
   0 Feb 14 14:30 ssh-24649-agent
   0 Dec  3 00:34 ssh2-29614-agent
In this case, user smith has several agent-related sockets in this directory. The two sockets owned by smith were created by agents run and owned by smith. The third, which is world-writable and owned by root, was created by the
[10]
SSH server to effect an agent forwarding. [Section 6.3.5]
This organization of a user's sockets into a single directory is not only for neatness but also for security and portability, because different operating systems treat socket permissions in different ways. For example, Solaris appears to ignore them completely; even a socket with permission 000 (no access for anyone) accepts all connections. Linux respects socket permissions, but a write-only socket permits both reading and writing. To deal with such diverse implementations, SSH keeps your sockets in a directory owned by you, with directory permissions that forbid anyone else to access the sockets inside.
Using a subdirectory of /tmp, rather than /tmp itself, also prevents a class of attacks called temp races. A temp-race attack takes advantage of race conditions inherent in the common setting of the "sticky" mode bit on the Unix /tmp directory, allowing anyone to create a file there, but only allowing deletion of files owned by the same uid as the deleting process.
6.3.4.2 Cracking an agent
If the machine running your agent is compromised, an attacker can easily gain access to the IPC channel and thus to your agent. This permits the interloper to make requests of the agent, at least for a time. Once you log out or unload your keys from the agent, the security hole is closed. Therefore, you should run agents only on trusted machines, perhaps unloading your keys (ssh-agent -D) if you're away from the computer for an extended time, such as overnight.
Since agents don't give out keys, your keys would seem safe from theft if the machine is compromised. Alas, that's not the case. An enterprising cracker, once logged into the machine, has other means for getting your keys, such as:
  • ●  Stealing your private key file and attempting to guess your passphrase
  • ●  Tracing processes that you're running, and catching your passphrase while you type it
  • ●  Trojan horse attacks: installing modified versions of system programs, such as the login program, shells, or
    the SSH implementation itself, that steal your passphrase
  • ●  Obtaining a copy of the memory space of your running agent and picking the keys out of it directly (this is a
    bit harder than the others)
    The bottom line is this: run agents only on trusted machines. SSH does not excuse you from securing other aspects of your system.
6.3.5 Agent Forwarding
So far, our SSH clients have conversed with an SSH agent on the same machine. Using a feature called agent forwarding, clients can also communicate with agents on remote machines. This is both a convenience feature- permitting your clients on multiple machines to work with a single agent-and a means for avoiding some firewall- related problems.
6.3.5.1 A firewall example
Suppose you want to connect from your home computer, H, to a computer at work, W. Like many corporate computers, W is behind a network firewall and not directly accessible from the Internet, so you can't create an SSH connection from H to W. Hmm... what can you do? You call technical support and for once, they have good news. They say that your company maintains a gateway or "bastion" host, B, that is accessible from the Internet and runs an SSH server. This means you should be able to reach W by opening an SSH connection from H to B, and then from B to W, since the firewall permits SSH traffic. Tech support gives you an account on the bastion host B, and the problem seems to be solved... or is it?
For security reasons, the company permits access to its computers only by public-key authentication. So, using your private key on home machine H, you successfully connect to bastion host B. And now you run into a roadblock: also for security reasons, the company prohibits users from storing SSH keys on the exposed bastion host B, since they can be stolen if B were hacked. That's bad news, since the SSH client on B needs a key to connect to your work account on W. Your key is at home on H. (Figure 6-5 illustrates the problem.) What now?
Figure 6.5. Bastion host scenario
Notice that this problem doesn't arise with telnet or rsh. You'd simply type your password to reach W (insecurely, of [11]
course). For a solution, we turn to SSH agents and agent forwarding.
SSH agent forwarding allows a program running on a remote host, such as B, to access your ssh-agent on H transparently, as if the agent were running on B. Thus, a remote SSH client running on B can now sign and decrypt data using your key on H as shown in Figure 6-6. As a result, you can invoke an SSH session from B to your work machine W, solving the problem.
Figure 6.6. Solution with SSH agent forwarding
page255image21176
page256image384
6.3.5.2 How agent forwarding works
Agent forwarding, like all SSH forwarding (Chapter 9), works "behind the scenes." In this case, an SSH client has its agent requests forwarded across a separate, previously established SSH session, to an agent holding the needed keys, shown in Figure 6-7. The transmission takes place over a secure SSH connection, of course. Let's examine, in detail, the steps that occur.
Figure 6.7. How agent forwarding works
  1. Suppose you're logged onto machine X, and you invoke ssh to establish a remote terminal session on machine Y:
         # On machine X:
         $ ssh Y
    
  2. Assuming that agent forwarding is turned on, the client says to the SSH server, "I would like to request agent forwarding, please," when establishing the connection.
  3. sshd on machine Y checks its configuration to see if it permits agent forwarding. Let's assume that it's enabled.
  4. sshd on machine Y sets up an interprocess communication (IPC) channel local to Y by creating some Unix
page256image9656
domain sockets and setting some environment variables. [Section 6.3.2.1] The resulting IPC mechanism is just like the one ssh-agent sets up. As a result, sshd is now prepared to pose as an SSH agent.
  1. Your SSH session is now established between X and Y.
  2. Next, from machine Y, you run another ssh command to establish an SSH session with a third machine, Z: # On machine Y:
    $ ssh Z
  3. This new ssh client now needs a key to make the connection to Z. It believes there's an agent running on machine Y, because sshd on Y is posing as one. So, the client makes an authentication request over the agent IPC channel.
  4. sshd intercepts the request, masquerading as an agent, and says, "Hello, I'm the agent. What would you like to do?" The process is transparent: the client believes it's talking to an agent.
  5. sshd then forwards the agent-related request back to the original machine, X, over the secure connection between X and Y. The agent on machine X receives the request and accesses your local key, and its response is forwarded back to sshd on machine Y.
  6. sshd on Y passes the response on to the client, and the connection to machine Z proceeds.
Thanks to agent forwarding, you have transparent access from machine Y to any SSH keys on machine X. Thus, any SSH clients on Y can access any hosts permitted by your keys on X. To test this, run this command on machine Y to list your keys:
# On machine Y:
$ ssh-agent -l
You will see all keys that are loaded in your agent on machine X.
It's worth noting that the agent-forwarding relationship is transitive: if you repeat this process, making a chain of SSH connections from machine to machine, then clients on the final host will still have access to your keys on the first host (X). (This assumes, of course, that agent forwarding is permitted by sshd on each intermediate host.)
6.3.5.3 Enabling agent forwarding
Before an SSH client can take advantage of agent forwarding, the feature must be turned on. SSH implementations vary in their default settings of this feature, and of course the system administrator can change it. If necessary, you
can turn it on manually with the configuration keyword ForwardAgent[12] in the client configuration file ~/.ssh/ config, giving a value of yes (the default) or no:
# SSH1, SSH2, OpenSSH
ForwardAgent yes
Likewise, you can use command-line options. In addition to the -o command-line option, which accepts any configuration keyword and its value:
# SSH1, SSH2, OpenSSH
$ ssh -o "ForwardAgent yes" ...

The ssh option -a turns off agent forwarding: # SSH1, SSH2, OpenSSH
$ ssh -a ...
In addition, ssh2 and OpenSSH's ssh accept options to turn on agent forwarding, even though it's on by default: # SSH2 only
$ ssh2 +a ...
# OpenSSH only
$ ssh -A ...
6.3.6 Agent CPU Usage
Before we leave our discussion of agents, we'll make one final note about performance. Agents carry out all cryptographic work that would otherwise be done by SSH clients. This means an agent can accumulate substantial CPU time. In one case we saw, some friends of ours were using SSH1 for a great deal of automation, running hundreds of short-lived SSH sessions in a row. Our friends were quite puzzled to find that the single ssh-agent used by all these processes was eating the lion's share of CPU on that machine.
[5]
This design also fits well with token-based key storage, in which your keys are kept on a smart card carried with you.
Examples are the U.S. government-standard Fortezza card or RSA Security's Keon system. Like agents, smart cards
respond to key-related requests but don't give out keys, so integration with SSH would be straightforward. Though adoption
of tokens has been slow, we believe it will be commonplace in the future.
[6]
Older versions of SSH1 use SSH_AUTHENTICATION_SOCKET instead of SSH_AUTH_SOCK. If this applies to
you, we recommend setting SSH_AUTH_SOCK yourself, for example (in C shell):
[7]
Why can't ssh-agent set its environment variables without all this trickery? Because under Unix, a program can't set
environment variables in its parent shell.
[8]
Actually, you can reconnect to an agent launched in a previous login by modifying your SSH_AUTH_SOCK variable to
point to the old socket, but this is gross.
[9]
X has its own security problems, of course. If someone can connect to your X server, they can monitor all your
keystrokes, including your passphrase. Whether this is an issue in using ssh-askpass depends on your system and security needs.
[10]
Even though this socket is world-writable, only user smith can access it due to the permissions on the parent directory, /
tmp/ssh-smith.
[11]
This key-distribution problem can also be solved with network file-sharing protocols, such as NFS, SMB, or AFP, but
these aren't usually available in the remote-access situation we're discussing.
[12] SSH2 supports the keyword AllowAgentForwarding as a synonym for ForwardAgent