I’ve previously written about SSH and
ssh-agent on Mac OS X where I mentioned a utility named SSHKeychain that helps manage the agent daemon and your passphrases. Well, Mac OS X 10.5 (Leopard) has been released since that post, and things have changed. The long and the short of it is that
ssh-agent is handled much better than before, by default. But its usage can also be a bit confusing (at least it was for me). I’ll try and explain how it all works in Leopard, so you can get the most out of it.
Why Use a Passphrase, Reprisal
I read a rebuttal or two to my previous post, where I mentioned that using a passphrase protects your private key, if someone gains access to your account. Their main argument was that if your system gets hacked, then the bad guy has complete access to your machine and you’re screwed anyways. True, if someone gets root on your machine, the game is pretty much over. As root, they can do really nasty things like steal
ssh-agent identities and install key loggers to snoop your passphrase.
~/.ssh/id_rsa), and post it to a website, for example. Or what if you accidentally left your laptop unattended (and unlocked) for a few minutes at the coffee shop? Someone could grab the private key file and stick it on a USB drive. If the attacker was even half way smart, they’d grab
~/.ssh/known_hosts along with your key, since that contains host names you’ve connected to.
Without a passphrase, your private key is completely usable to the bad guy. Using a passphrase encrypts your private key, so that they would have to crack your passphrase to get access to it. In summary, an empty passprhase on your private key is a bad idea. It’s just asking for trouble.
Okay, back to the topic at hand:
ssh-agent on Leopard. One of the benefits of SSHKeychain (or one of the other
ssh-agent apps for OS X) is that it starts
ssh-agent at login time. It also sets the
SSH_AUTH_SOCK environment variable (which points to a Unix domain socket) to be accessible by all apps (usually by modifying
~/.MacOSX/environment.plist). Leopard gives you the equivalent of this, out of the box. Open up a terminal and see:
% echo $SSH_AUTH_SOCK /tmp/launch-nZRFjA/Listeners
While this environment variable is automatically set for all processes, this is a little deceiving.
ssh-agent does not actually get started when you log in. Go ahead and see for you self, by running this command right after you login:
% ps xa | grep ssh-agent | grep -v grep
You should get nothing back. It turns out that
ssh-agent gets started on demand, the first time something tries to connect to the socket:
% ps xa | grep ssh-agent | grep -v grep % ssh-add -l The agent has no identities. % ps xa | grep ssh-agent | grep -v grep 10877 ?? S 0:00.02 /usr/bin/ssh-agent -l
ssh-add -l tried to connect to the socket, and thus caused
ssh-agent to launch. Pretty nifty.
But how can the
SSH_AUTH_SOCK be valid without
ssh-agent? The magic sauce is
launchd was introduced in Mac OS X 10.4 as a replacement for many traditional Unix daemons such as
(x)inetd is known as a super-server, and
xinted plus a few other daemons, I like calling
launchd an über-server. In this context,
launchd creates the Unix domain socket and listens for connections, on behalf of
ssh-agent. When something connects, it automatically launches
launchd is always running, it can listen for connections right after you login.
For the curious, this is done with the new
SecureSocketWithKey plist key for
This optional key is a variant of SockPathName. Instead of binding to a known path, a securely generated socket is created and the path is assigned to the environment variable that is inherited by all jobs spawned by launchd.
launchd plist file for
ssh-agent is at:
The benefit of this lazy launching is that
ssh-agent is only run if you use
ssh. If you never use
ssh, the agent never gets launched, and you don’t waste any resources running it.
But the awesomeness doesn’t stop there. If you have an SSH key and try to connect to a server, you’ll get this nifty dialog box asking for your passphrase:
The first benefit of this dialog box is that it uses a secure text field for your passphrase. This field is not copiable and not snoopable via universal access or low-level keyboard routines. The real benefit, though, is the second checkbox: “Remember password in my keychain.” While it does store the passphrase in your keychain, it actually does more than that. It also adds the identity to your
ssh-agent for you:
% ssh-add -l The agent has no identities. % ssh firstname.lastname@example.org ... Type passphrase and check "Remember in my keychain" ... example.com> exit Connection to example.com closed. % ssh-add -l 2048 xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx /Users/dave/.ssh/id_rsa (RSA)
By adding your identity to the agent, you can now log right back into the same machine, without typing any passphrase. However,
ssh does not prompt for you passphrase because it gets it from the agent, not your keychain. Remove your identity from the agent, and try again:
% ssh-add -D All identities removed. % ssh email@example.com
You’ll get that same GUI dialog box again. But it stores your passphrase in your keychain, right? Then why is it asking for your passphrase if it’s in your keychain? And, why would it store the passphrase in your keychain if it’s not going to actually use it? I know I was scratching my head at this point. Well, it does use the keychain, just not how you may think.
It turns at that when
ssh-agent is started, it automatically adds all identities that have passphrases stored your keychain. So the normal workflow, where identities are not removed from the agent, just works. Since the GUI dialog box added your identity to the agent when it added the passphrase to your keychain, you don’t need enter your passphrase again for the rest of that login session. For all future sessions, the identity is automatically added when
ssh-agent starts. And remember from above, that
ssh-agent starts on demand, only when needed. Thus, you only enter your passphrase once, and from then on it grabs it from the keychain.
Manually Adding Identities
If you do want to remove your identities, you can manually add all the identities from the keychain with the Apple-specific
-k option on
Add identities to the agent using any passphrases stored in your keychain.
Sweet! Let’s try it out:
% ssh-add -D All identities removed. % ssh-add -l The agent has no identities. % ssh-add -k Added keychain identities. % ssh-add -l 2048 xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx /Users/dave/.ssh/id_rsa (RSA)
Beware, the passphrase dialog box is not a GUI version of
SSH_ASKPASS, though. Try adding identities without consulting the keychain, and you’ll still get prompted in the terminal:
% ssh-add -D All identities removed. % ssh-add Enter passphrase for /Users/dave/.ssh/id_rsa:
And that covers it. I’m a little too paranoid to use this default behavior, though, as I don’t want my passphrase to be stored in my login keychain. Thus, I’ve written a follow-up article that discusses possible ways to beef up security of your passphrase.