ssh-keygen, ssh-agent, ssh_config, and their password-less buddies

Like most command-line habitués, I’ve been using SSH and it’s buddies (scp, rsync, svn+ssh, sftp, vpn, tunneling), since around the time I wrote my first #include “stdio.h” main(){printf(“hello world\n”);} , and just got used to typing in username/password pairs every time I had to log in. Of course as with email, e-commerce sites, forums, and everything else on the wonderful intrawebs, I was schooled from an early stage to prioritize security (i.e.; not using my first name as the user and my dog’s name as the pass on all my accounts). This makes frequent log-ins on a dozen or so accounts considerably more enjoyable..

With a few recent projects the need to frequently login on many machines, and create automated tasks requiring user-less logins (backups,etc,..) got me looking into public/private keys and other methods. I had heard that there were all kinds of slick tricks that could be done with SSH, including baking cakes and putting satellites into orbit, but my intention was to achieve some level of convenience/security without reading a treatise the length of Crime and Punishment.

Public/Private keys

Without dissecting the full mechanism of encrypted communication let it be said that public/private keys are similar to physical locks and keys; once a lock is made, it is possible for an unauthorized entity to pick it, but it is relatively very difficult (depending on encryption type and key length). The “public” key is like a physical lock, and the “private” key like the physical key that is used to open it, through a challenge/response protocol. So it is relatively ok for many others (public) to know about the lock but we probably want to keep the key to ourselves. In order to use the lock/key pair we install a public key on the server we want to log in to, and keep the private key on our client. If we have more than one of these for logging into many machines it becomes a “keychain” in effect. Nuts & bolts for creating/storing/installing the key pairs:

# type can be dsa or rsa, generated on the client account. for
# automated tasks requiring user-less logins it may be necessary
# to use a blank password although this is less secure. There are
# ways around that reqt and one of them will be discussed further down
pancho@client$ ssh-keygen -t dsa
Generating public/private dsa key pair.
Enter file in which to save the key (/home/pancho/.ssh/id_dsa): /home/pancho/.ssh/id_dsa_key1        
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/pancho/.ssh/id_dsa_key1.
Your public key has been saved in /home/pancho/.ssh/id_dsa_key1.pub.
The key fingerprint is:
93:80:fe:8a:f9:c2:2a:f5:78:cf:2b:57:b3:8b:cb:f6 pancho@client
The key's randomart image is:
+--[ DSA 1024]----+
|                 |
|    *     **     |
|   **      *     |
|                 |
| *           *   |
|  *         *    |
|   * *******     |
|                 |
|                 |
+-----------------+

Now the private key will be stored as /home/pancho/.ssh/id_dsa_key1 while the public key is stored as /home/pancho/.ssh/id_dsa_key1.pub

There are clever and useful ways to automagically send the public key over to the server account (such as ssh-copy-id) but you can also just log in to it, open up its $HOME/.ssh/authorized_keys file and carefully append the shiny new public key (which is really the lock). It is a good idea to make a backup copy of the file and/or be careful when editing it because it may already contain other keys, and changing them will render their respective key-authenticated logins broken. Here is a sample authorized_keys file:

willamina@server$ cat ~/.ssh/authorized_keys
ssh-dss AAAAB3NzaC1kc3MAAACBAMjOAVT6RdfFjGM2Wk3L9sDe/u7qgFUGHF9kL+jFVLbX9qrPQqBh5wlicrGdL/Fish5u0VfyJD+R/8j71KVXFXLlDOVlxrXCXGBRpRuJwMj2jh0mrA2BaC3DZcCTF5sfOh5Fda+W3o/bvZ9GR+3/54Eov4NvamF/A1EISEuqw8SnAAAAFQCAbdkgeW2+BGJiKLZLmb+F2M2gAwAAAIARQE5/cr1eYCr++gPlGwa93nXu81a9EZm3msPTbfF5UTF3MTt27/nHKnZ2UXJtZo/cn3Eh4T8QS8q3wqeatdq2/oJ2ppbQBbY5dkY3M01he48ffYmVO+PggCAmzD7OuDwF2lpUW6Fg3Agz+xZEYDTuhoe+0jw7aOVw5FhHdKgVzAAAAIEAq8bekAARMEERzUw1sUUX6IiVqvnMWeWojq+bG8OvK43zD8dSTgLXjp3D9sFG4akceUfxrRUViN3oGLljg8F/NyZU8dteWnW6nMBNEuMGMRHcDjOiL4X3wTsZJuSeVHqKFw/d0NgPsPNEbYh03LbdrHvuK5ROvlVcIL50YmBM40Q= pedro@client1
ssh-dss AAAAB3NzaC1kc3MAAACBAJZfNfZ1ibOiWQu5nPghZA+eRLSWm+4QnjtmbaFVb+cb3p9HyNnlvcFCjVwJGYjlW6BbT+bqkoT8A1LVNFwtswd+9FEqm+G/BYJKLRYLHEvZvGiqFx81Y2XSorhAInvMSjFzW4nylb6RtZH0nuGUcLb0dzt/OG//Zr8yJBbXcRG1AAAAFQD63fOcG+OQ6hLoj3Uvez58HgsA7wAAAIEAjs0mwxbilSCXANtQpgLLWgQLXKlpZNpmg5WwLlWhmrPy0llq/9kijFBTaSJ4W6/T363wT6M4xwajDfDyoclzgW16RtmCGtAScGDk3RAQm/R7zKV5h0LKZKBN0b/RdhNUaYSTsJ9JXG+NV98lp+TkJ2bHaC3ffvplVMoFtoSl3TAAAACAZV3vv9lVPoFSHG5LKmh3TI2kIgJssIJCUbPWlMponkqLy5Cx9+xICY8r3zv/MeJK8FMwSuVObRZ1qygoc5OHaIiXHKLm5gW8HvdNzEltemDn//TiaT5HRrVaGY7Kxv1nEzvf+H7H23IQFPvm2NKn/WKGxvORtfj5FGWDpZQfyTA= pancho@client2

It is a good idea to set restrictive permissions on at least the private key and authorized_keys files, such as 0600. It may also be a good idea to change keypairs every now and then. ssh-keygen itself has many other options for managing the keys.

And Voila.. Log out of the server account and next time you log in from the client the server should prompt you for the password you used when the key-pair was generated (or not if none was used). The password-less part is especially useful for automated tasks but bad if someone gets a hold of the private key. So in general we want to keep using passwords, and all we’ve done with this whole key business is add another component to authentication making it a bit more secure. What a waste of time. But wait,.. ..Ladies & Gentlemen. In this corner, weighing in at 256 lbs, with 1024 victories, NaN defeats, hailing from inode 0xBADC0FFEE0DDF00D, block 0xdeadbeef, it’s,… S.S.H. Aagent!!… (cheers and applause) <ding>

ssh-agent

# calling ssh-add registers an identity with an ssh-agent for use in
# locally "un-locking" the private key for use in future log-ins. On
# my system, ssh-agent was already running as a daemon process and the
# necessary environment variables were set. On other systems it may be
# necessary to start/stop the agent through other means.
pancho@client$ env | grep SSH
SSH_AGENT_PID=1234
SSH_AUTH_SOCK=/tmp/ssh-nzpaAalp5678/agent.5678
pancho@client$ ssh-add -l
The agent has no identities.
pancho@client$ ssh-add ~/.ssh/id_dsa_key1
Enter passphrase for /home/pancho/.ssh/id_dsa_key1: 

Now as long as the agent is running and the registered identity is not deleted (which can be done), I can log in to the server with no password.. 😀

I have yet to investigate whether this will fail, but see no reason why identities for automated processes couldn’t also be registered once and remain usable as long as the same agent is running. Passwords would need to be re-entered if the machine is rebooted / ssh-agent killed / etc,..
wonderful key hanger by Janice Warren
There are many additional tools for managing ssh-agent and the “keychain” for logging into multiple accounts. They vary in degree of complexity and sophistication and include GUI apps, systems for managing large numbers of users across multiple networks and such, but I won’t go into them here.

As you can imagine, even with keys and ssh-agent, it can be a chore to keep track of all the keys, usernames, ip addresses/hostnames across all the client and server systems. Luckily SSH can use both a system-wide configuration file, and a per-user config to address this.

ssh_config

SSH_CONFIG(5)             Gentoo File Formats Manual             SSH_CONFIG(5)

NAME
     ssh_config -- OpenSSH SSH client configuration files
...
ssh obtains configuration data from the following sources in the following order:

1. command-line options

    2. user's configuration file (~/.ssh/config)
    3. system-wide configuration file (/etc/ssh/ssh_config)
...
The configuration file has the following format: 
...
Host' Restricts the following declarations (up to the next Host keyword) to be only for those hosts that match one of the patterns given after the keyword. '*' and '?' can be used as wildcards in the patterns. A single '*' as a pattern can be used to provide global defaults for all hosts. The host is the hostname argument given on the command line (i.e., the name is not converted to a canonicalized host name before matching). 
...
HostName
Specifies the real host name to log into. This can be used to specify nicknames or abbreviations for hosts. Default is the name given on the command line. Numeric IP addresses are also permitted (both on the command line and in HostName specifications). 
...
IdentityFile
Specifies a file from which the user's RSA or DSA authentication identity is read. The default is ~/.ssh/identity for protocol version 1, and ~/.ssh/id_rsa and ~/.ssh/id_dsa for protocol version 2. Additionally, any identities represented by the authentication agent will be used for authentication. The file name may use the tilde syntax to refer to a user's home directory. It is possible to have multiple identity files specified in configuration files; all these identities will be tried in sequence. 
...
User' Specifies the user to log in as. This can be useful when a different user name is used on different machines. This saves the trouble of having to remember to give the user name on the command line. 
...
SEE ALSO
     ssh(1)

AUTHORS
     OpenSSH is a derivative of the original and free ssh 1.2.12 release by Tatu Ylonen.
     Aaron Campbell, Bob Beck, Markus Friedl, Niels Provos, Theo de Raadt and Dug Song removed
     many bugs, re-added newer features and created OpenSSH.  Markus Friedl contributed the
     support for SSH protocol versions 1.5 and 2.0.

Gentoo/linux                     July 27, 2011                    Gentoo/linux

Yes, there are a ton of options. Many of them quite useful.. The ones shown in my copy & paste from the man page above are what I focused on for my purposes for now. Given a .ssh/config file like:

Host server1
   HostName example.com
   IdentityFile /home/pancho/.ssh/id_dsa_key1
   User willamina

Host server2
   HostName 203.0.113.123
   IdentityFile /home/pancho/.ssh/id_rsa_key2
   User tawhaki

Host daedalus
   HostName foo.org
   IdentityFile /home/pancho/.ssh/id_rsa_key3
   User curly

..I don’t have to keep track of all the hostnames/usernames/keys for all the accounts. Instead I can log-in with a simple “ssh daedalus”. Of course it is possible to use the same key for all the accounts. ssh will still prompt for the key passphrase every time if one was given, but that can be remedied with ssh-agent and ssh-add.

Finally a sample bit of scriptactic sugar to tie it all together (there are other apps/tools to address this such as this and this):

#!/bin/bash
# "host" here corresponds with host lines in local ssh_config file.
# if a key exists for the host, the user will be prompted for its
# passphrase on the 1st login for the running ssh-agent. Afterwards
# since the identity has been added no pass will be necessary

host="$1"

if [ -z "$host" ]; then
	echo 'usage: '`basename $0`' <host>'
	exit 1
fi

IdentityFile=`
	awk '	($1=="Host"){
			last_host=$2
		}
		($1=="IdentityFile" && last_host=="'$host'"){
			print $2
			exit 0
		}' $HOME/.ssh/config`


if [ -n "$IdentityFile" ]; then
	ssh-add -l \
	| grep -q "$IdentityFile" \
	||  ssh-add "$IdentityFile"
fi

/usr/bin/ssh "$host"
This entry was posted in Uncategorized. Bookmark the permalink.

2 Responses to ssh-keygen, ssh-agent, ssh_config, and their password-less buddies

  1. Subhiksha Gara says:

    Do you mind if I quote a couple of your articles as long as I provide
    credit and sources back to your site? My blog is in the
    exact same niche as yours and my users would really benefit from a lot of
    the information you provide here. Please let me know if this alright with you.
    Thank you!

  2. Pablo says:

    Of course I don’t mind, go for it! 😀

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.