Alex headshot

AlBlue’s Blog

Macs, Modularity and More

SSH logins using keys

2005

Key-based login allows for a more secure way of connecting between different machines than password-based authentication. The key is much stronger than the password is (so there is less chance of it being cracked) and it can be distributed across multiple machines, even if the passwords on all systems are different. Furthermore, keys can be created and disposed of at will; so if there's a suspected case of key loss, it's trivial to disable that key and replace it with another.

This HowTo assumes that you're using Open SSH, which is the one that comes by default with Mac OS X. However, it's the most prevelant one on Linux systems as well; and furthermore, they're compatible. It also assumes that you've got SSH working already for password-based authentication; it doesn't cover how to set up SSH in the first place. (If you're using Mac OS X, then it's easy to start it with System Preferences; go to Sharing and select Remote Logins.)

A key is based on two parts; a public key (which is easily readable as plain text and does not need protection) and a private key (which should not be readable by anyone else but the user). Data encrypted with the private key can be read with the public key, and vice-versa. Used in SSH, the public key (usually called id_dsa.pub or id_rsa.pub for SSH2, or identity.pub for SSH1) is copied onto whichever hosts you want to log on to, and the private key (called id_dsa or id_rsa for SSH2, or identity for SSH1) is only on the client machine.

There are actually multiple protocols that SSH uses for keys; RSA and DSA are the two main variants (hence the two keys). DSA relies on random number generation and so RSA is probably the preferred option; at least, according to the PuTTy FAQ. In practice, they are used in the same way so this isn't covered further here.

Setting up key-based SSH involves:

  • Generating the private/public keys
  • Installing the public keys on the remote hosts
  • Testing the SSH connection

Generating the key

OpenSSH has a command ssh-keygen that does all of the hard work for you. About the only decision that you need to make is how long to make the key (2048 is generally considered sufficient by today's computer standards) and what flavour (RSA/DSA).

ssh-keygen -t rsa -b 2048

By default, it will suggest ~/.ssh/id_rsa and ~/.ssh/id_rsa.pub, but this can be changed if required (see below).

Installing the key on remote machines

On the remote machine, copy and paste the contents of the id_rsa.pub key into the ~/.ssh/authorized_keys file (creating it if it doesn't exist). For example, put the following in your file:

ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAmLmwkzQDjEOW1Rj3TP5NldVDqUODVH9xuYrkeaSkxtdP
J8D9Hz+XAWnGAXdaIkCVOw2YEfHKWSo6befgNxiS+AKS+S+wM/bJpc4qOLe5ozFjZPNRHcw5O8WkgP5g
/wg2BOvxBqSKpsSzvi4rYVRLtl7TLVMyajhELiJ9GqT8f25gr3jFmtuQQIkRES1aC4oL2tHsn529POfP
1lPhh5tb2FbqEpm9L3779ljjkSX7mD4zza3zUckkuAIb5R7KSOrvPnJaEU903hrI0tx5omGyDy+h/2D1
h0aqHanPcU9Ml91ZpMKdpa0+FeVgs2M3LHYTNnvZ76ScV2VtUQwm3YEvjw== alex@smartarse.org

Note that the contents of this file should be on a single line; and that the file must not be writable by anyone other than yourself (chmod 644 ~/.ssh/authorized_keys).

You should then be able to SSH from one machine into another. You don't even have to SSH in as the same user; for example, I can SSH into a remote machine as user 'alblue' as long as I specify it on the command line (ssh alblue@machine or ssh -l alblue machine) and my public key is in the machine:~alblue/.ssh/authorized_keys file.

Troubleshooting

The most common problem with a key-based authentication is that the file permissions are wrong. SSH is very fussy (with good reason!) about the file permissions; essentially, private files should not be readable/writable by anyone other than yourself, and public files should not be writable by anyone else. The permissions should be:

~/.sshdrwxr-xr-xchmod go-w ~/.ssh
~/.ssh/id_rsa-rw-------chmod go= ~/.ssh/id_rsa
~/.ssh/id_rsa.pub-rw-r--r--chmod go=r ~/.ssh/id_rsa.pub
~/.ssh/config-rw-r--r--chmod go=r ~/.ssh/config

If that doesn't work, then see what SSH is doing under the covers when it is logging in. You should see it discussing the key-based authentication when logging in with verbose mode (ssh -v alblue@machine):

debug1: Next authentication method: publickey
debug1: Offering public key: /home/alblue/.ssh/id_rsa
debug1: Server accepts key: pkalg ssh-rsa blen 149
debug1: read PEM private key done: type RSA
debug1: Authentication succeeded (publickey).

If you don't see this message (or it falls back to password prompt) then the public key isn't working. The server should come back with a message like:

debug1: Authentications that can continue: gssapi-with-mic,publickey,gssapi,password,keyboard-interactive

which lists which authentications that the server and client agree on. If you don't see publickey in the list, it may well be because the client or server don't want to exchange that key (or those versions of keys). The server can be configured to not allow public key authentication (the default is PubkeyAuthentication yes in /etc/sshd_config), as can the client /etc/ssh_config and ~/.ssh/config). They both have to be yes for authentication to use public key authentication; but since that's the default, if the files don't list it, then it should be OK.

If it still doesn't work, check to see if the line in the authorized_keys file is all on one line, and that it matches the id_rsa.pub contents. If you really want to make sure, copy the id_rsa.pub file to the remote machine and rename it as authorized_keys.

It's also worth checking that the versions of the client are compatible. They should be using OpenSSH 2.0+; although there is an SSH1 version, no-one really uses it any more. In fact, the server (and client) can refuse to talk to older versions. There's a Protocol directive that lists what's available; 2 means only use SSH2, whilst 2,1 means try 2, and fallback to 1 if not available. If you've got a server that only speaks version 1, and the client is configured to only use version 2, then they can't talk to each other. It's probably a good idea not to talk to version 1 servers though; in any case, the id_dsa and id_rsa identity files are only used by version 2.

Security

It's possible to associate a passphrase with an SSH key, and it's strongly recommended that you use this. (It's called a passphrase instead of a password because it's expected that you'll use many words, not just a single one. So instead of bananas, you could have bananas are my favourite fruit which is somewhat stronger, cryptographically speaking.) Without a passprhase, the private key becomes essentially an unencrypted password that others can easily steal and use (though not to change your current password). If you don't specify a password, then you essentially get passwordless logon to remote machines, and if you trust your file system enough, then you can effectively have a single-sign-on architecture.

There are alternatives that are more secure, however. One way is to put the SSH key on a removable device, such as a USB keyring, and carry it with you at all times. That way, it is only connected to the computer when you need to authenticate. However, most auto-mounting systems may mount it read-write, or globally readable, which as noted above is a big security threat. Alternatively, you could mount an encrypted filesystem (in Mac OS X, using an encrypted DMG - disk image - is an excellent way of achieving this).

The other way of achieving this is to use an ssh key agent. This is a program that loads the key into memory (after unlocking it with your passphrase). Programs can then borrow the key from the key store, and use them as if it were a key with no passphrase. When the agent is stopped, the in-memory unencrypted private key is lost, and programs will no longer be able to use it.

Using the ssh-agent is relatively easy; it just needs to be run in the background, often started with the login scripts. It sets up environment variables, so can either be used to launch programs with the environment set up (e.g. ssh-agent xterm) or in a running shell (eval `ssh-agent -s` for bourne shells such as BASH). You can even have it kicked off with a .login or .bash_profile script.

Once SSH agent is running (and the variables ${SSH_AUTH_SOCK} and ${SSH_AGENT_PID} are available) then keys can be added to the agent using ssh-add command. Run without arguments, it adds the default keys including id_rsa and id_dsa into the agent. If they are locked with a passphrase, it will ask for them; in an X environment, it uses the variable ${SSH_ASKPASS} to run a command to obtain the passphrase.

Of course, there's no reason these can't be combined. You can have the ssh keys on an encrypted, removable device and further protected with a passphrase.

Multiple keys

SSH doesn't restrict you to these two identities; they are just the default names for the files. When generating a key, you can specify which name you want to use; so if you are dealing with multiple hosts, you might want to have a key for each (in case one gets compromised). You might want to have id_rsa.example.org and id_rsa.machine, for example, and copy the appropriate public keys to the respective authorized_keys file.

Generating multiple keys is easy; just give it a different file name when you generate it (either interactively, or using the -f keyname argument to ssh-keygen). Of course, you can encrypt them with the same passphrase or use a different one for each.

Specifying that you want to use a different key for an SSH login is fairly simple as well. Using ssh -i ~/.ssh/id_rsa.example.org alblue@example.org will try the key given on the command line. You can give multiple keys on the command line if you have several identities, so you can have ssh -i ~/.ssh/id_rsa.BruceWayne -i ~/.ssh/id_rsa.Batman example.org.

However, if you have multiple keys and don't want to type them in each type on the command line, you can set them up in the client-configuration file (which is /etc/ssh_config for global configuration and ~/.ssh/config for personal ones). You can add a directive IdentityFile ~/.ssh/id_rsa.BruceWayne and another IdentityFile ~/.ssh/id_rsa.Batman (on seperate lines) which will have the same effect as the above command. If you're creating the ssh_config file, you should ensure that it is not world- or group- writable.

Lastly, if you want to ensure that the right key is used for the right host, it's possible to group SSH key definitions by host. So you can have:

Host WayneManor
IdentityFile ~/.ssh/id_rsa.BruceWayne

Host Batcave
IdentityFile ~/.ssh/id_rsa.Batman

in which case, Bruce isn't allowed into Batcave and Batman isn't allowed into WayneManor. If you don't specify any Host directives, it's as if Host * appears at the top of the file and is checked into any host.

Interoperability

Given that SSH is open, it's relatively easy for SSH clients and SSH servers to speak to each other. However, the format of the public/private keys is often subtly different. The types of format are:

  • Open SSH format. This is the one used by most Linux-systems, Mac OS X, and other free Unix-based systems. This is the one to try first; if you're not sure what the server is running, telnet to it on port 22:

    $ telnet localhost 22
    Trying 127.0.0.1...
    Connected to localhost.
    Escape character is '^]'.
    SSH-2.0-OpenSSH_4.1p1
    
  • SSH2-compatible. Use ssh-keygen -i to convert to OpenSSH format.
  • SECSH Public Key File Format. Use ssh-keygen -i to convert to OpenSSH format.
  • Putty Key format (PPK). PuttyGen can be used to generate keys on Windows systems, along with Pageant as an equivalent to ssh-agent. It can print out an OpenSSH compatible file for pasting into an authorized_keys file.