Using SSH Public Key Authentication

why do I want to use public key authentication?

Passwords aren't the most secure things in the world. Even if a user picks a 'secure' password that's stronger than their dog's name, the password is still susceptible to a brute-force attack. Brute force attacks via ssh against user passwords are quite common on the Internet and several prevalent worms and zombies perform automated attacks incessantly against any internet-connected host. Even a secure password is at risk to these attacks, done by hand or by worm. Allowing password access to a system with many users is an invitation for a security breach.

Additionally, if you've got accounts on a large number of hosts it's tempting to reuse the same password on more than one host to reduce the number of passwords that your fingers have to memorize. Each shared password on a remote system puts you more at risk of a brute force attack on that host's password file, and means that if one host is compromised that all your other hosts sharing that same password are significantly less safe. Heck, you could accidently "erp" your password into an IRC channel by mistake some day and then spend the rest of the afternoon finding all the systems where you've re-used that password so that you can change it before anyone figures it out. It's not fun (or so I've heard!)

Thankfully, there's a solution! OpenSSH has a robust and well-tested public key authentication system built right in. When set up properly, it's not only more secure than using passwords but it's also a lot easier to use. How often does that happen?

what does public key mean, exactly?

Public-key authentication (or PKI -- a public key infrastructure) is an authentication method that relies on a generated public/private keypair. With PKI a special "key" is generated which has a very useful property: Anyone who can read the public half of the key is able encrypt data which can then only be read by a person who has access to the private half of the key. In this way, having access to the public half of a key allows you to send secret information to anyone with the private half, and to also verify that a person does in fact have access to the private half. It's easy to see how this technique could be used to authenticate.

As a user, you can generate a keypair and then place the public half of the key on a remote system. That remote system is then able to authenticate you, or prove that you are really you, and allow you to login just by having you demonstrate that you have access to the private half of the keypair. This is done at the protocol level inside SSH and happens automatically.

It does, however, mean that you need to protect the privacy of the private key. On a shared system where you do not have root this can be accomplished by encrypting the private key with a passphrase, which functions similarly to a password. Before SSH can read your private key in order to perform the public key authentication you'll be asked to supply the passphrase so that the private key can be decrypted. On more secure systems (like a machine where you are the only user, or a machine at your home where no strangers will have physical access) you can simplify this process either by creating an unencrypted private key (with no passphrase) or by entering your passphrase once and then caching the key in memory for the duration of your time at the computer. OpenSSH contains a tool called ssh-agent which simplifies this process.

how do I set this up?

The first thing you need to do is generate a keypair using the ssh-keygen tool which is part of OpenSSH. Windows users who use PuTTY can use the related putty-keygen.exe program in the same manner. SecureCRT has a keypair generator built in as well. Here's a log of creating a keypair:

luser@localhost:~$ ssh-keygen -t dsa
Generating public/private dsa key pair.
Enter file in which to save the key (/home/luser/.ssh/id_dsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/luser/.ssh/id_dsa.
Your public key has been saved in /home/luser/.ssh/id_dsa.pub.
The key fingerprint is:
f3:77:56:58:a8:bb:08:59:67:15:2c:0e:1d:d0:40:a3 luser@example.com
luser@localhost:~$

Now you have the two halves that comprise a keypair. The file id_dsa is the private half and the file id_dsa.pub is the public half. You'll want to make sure that only you can read the private files. This is critical, because OpenSSH will refuse to work with key files which are world or group readable. Verify thusly:

luser@localhost:~$ chmod 700 $HOME/.ssh
luser@localhost:~$ chmod 600 $HOME/.ssh/id_dsa*
luser@localhost:~$ ls -la .ssh
total 10
drwx------   2 luser  luser   512 Nov 15 16:12 .
drwx-----x  12 luser  luser  1024 Jun 24  2004 ..
-rw-------   1 luser  luser  1264 Nov 15 16:12 id_dsa
-rw-------   1 luser  luser  1123 Nov 15 16:12 id_dsa.pub
luser@localhost:~$

So now we're half-way there. You've got your keypair but we need to place the public half of the key on a remote machine. If you look at the id_dsa.pub file in your favorite editor you'll see that it's just a big block of numbers and letters. This means that it's easy enough to cut and paste, which can be simpler than trying to send it to a remote machine using a file transfer protocol like sftp or scp. Usually it's easiest to just open up another terminal window and paste it to the other host. In either case, you'll want to take the contents of id_dsa.pub and stuff them into a file named $HOME/.ssh/authorized_keys on a remote host. As before, make sure that the .ssh directory is only readable by you (chmod 700) and that the authorized_keys file is as well (chmod 600). You should end up with something like this:

luser@remotehost:~$ls -la .ssh
total 16
drwx------  2 luser  luser   512 Jul 11 19:01 .
drwx--x--x  7 luser  luser  1024 Nov 14 18:01 ..
-rw-------  1 luser  luser  1123 Nov 15 16:21 authorized_keys

So now you're all set to log in using public key authentication. Instead of being asked for your password on remotehost, you'll instead be challenged for the passphrase you've used to encrypt your local, private key. Give it a shot:

luser@localhost:~$ssh remotehost.example.com
Enter passphrase for key '/home/luser/.ssh/id_dsa':
Last login: Tue Nov 15 16:20:50 2005 from localhost.example.com

luser@remotehost:~$

That's all there is to it! In reality, the user account on the remotehost side doesn't even need to have a password. With no password (which is not the same as a NULL password) it's not even possible to log in to a remote system without the private key. This makes the account totally immune to a brute-force password attack. There are other benefits as well, though. You can actually have more than one public key placed into the authorized_keys file on a remote host. This means that you can generate more than one private key if you routinely ssh from multiple locations. For example, you might create separate keypairs for your computer at home and the computer at your office. Remote hosts might have just one or both of those keys in the authorized_keys file. You might create a third keypair for your work laptop.

Why would you want multiple keys? Well imagine if your laptop gets lost or stolen. You can simply log in to remote hosts and remove that specific public key from your authorized_keys files and the laptop will be rendered incapable of logging in to the remote hosts, even if someone manages to crack or guess the passphrase which protects the private key. In this way, it's simple to "revoke" a keypair.

You'll also find that you're no longer tempted to share a single password with multiple remote hosts, which yields a dramatic increase in your overall security because if an account is compromised on one remote host it cannot be linked to any of your other accounts on other hosts.

advanced usage: ssh agent

You can actually take public key authentication one step further and make your life even easier by caching your private key locally. OpenSSH contains a tool called "ssh-agent" which can be used to accomplish this. This provides a compromise between the easy use of a passphrase-less private key and the security of having your private key encrypted in case someone gains unauthorized access to the actual file. You run ssh-agent on the local machine where you're sitting, and use it to cache the private key for repeated use.

The key to using ssh-agent is launching it as the parent process to your user session itself. Many Unixes are already doing this for you, and if you check your process list you may find that ssh-agent is already running to provide this capability. Windows users using PuTTY can use PuTTY's "pagaent.exe" tool and Mac OS X users can download a third party tool like SSHKeyChain or use the command-line ssh-agent as they would with any other Unix. [Edit: OS X 10.5 "Leopard" has ssh agent support built in. You don't need to install any additional software if you're running Leopard] If your operating system isn't running ssh-agent for you, you'll want to invoke it as part of your login process. For example, you might change your gdm or xdm login to invoke "ssh-agent gnome-session" instead of just "gnome-session" so that ssh-agent can sit on top of your session. The key is making sure that the SSH_AUTH_SOCK and SSH_AGENT_PID environment variables are global to your sessions on the host. With ssh-agent resident, you can run it manually and store your private key inside with ssh-add:

luser@localhost:~$ ssh-add ~/.ssh/id_dsa
Need passphrase for /home/luser/.ssh/id_dsa (luser@example.com).
Enter passphrase:
luser@localhost:~$

After having done that, you should now be able to ssh to any remote host without being challenged for your passphrase. OpenSSH will talk to the local ssh-agent daemon and retrieve the private key from it automatically. That authentication can even be (optionally) tunneled to the remote host which means if you ssh hop from that remote host to yet a third host the authentication can pass back through to your local ssh-agent and still not require typing a password or passphrase.

Be aware that using ssh-agent puts your private key at risk to anyone who has root access on the machine where ssh-agent is. It's not wise to use ssh-agent on a machine where you aren't or don't trust the administrator. It's also not wise to allow OpenSSH to forward the authentication on to a machine where you aren't or don't trust the administrator. You can control whether or not this agent forwarding takes place using the command line:

     -A      Enables forwarding of the authentication agent connection.  This
             can also be specified on a per-host basis in a configuration
             file.

             Agent forwarding should be enabled with caution.  Users with the
             ability to bypass file permissions on the remote host (for the
             agent's Unix-domain socket) can access the local agent through
             the forwarded connection.  An attacker cannot obtain key material
             from the agent, however they can perform operations on the keys
             that enable them to authenticate using the identities loaded into
             the agent.

     -a      Disables forwarding of the authentication agent connection.

or by defining it the $HOME/.ssh/config file like this:

#
# .ssh/config file for luser@localhost
#

# Forward X11 and Auth to slacker.com hosts, they can be trusted
Host *.slacker.com
ForwardAgent              yes
ForwardX11                yes

# Forward X11 and Auth to my trusted example.com hosts
Host *.example.com
ForwardAgent              yes
ForwardX11                yes

# All hosts which haven't been matched above, paranoid settings
Host *
ForwardAgent              no
ForwardX11                no
Compression               yes
KeepAlive                 yes

That's it!

Once you've grown accustomed to using public key authentication you'll wonder how you ever managed to live without it. It's really that remarkable. Not only is it more secure but it makes using SSH much simpler and more powerful. Enjoy!


© Copyright 1995-2014 David C. McNett. All Rights Reserved.