Recently I was discussing with a friend how to use SSH to achieve secure, passwordless authentication. He was looking for a way to automate some file transfers and wanted to do it using an expect script (to pump in his passphrase when prompted) to automate the process. I suggested 'ssh-agent', but didn't know quite how to make it work at the time. Since then, I've learned, and it's quite easy.
Using the agent for key based authentication is a method to facilitate communications. You can use key based authentication without the agent, you just have to unlock the key every time you want to use it. Note that by default the ssh client will attempt to authenticate using keys before a password. The agent just makes management of this much easier.
There are several implementations of the ssh protocol, each with its own peculiarities of usage and behavior. The two most common implementations are from openssh.org) and ssh.com). OpenSSH was created for OpenBSD and is thus free software. ssh.com's ssh is a commercial product that is no-cost for open-source operating systems (and for trial, non-commercial and educational use on other OSes). Each implementation of ssh has some slight peculiarities of usage and behavior.
As if multiple implementations weren't enough, there are also two ssh protocols, SSH1 and SSH2. This article focuses on using the SSH1 protocol, which differs slightly from the SSH2 protocol. Previous articles in Linux Gazette have introduced the use of ssh-agent for ssh2 (see below). Note that, by default, ssh2 uses DSA keys, and different directory and file names from ssh1, though compatability can be introduced. Since most people use the SSH1 protocol (data from recent University of Alberta Internet scans using 'scan-ssh'), we will focus on this version. OpenSSH follows, almost perfectly, the syntax of the ssh.com ssh1 program for agent based key management. Note that it differs for ssh2 handling (not covered here).
The benefits of RSA based authentication are numerous, frankly:
Hence, I can't think of any reason (other than not knowing how, which this document is trying to teach you) why you shouldn't use it.
First up, our cast of characters. These are the components that play in this whole thing, so get to know them:
-rw------- 1 jose users 530 Feb 8 12:14 identity
-rw------- 1 jose users 334 Feb 8 12:14 identity.pub
Before we begin, let's make sure the target server allows RSA key based authentication:
$ grep RSA /etc/sshd_config
RSAAuthentication yes
If that says 'no', then this whole thing is moot. Speak to your administrator if you need to.
We use ssh-keygen to generate the keypair. A typical session looks like this:
$ ssh-keygen
Initializing random number generator...
Generating p: ............................++ (distance 446)
Generating q: ...............++ (distance 168)
Computing the keys...
Testing the keys...
Key generation complete.
Enter file in which to save the key (/home/jose/.ssh/identity):
Enter passphrase: (not echoed)
Enter the same passphrase again: (not echoed)
Your identification has been saved in /home/jose/.ssh/identity.
Your public key is:
1024 37
13817424072879097025507991426858228764125028777547883762896424325959758548762313498731030035107110571218764165938469063762187621357098158111964592318604535627188332685173064165286534140697800110207412449607393488437570247411920664869426605834174366309317794215856900173541953917001003859838421924037121230161484169444067380979 jose@biocserver
Your public key has been saved in /home/jose/.ssh/identity.pub
So, now we have the two pieces we need, our public and private keys. Now, we have to distribute the public key. This is just like PGP, frankly, you can share this with anyone, then you can login without any hassle. I'll use 'scp' to copy it over:
$ scp .ssh/identity.pub jon2@li:~/.ssh/biocserver.pub
jon2@li's password:(not echoed)
identity.pub | 0 KB | 0.3 kB/s | ETA: 00:00:00 | 100%
Having copied it there, I will now login to the target machine (in this case the SCL machine 'li') and add it to the list of keys that are acceptable:
li$ cat biocserver.pub >> authorized_keys
OK, now li is all set to let me authenticate using my RSA private key I generated above. Let's go back to my client machine and set up ssh-agent. First, before I invoke the agent, let's look at a couple of environmental variables in my shell:
$ env | grep -i SSH
SSH_TTY=/dev/ttyp3
SSH_CLIENT=129.22.241.148 785 22
Now let's invoke ssh-agent properly. It starts a subshell, so you have to tell it what shell to invoke so it can set it up right.
$ ssh-agent /bin/bash
And it's now set up my environment correctly:
$ env | grep -i SSH
SSH_TTY=/dev/ttyp3
SSH_AGENT_PID=3012
SSH_AUTH_SOCK=/tmp/ssh-jose/ssh-3011-agent
SSH_CLIENT=129.22.241.148 785 22
The two new variables, SSH_AGENT_PID and SSH_AUTH_SOCK, will allow the agent and accessory applications (ie the ssh client, the cache loading tool ssh-add, and such). The sockets are just regular files in the /tmp directory:
$ ls -l /tmp/ssh-jose/
total 0
srwx------ 1 jose users 0 Apr 24 13:36 ssh-3012-agent
So, now that the agent is properly set up, load the cache with your private key. Remember, the agent communicates with the client to hand off your private key when you want to authenticate. Invoking it without any arguments assumes the standard, default private keyfile:
$ ssh-add1
Need passphrase for /home/jose/.ssh/identity (jose@biocserver).
Enter passphrase:(not echoed)
Identity added: /home/jose/.ssh/identity (jose@biocserver)
The passphrase you use here is to ensure "yes, it's me, I have a right to use this key", and it's the same passphrase you set up above when you ran ssh-keygen. Now that the key is loaded, let's look at the cache, using the -l (for 'list') option to ssh-add:
$ ssh-add -l
1024 37 11375588656963284515711893546976216491501314848762129298719958615531627297098741828662897623987120978747144865157469714395736112700558601876305400606604871996923286317135102021232606807975642627653113389875325214757393348628533138103638880715659452391252482099813547642625002508937138181011315411800330612532401318392577 jose@biocserver
Now, when you ssh to another host, you will not get prompted for a passphrase, the private key would have been used as your authenticator using ssh-agent!
$ ssh -l jon2 li
Last login: Tue Apr 24 14:53:39 2001 from biocserver.bioc.
You have mail.
bash-2.03$
Look, Mom, no passphrase needed!
Note that you can alter the above, if you would like, to add some flexibility. First, you can use the output of the ssh-agent program (when invoked without a shell argument), to modify the current shell and set up the agent socket for communication:
$ eval `ssh-agent`
Agent pid 19353;
Now you can add keys as described above, and you have not started a subshell, only having modified the login shell you are currently using. The eval and backticks combination is needed to handle the output that the agent presents to modify the shell. This is because child processes cannot modify the parent shell's parameters.
A second modification you can do is to start your X desktop, such as GNOME or KDE, as the argument to ssh-agent. This will cause every X client locally started to be aware of how to communicate with the agent, allowing for greater ease when you use terminals to log in to other hosts.
That said, you can unload specific keys using ssh-add's '-d' flag, or you can unload all of them using the '-D' flag:
$ ssh-add -D
All identities removed.
This is a good thing to do when you walk away from your workstation. It'd be neat to have a small idle timeout feature, or link this into the screensaver command on your system, or an APM suspend on your laptop.
$ ssh-agent
SSH_AUTH_SOCK=/tmp/ssh-jose/ssh-3019-agent; export SSH_AUTH_SOCK;
SSH_AGENT_PID=3020; export SSH_AGENT_PID;
echo Agent pid 3020;
Let's have a look and see if the correct environmental variables have been set in our shell. These are needed for the agent to work properly, as we saw above:
$ env | grep -i ssh
SSH_TTY=/dev/ttyp3
SSH_CLIENT=129.22.241.148 785 22
The consequences of this are evident when you try and add keys to the cache:
$ ssh-add
Need passphrase for /home/jose/.ssh/identity (jose@biocserver).
Enter passphrase: (not echoed)
Could not open a connection to your authentication agent.
It can't find the socket or the process ID, which is stored in this variable. As such, no keys are available in the cache.