SSH and GPG Agents
If you’re using SSH or GPG with any sort of frequency, you should
definitely be using their accompanying
*-agent programs. The agents
allow you to gain a whole lot of convenience without compromising your
security. Many people seem to be unaware these tools exist, so here’s
an overview along with some tips on how to use them effectively.
Let’s start from the top.
Both SSH and GPG involve the use of asymmetric encryption, and the private key is protected by a user-entered passphrase. The private key is generally never written in to the filesystem in plaintext. In the case of GPG, these keys are the primary focus of the application. For SSH, they’re a useful tool to make accessing remote machines less tedious. (The SSH server is authenticated by a public key, too, but this is unrelated to agents.)
For those who are unaware, rather than enter a password when logging into a remove machine, you can identify yourself by a public key. Generating a key is simple.
You’ll almost certainly want to accept the default location for the
~/.ssh/id_rsa) because this is where SSH will look for it. Make
sure you enter a passphrase, which will encrypt the private key. The
reason this is important is because, without it, anyone who gains
access to your
id_rsa file will be able to access any remote systems
that have been told to trust your public key. By having a passphrase,
this person needs not only the
id_rsa file, but also the passphrase
(two-factor authentication), so you probably want to pick a long,
strong one. This may sound inconvenient, but
ssh-agent will help
The key generation process will create two files:
id_rsa.pub (public key). The latter is what you give to
Telling a remote system about your key is simple,
This will copy your
id_rsa.pub to the remote system, prompting you
for the password on the remote system (not the passphrase you just
entered), adding it to the file
~/.ssh/authorized_keys. From this
point on, all logins will use your new keypair rather than prompt you
for a password. Since you put a passphrase on your key, this may seem
pointless — it seems you still need to type in a password for every
connection. Bear with me here!
As a side note, you should have a unique SSH keypair for each site, so you’ll have several of them. This way you can revoke access to a particular site without affecting the others.
For GPG — the GNU Privacy Guard, the free software PGP
implementation — your keys are stored under
~/.gnupg/ in a
database. Generating a key is also a simple command,
This is a slightly more complicated process, which I won’t get into here. In contrast to SSH, you’ll generally have only one keypair per identity (i.e. you only have one).
So you’ve got these keys are encrypted by passphrases. If they’re going to be any use then they’ll be long, annoying things that are a pain to type in. If that was the end of the story this would be really inconvenient, enough to make the use of passphrases too costly for many people to bother. Fortunately, we have agents to help.
An agent is a daemon process that can hold onto your passphrase
gpg-agent) or your private key (
ssh-agent) so that you only need
to enter your passphrase once within in some period of time (possibly
for the entire life of the agent process), rather than type it many
times over and over again as it’s needed. The agents are very careful
about how they hold on to this sensitive information, such as avoiding
having it written to swap. You can also configure how long you want
them to hold onto your passphrase/key before purging it from memory.
gpg programs need to know where to find the
agents. This is done through environmental variables. For
the process ID is stored in
SSH_AGENT_PID and the location of the
Unix socket for communication is in
stuffs everything into one variable,
GPG_AGENT_INFO (which is a pain
if you want to use this information in a script). When the main
program is invoked and it needs to use the private key, it will use
these variables and get in touch with the agent to see if it can
supply the needed information without bothering the user.
Remember, a process can’t change the environment of their parent process so you need to set this information in the agent’s parent shell somehow. There are two methods to set these up: eval and exec.
When you start the agent, it forks off its daemon process and prints
the variable information to stdout. This can be
evaled directly into
the current environment. You could drop these lines directly in your
.bashrc so that the agents are always there. (Though they won’t exit
with your shell, lingering around uselessly! More on this ahead.)
eval $(ssh-agent) eval $(gpg-agent --daemon)
For the exec method, you replace your current shell with a new one with a modified environment. To do this, you ask the agent to exec into a shell, with the variables set, rather than return control.
exec ssh-agent bash exec gpg-agent --daemon bash
As cool trick, you can chain these together.
gpg-agent which then becomes
exec ssh-agent gpg-agent --daemon bash
gpg-agent is capable of being an
ssh-agent as well by
--enable-ssh-support option, so you don’t need to launch
ssh-agent. Unfortunately, I don’t like to use this because
gpg-agent gets a little too personal with the SSH key, storing its
own copy with its own passphrase again.
On the other hand,
gpg-agent is much more advanced than OpenSSH’s
ssh-agent. When you want to have
ssh-agent manage a key, you need
to first tell it about the key with
ssh-add. With no arguments, it
~/.ssh/id_rsa. If you forget to do this,
ssh will ask for
your passphrase directly, in your terminal, not allowing
to hold onto it. By comparison,
gpg will always ask
retrieve your passphrase when it’s needed (if the agent is available),
so it will cache your passphrase on demand. No need to explicitly
register with the agent. Even better, it will try its best to use a
“PIN entry” program to read your key, which helps protect against some
kinds of keyloggers — preventing other processes from seeing your
Well, this is all fine and dandy except when you’ve already got an
agent running. Say you’re launching a new terminal emulator window
from an existing one, creating a new shell. Unfortunately, even though
you have agents running and they’re listed in your environment (from
the origin shell), they’ll still spawn new agents! This is really
lousy behavior, in my opinion. There’s no
--inherit option to tell
them to silently pass along the information of the existing agent if
it appears to be valid. This causes two problems. One, you’ll need to
enter your passphrases again for the new agent. Second, these new
agents will linger around after the spawning shell has exited —
hogging important non-swappable memory.
The direct workaround is to, in your shell init script, check for these variables yourself and check that they’re valid (the agent process is still running) before trying to spawn any agents. This is tedious, error-prone, and makes each user do a lot of work that could have been done in one place by one person instead.
There’s still the problem of when you launch a new shell that doesn’t inherit the variables (i.e. a remote login), so there’s no way for it to be aware of the existing agents. To fix this, you’d need to write the agent information to a file. The shell init script checks this file for an existing agent before spawning one. This is even more complicated, more error-prone, and subject to race-conditions. Why make every use go through this process?!
Fortunately someone’s done all this work so you don’t have to! There’s
an awesome little tool called
Keychain which can be used to
launch the agents for you. It stores the agent information in a file
so that you only ever launch one instance of the agent, and the agents
will be shared across every shell. It does have an
option — the default behavior, so you don’t even need to ask
nicely. Instead of running the
*-agents directly, you just put this
eval $(keychain --eval --quiet)
So simple and it just works! I was so happy when I found this. This is the magic word that makes using agents a breeze, so I can’t recommend it enough.