Passwords are a necessary evil, but they get in the way when we have to administer multiple Unix hosts via scripts.  Once we've set up so Unix hosts trust one another enough to log us in without passwords, then the next step is to extend the functionality to PuTTY, the ssh client of choice for Windows-to-Unix logins.

Before we continue, a disclaimer and explanation about the infrastructure.  We're not doing anything beyond the design and intent of ssh.  This feature has long been part of ssh, just under-utilized.  We do use a password, but it's a master password that unlocks one or more public key/private key pairs stored outside the ssh client in a separate program called an agent.  This agent is checked for when ssh (and PuTTY, which is just an implementation of ssh) attempts to connect.  Once we enter the master password to the agent program, then we can log in without being challenged for our password each time.

First, let's set up our public key/private key pairs.  On the local Unix host, make sure we have a $HOME/.ssh directory.  Once verified or created, ensure permissions on this directory are 700, i.e., only we can read, write and execute.  Now, run the command

bash$ ssh-keygen -t dsa

When prompted, save the file as

$HOME/.ssh/id_dsa

Remember the password - there's no way to recover it.  In our $HOME/.ssh directory, we should have two files, the aforementioned id_dsa and id_dsa.pub.  The former is the private key, which is decrypted with the password we just used.  The latter is the public key, the file we don't care if others can see.

Now, let's log onto the remote Unix host (using a password.)  Once there, create $HOME/.ssh directory if it doesn't exist already.  Ensure permissions on this directory are 700, i.e., only we can read, write and execute.  Group and other permissions are denied.  Now copy $HOME/.ssh/id_dsa.pub to $HOME/.ssh/authorized_keys .  Once that is complete edit the authorized_keys file by prepending the string

from="$local_host"

to the line that starts with 'ssh-dss'.  (Where $local_host is the name of the Unix host from which we will be logging onto this remote host.  If  we're not sure, the hostname is presenting in the output of the "who am i" command.)  This means that if the remote Unix host's sshd (ssh service for Windows users) sees a successful public key challenge response from a host in the authorized_keys file, then it will allow access.  It's an 'and' logical test - the public key challenge response must be correct, -and- the host trying to log in must be one listed in the authorized_keys file.

Now, back to the local host.  ssh-agent is the penultimate part of the puzzle.  It is a persistent process, a daemon that only works for us to give the correct response to the public key challenge from the remote host.  Run the following command:

 bash$ ssh-agent | tee $HOME/.ssh/ssh-agent.out

The output will look something like this:

SSH_AUTH_SOCK=/tmp/ssh-w90fj29/agent.1234; export SSH_AUTH_SOCK
SSH_AGENT_PID =235; export SSH_AGENT_PID
echo Agent pid 1235

This is how the Unix ssh client knows to talk to the agent, by those environment variables.  Mind you, these variable are not actually set yet - the agent is just leaving a one-time message telling us how to talk to it (hence teeing the output to a file.)  Run the command

bash$ . $HOME/.ssh/ssh-agent.out

This will set the environment variables, a necessary step. 

Time for the last part, and the smoke test.  Run the following command

bash$ ssh-add $HOME/.ssh/id_dsa

This will add the key we just created to the agent.  Now, for the smoke test!

bash$ ssh $remote_host

Where $remote_host is the remote Unix host we just set up the authorized_keys file on.

If that works, we're set.  If it doesn't, you're on your own. =)  Seriously, leave a comment, I'll see what I can do.

===

Now, for PuTTY.  This is really just icing on the cake - we don't need this, but seeing as we've set up the infrastructure already, let's enable Windows to connect via PuTTY to the $remote_host as well.

First, run PuTTYgen, the putty equivalent of ssh-keygen.  Now, copy the id_dsa file from $local_host's .ssh directory to a file on our Windows desktop (alternately, if our Unix $HOME is accessible over CIFS/SMB, just open a Windows Explorer and verify we can get to that file.)  In PuTTYgen, look for the Conversions menu, and select Import.  In the Open File dialog, open id_dsa.  It will prompt us for the password, which we of course supply.  It then offers to generate a key pair.  However, it defaults to RSA, and we're using DSA, so we need to set "Type of key to generate" (in the Parameters section of the Key Generator window) to "SSH-2 DSA".  Let's name the file id_dsa_putty.

This will generate a file pair, a private key: id_dsa_putty.ppk, and a public key: id_dsa_putty.pub.  These correspond to the id_dsa and id_dsa.pub files that Unix ssh-agent uses.  PuTTY's counterpart is Pagent.  Let's start Pagent.  Not a lot happens - it creates the Pagent icon in the system tray and that's it.  When we right-click on the Pagent system tray icon, that's where we see the option to add a key.  We point it at the newly created id_dsa_putty.ppk, enter our password and try using PuTTY to ssh to $remote_host.

 And fail.

We forgot to add the Windows host to the authorized_keys file.  Once we log onto $remote_host and add the Windows host to the file, our next attempt will work.

Again, if it doesn't work, leave a comment and I'll see what I can do.

To review, we learned a few things:

  • We can automate our login with ssh-agent and PuTTY's pagent.
  • We scratched the surface of public/private key infrastructure.
  • Now we can automate tasks across Unix hosts without the security hole that is rsh.