Securing Keyless SSH Logins

Introduction

In this guide, I will set up secure, keyless SSH access from a test
server, server.example.com to a client client.example.com.

I type “secure” since ordinary keyless SSH setups do not involve
entering a password for the SSH key. It is easy to see how this is
useful for cron jobs, for example, which might need access to client
machines. This also means that if the server is compromised, it is
trivially easy to gain access to its keyless clients.

This situation can be improved greatly with strong passwords,
ssh-agent and its delightful front-end,
keychain.

Pre-flight - Pertinent folders and permissions

Explained later

Commands

Files and Folders

This is Ridiculously Crucial

Generating SSH keys for keyless access

Generating default keys

This is simple. For typical ‘keyless’ access, you would just hit Enter
when asked for a password. Not now! Choose a nice, secure password for
your default key and type it in twice.

ssh-keygen -t rsa  
Generating public/private rsa key pair.  
Enter file in which to save the key (/var/user/.ssh/id_rsa):  
Enter passphrase (empty for no passphrase):   
Enter same passphrase again: 

You should now have two files in .ssh in your home folder. These are
id_rsa, your private key, and id_rsa.pub, your public key. Do not
share your private key.

Copy the Public key to the Client

On CentOS, this is very simple:

ssh-copy-id -i ~/.ssh/id-rsa user@client.example.com

Note: Don’t worry; the command above doesn’t copy the private key of
the identity pair. Since the command is a bash script, you can verify
this yourself.

On Mac OS X, you do it the traditional way (or you can port the
ssh-copy-id script over from CentOS):

scp ~/.ssh/id_rsa.pub user@client.example.com:/var/user/.ssh/server-key.pub  
ssh user@client.example.com  
Password: (Enter Password)  
cat ~/.ssh/server-key.pub >> ~/.ssh/authorized_keys  
rm ~/.ssh/server-key.pub

Verify Permissions

OpenSSH is picky like that.

Test the Connection

Assuming you’re logged in as user on the server:

ssh client.example.com  
Enter passphrase for /user/.ssh/id_rsa:

Enter the password to your SSH key, and you should be in! Yay!

Back to Square One? Not with ssh-agent!

Some background

So here’s the deal: You might think this is pointless since you’ll have
to enter a password anyway (for the key in this case.) However,
ssh-agent caches private keys (and, in our case, login information to
client machines) in memory. From the horse’s
mouth
:

ssh-agent is a program to hold private keys used for public key
authentication (RSA, DSA).  The idea is that ssh-agent is started in the
beginning of an X-session or a login session, and all other windows or
programs are started as clients to the ssh-agent program.  Through use of
environment variables the agent can be located and automatically used for
authentication when logging in to other machines using ssh(1).

Although there exist many scripting examples which initialize and use
ssh-agent, we’ll make that easier with the amazing
Keychain
. Why I like it is purely
because it maintains a single ssh-agent process per system, rather
than per session.

Installation

On CentOS, just issue yum install keychain after installing DAG’s
RPMForge repo (see Resources on this page). On OS X, check out the
download page
.

Using Keychain

Remember that I’m trying to log in as user on both server and client.
I’ll add this to .bashrc on CentOS, or .profile on OS X on the
client machine’s user home folder:

eval `keychain --eval --agents ssh id_rsa`

Now log out and log back into the server. You should see this:

 * keychain 2.6.10 ~ http://www.funtoo.org
 * Found existing ssh-agent: 274  
 * ssh-agent: All identities removed.  
 * Adding  1 ssh key(s): /var/user/.ssh/id_rsa  
Enter passphrase for /var/user/.ssh/id_rsa:   
 * ssh-add: Identities added: /var/user/.ssh/id_rsa

You should have full keyless access to the client after entering the
password to the private key at this point. You may now have a drink.

Not so fast

Remember that Keychain allows ssh-agent to cache private keys per
system. This means that the next time you log into the server,
Keychain will not ask you for the private key’s password since it’s
already cached it in memory. So if you entered the user password to the
server, here’s what you get the second time you log in. For a
compromised server, this may be as good as a password-less login.

user@server.example.com's password:   
Last login: Sun May  2 23:53:38 2010 from 173-30-221-179.client.mchsi.com  
  
KeyChain 2.6.8; http://www.gentoo.org/proj/en/keychain/
Copyright 2002-2004 Gentoo Foundation; Distributed under the GPL  
  
 * Found existing ssh-agent (17986)  
 * Known ssh key: /user/.ssh/rsync-key

Securing Keychain Further

How do you fix the situation? Simple!

  1. You assume that a user is an intruder until s/he has
    proven otherwise.
  2. If the user has not entered the correct passphrase for a private
    key, flush the key from memory

This works, because keys are cleared by ssh-agent on log out, not
log in
. Here’s how you’d edit .bash_profile or .profile:

eval `keychain --clear --eval --agents ssh id_rsa`

At the next login, Keychain will ask you the passphrase for your
keypair. Try entering a bad one a few times and logging into the client
machine. If you’ve set this up right, you’ll be asked for a password to
the machine!

Working with Multiple Identities

In the example above, we’ve used the server’s default SSH identity.
id_dsa/.pub and id_rsa/.pub are the keypair for this identity. It
may not be preferable to use this one single identity for multiple
clients. You’re able to generate other identities using ssh-keygen, by
typing in the name of the keypair when it asks you for one:

ssh-keygen -t rsa  
Generating public/private rsa key pair.  
Enter file in which to save the key (/var/user/.ssh/id_rsa): sample-ssh-key
Enter passphrase (empty for no passphrase):   
Enter same passphrase again: 

Using the key for SSH

When you use the id_dsa/.pub or id_rsa/.pub keypairs, you don’t need
to specify the -i (identity) switch with the SSH command since they’re
default keys. This is how you’d use another identity to SSH in:

ssh -i ~/.ssh/sample-ssh-key client2.example.com

Note that you used the private key in the example above! This
command will ask you for the password to the sample-ssh-key private
key, assuming you’ve copied over the public key to authorized_keys on
the client (as describe in earlier sections.)

Using Keychain to Store Multiple Identities

Simple! For each secure identity you create, just add its appropriate
line to .bash_profile or .profile and ask Keychain to store this in
memory. For the example above:

eval `keychain --clear --eval --agents ssh  `sample-ssh-key``

The next time you log in, you’ll be asked the passphrases to each
private key
you’ve defined via Keychain. For the id_rsa and
sample-ssh-key keypairs above, here’s what I see:

 * keychain 2.6.10 ~ http://www.funtoo.org
 * Found existing ssh-agent: 274  
 * ssh-agent: All identities removed.  
 * Adding  1 ssh key(s): /var/user/.ssh/id_rsa  
Enter passphrase for /var/user/.ssh/id_rsa:   
 * ssh-add: Identities added: /var/user/.ssh/id_rsa  

 * keychain 2.6.10 ~ http://www.funtoo.org
 * Found existing ssh-agent: 274  
 * ssh-agent: All identities removed.  
 * Adding  1 ssh key(s): /var/user/.ssh/sample-ssh-key  
Enter passphrase for /var/user/.ssh/sample-ssh-key:   
 * ssh-add: Identities added: /var/user/.ssh/sample-ssh-key

All the keyless logins you’ve set up with multiple, passworded keys will
work now. All cron jobs will continue chugging along. Intruders won’t
have direct access to clients. You’ll sleep better.

Resources