I've started writing something I hope will be an ebook called Take Control of SSH. It's an amazingly long process, so here's an excerpt on private and public key authentication, the most important part of SSH. Note that this was not written as a stand-alone piece, so please excuse the rough edges.

If you'd like to read the rest, please vote for Take Control of SSH.

Part II: Public Key Authentication (Practice)

Private keys are much more secure than passwords (which must be short enough for people to remember and easy to type), but fundamentally they're still large numbers which function as password equivalents. This means that anyone who has your private key can use it to authenticate as you to an SSH server. Files containing private keys (such as ~/.ssh/id_rsa) are therefore as security sensitive as passwords.

On the server side, public keys serve identify users to the SSH server. If someone can manipulate your ~/.ssh/authorized_keys file, they can insert their own key and impersonate you. To protect against this, sshd ignores authorized_keys files with insecure permissions. Unfortunately, sshd doesn't complain about this in a visible way, so mysterious failures of public key authentication are often traced back to inadequate permissions. The rule is that the ~./ssh/authorized_keys file and all the directories that contain it, up to the root of the filesystem, must not be writable by any account except the owner. File system permissions cannot stop a system administrator from accessing the key files (both private and public, although the private keys should be encrypted), of course. Similarly, anyone with access to the machine or unencrypted backups could copy the key files.

Create a Public/Private Keypair

To get started with public key authentication, first generate a new rsa key in Terminal with "ssh-keygen -t rsa". Let ssh-keygen create ~/.ssh/id_rsa, and provide a good password you will remember, because keys cannot be recovered if you forget your password. ssh-keygen will create ~/.ssh if it doesn't already exist, as well as ~/.ssh/id_rsa (your new private key, encrypted with the provided passphrase) and ~/.ssh/id_rsa.pub (your new public key).

pepperbook:~ sample$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/sample/.ssh/id_rsa): 
Created directory '/Users/sample/.ssh'.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /Users/sample/.ssh/id_rsa.
Your public key has been saved in /Users/sample/.ssh/id_rsa.pub.
The key fingerprint is:
ba:d1:0c:43:93:a8:d5:5a:b8:a2:4b:29:ae:34:ee:3f sample@pepperbook.reppep.com
pepperbook:~ sample$ ls -ld .ssh
drwx------   4 sample  sample  136 Apr  6 00:22 .ssh
pepperbook:~ sample$ ls -l .ssh
total 16
-rw-------   1 sample  sample  736 Apr  6 00:22 id_rsa
-rw-r--r--   1 sample  sample  618 Apr  6 00:22 id_rsa.pub
pepperbook:~ sample$ 

To use the new public key for logging into this account on this machine, copy id_rsa.pub to authorized_keys: "cp ~/.ssh/id_rsa.pub ~/.ssh/authorized_keys". Test it with "ssh 127.0.0.1". First, you will be asked to accept the public key for 127.0.0.1 (recall that keys are used to identify servers, as well as users); it's then automatically copied into ~/.ssh/known_hosts. Second, you will be asked for the passphrase for your private key, instead of the account password for the remote system (note that the passphrase might be the same same as the account password). Finally, you will get a UNIX shell dmg pt on the "remote" system once the ssh session is established. Log out to terminate the ssh session.

Warning: Some Mac OS X versions suffer confusion between the current IPv4 Internet Protocol and the newer IPv6 protocol; IPv6 enables more computers to connect to the Internet but is not yet in wide use. To avoid these issues, use 127.0.0.1 in commands instead of localhost. They should be equivalent, but are not (at least in early versions of Mac OS X 10.4 "Tiger", and I believe in "Panther" as well). Fortunately, Apple fixed this by 10.4.8, but it caused a lot of confusion before that, and still does for people using older versions.

pepperbook:~ sample$ cp ~/.ssh/id_rsa.pub ~/.ssh/authorized_keys
pepperbook:~ sample$ ssh 127.0.0.1
The authenticity of host '127.0.0.1 (127.0.0.1)' can't be established.
RSA key fingerprint is 8a:d3:22:82:f5:2b:88:f0:20:1b:72:bd:aa:30:e0:e2.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '127.0.0.1' (RSA) to the list of known hosts.
Enter passphrase for key '/Users/sample/.ssh/id_rsa': 
Welcome to Darwin!
pepperbook:~ sample$ ls -l .ssh
total 32
-rw-r--r--   1 sample  sample  618 Apr  6 00:23 authorized_keys
-rw-------   1 sample  sample  736 Apr  6 00:22 id_rsa
-rw-r--r--   1 sample  sample  618 Apr  6 00:22 id_rsa.pub
-rw-r--r--   1 sample  sample  219 Apr  6 00:23 known_hosts
pepperbook:~ sample$ exit
logout
Connection to 127.0.0.1 closed.
pepperbook:~ sample$ 

Now that you've confirmed your public and private keys work, it's time to copy authorized_keys to some other SSH servers. First, make sure there's a suitable ~/.ssh directory, next send the file, and then login using the key to confirm it works.

pepperbook:~ sample$ ssh www "mkdir -p .ssh ; chmod 700 .ssh"
The authenticity of host 'www (66.92.104.200)' can't be established.
RSA key fingerprint is 53:66:e9:b5:92:e1:5f:d9:71:fa:87:7b:35:99:f2:d3.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'www,66.92.104.200' (RSA) to the list of known hosts.
Password:
pepperbook:~ sample$ scp ~/.ssh/authorized_keys www:.ssh/
Password:
authorized_keys                               100%  618     0.6KB/s   00:00    
pepperbook:~ sample$ ssh www
Enter passphrase for key '/Users/sample/.ssh/id_rsa': 
Welcome to Darwin!
www:~ sample$ exit
logout
Connection to www closed.
pepperbook:~ sample$ 

Once that's tested, you're ready to copy authorized_keys to more computers. You can also put the id_rsa file on other machines, but be careful with this file! Don't put it on any machines you don't completely trust.

Remember that you can use multiple private and public keys. The id_rsa and id_rsa.pub filenames are just defaults -- keys don't need to have any special names, although agents tend to assume that the public key filename is the private key filename plus ".pub", and may get confused otherwise.

SSH Authentication Agents

So far you've replaced password authentication with public key authentication. This is valuable, because it means someone who controls an sshd server which you log into will not get a chance to steal your password. The way SSH handles plain password authentication (against the built-in UNIX or Windows account databases) is by taking the password typed in and sending it through the ssh connection; at the other end the username and password are decrypted and checked against the remote account's password on file. Both the local ssh program you run and the remote sshd program work with the unencrypted password. There have been major incidents in the past where a popular site (such as SourceForge.net) was broken into, and the crackers recorded all the usernames and passwords used to ssh into SourceForge until people noticed something was wrong. The crackers then used those username/password pairs to log into other sites. It was a colossal mess. Public keys avoid this risk, because the remote system only gets the public key. It doesn't need the private key, and cannot determine it from the public key.

Warning: It is possible to use unencrypted private keys, but this is generally a very bad idea, because anyone who gains access to the private key file (trivial, with physical access to the computer) then controls that identity, and can impersonate or eavesdrop on the owner. About the only time unencrypted private keys are appropriate is for automated tasks, run when nobody is available, if using an agent is not feasible. Such keys should be restricted as much as possible in authorized_keys, and the accounts they access should be restricted as much as possible as well.

Key agents make use of SSH much more efficient, at the cost of a small reduction in security. Agents read encrypted key files; the agent keeps the unencrypted key in memory (never in a file on disk), making it immediately accessible without further decryption or password entry. This makes use SSH dramatically more efficient. Without an agent, connecting to 5 systems 3 times each would require typing one's password correctly 15 times; with a loaded agent, all the connections are automatic. As an example, consider checking a file on several machines. An SSH agent automatically provides the private key for each connection, making large-scale operations quick and convenient.

The risk in using an agent is that anyone who can control the agent or its socket (such as a root user) can use it to authenticate, although the private keys themselves are not available directly from the agent. This makes agents (and agent forwarding) unsuitable for use on untrusted machines.

SSHKeychain

On the Mac, SSHKeychain is an excellent SSH agent. It can automatically load keys when needed, forgetting them when the system sleeps. SSHKeychain is accessible through the Dock or the SSHKeychain menu, and integrates with Apple's Keychain, optionally automatically loading and unloading SSH keys as the Apple Keychain unlocks and locks, and it works with other SSH-supporting programs such as BBEdit.

Once you've created and tested your private/public keypair, there are several simple steps to activate SSHKeychain.

  1. Open SSHKeychain.dmg to mount it.
  2. Install SSHKeychain (I just dragged SSHKeychain.app into /Applications).
  3. Unmount SSHKeychain.dmg.
  4. Open /Applications/SSHKeychain.app.
  5. Configure SSHKeychain's preferences (accessible from the new SSHKeychain menu on the right side of the menu bar); the critical parts are to enable "Manage (and modify) global environment variables", and configure specify your private keys if you used nonstandard filenames.
  6. Use Agent > Add All Keys from SSHKeychain's menu to confirm it can load your encrypted keys, and that now you can ssh into systems without entering a password. If you check "Add passphrase to the Apple keychain", SSHKeychain will no longer prompt you for the key's passphrase, although it may prompt to unlock your keychain to retrieve the key's passphrase.
  7. Set SSHKeychain to run automatically (I control-clicked on its Dock icon and set Open at Login).
  8. From now on, whenever you log into Mac OS X, SSH programs will use SSHKeychain for key management.

The SSHKeychain menu shows either of two icons. When it has one or more keys loaded, it shows a keyring with three keys. When it doesn't hold any keys, the ring is missing, but the keys are still shown.

There are a couple bugs in Mac OS X 10.4.x's authentication and the Apple Keychain. In theory, unlocking the screensaver should unlock the user's default keychain (if it uses the account's login password, which is Apple's default configuration), but this doesn't work properly. Additionally, while a keychain is unlocked, the system does not need to prompt the user to "unlock" that keychain again until it's re-locked, but this is also broken -- Mac OS X prompts to "unlock" keychains that are already unlocked.

In reality, if the system is running with the screensaver and Keychain locked, applications tend to bring up their own Keychain dialogs behind it. Unlocking the screensaver or the Keychain does not dismiss these dialogs. The upshot is that, with SSHKeychain set to lock the Apple Keychain on screen activation (its default behavior), unlocking the screensaver may reveal multiple Keychain dialog boxes which must each be addressed individually. With .Mac synching set to Auto, I often saw three authentication dialogs when I unlocked the screensaver.

As a partial workaround, open Keychain Access, create a new keychain (I called mine "lowsec"), select the new keychain, and use "Edit > Change Settings for Keychain "lowsec" to disable locking entirely for this keychain. Then go back to your main (login) keychain, and move the ".Mac password" item (its name will be your .Mac username) from the main login keychain into the new one. Additionally, enable "Use custom security settings" in SSHKeychain, and set "On screensaver:" to "No action". With this configuration, the lowsec keychain will be unlocked once at login time and then available until logout or reboot. For higher security I set my main login keychain to automatically lock after 30 minutes of inactivity. Ideally, both Keychain Access and SSHKeychain would allow locking specific keychains when the screen locks, leaving other keychains unlocked, but this is not currently implemented.

Key Restrictions and Forced Commands

It can be very useful to provide restricted access to an account. In SSH, this is possible by adding restrictions to specific keys in authorized_keys. Some of the possible restrictions include disabling interactive login; restricting agent, X11, or port forwarding (tunneling); and specifying a "forced command", which is always run when the associated key is used, regardless of what is requested by the ssh client. These restrictions are particularly useful for non-interactive tasks such as backup scripts. Such tasks may require SSH connectivity with unencrypted private keys, which should not provide unrestricted ssh access.

Note that multiple keys may provide access to the same account. This is handy for people sharing single accounts, or for using special-purpose keys with different forced commands. For more information on key restrictions and forced commands, see the "AUTHORIZED_KEYS FILE FORMAT" section of the sshd manual page.

Public keys can be further restricted by allowing access to carefully circumscribed accounts; unencrypted public keys which give access to "real" user or root accounts should be avoided as much as possible.

Raising the Bar: Requiring Stronger Security

There are several ways to protect a server against ssh-based attacks, including firewalls, TCP Wrapper, not creating or distributing UNIX passwords (forcing ssh public key authentication), and disabling password access for all accounts or the root account. To read about using firewalls and TCP Wrapper with ssh, please vote for Take Control of SSH.

UNIX passwords present several problems for administrators. What legitimate users can remember and type (generally considered to be 8 letters and numbers) is a small enough range of possibilities for attackers to try all possibilities. "Account lockout" is a feature of some systems (including Mac OS X Server) to disable accounts after several failed guesses -- which often identifies an attack. Unfortunately, this means legitimate users get blocked when their accounts are attacked, and locking legitimate users out of their own accounts is a successful attack (although not as serious as gaining illicit access). System administrators would often prefer to avoid this by not allowing password access at all. On Mac OS X, it is difficult to set up an account without a password; it's easier to create a long random password (12+ characters -- Keychain Access can do this for you), and never write it down or give it to the account user, requiring public key authentication or some other high security authentication (such as smart cards) instead. On other systems, it's easy to simply not set UNIX passwords for accounts (although there are complications).

OpenSSH has its own features to help force users to use public keys, blocking password guessing over the Internet. If you have enabled the root account (which is technically accomplished by setting a password for it), you should definitely set "PermitRootLogin without-password" in /etc/sshd_config; this will prevent people from breaking into the root account by guessing its password -- an amazingly common and disturbingly successful Internet attack. Even better, set "PasswordAuthentication no" to prevent sshd from accepting passwords for any account -- thus requiring keys for ssh access. For more about how to configure sshd and ssh, and enhance security with ssh, please vote for Take Control of SSH.