There is something oddly satisfying about having my private ssh keys only on a hardware device where they cannot be directly accessed.
For the past 6 months I’ve been using a yubikey for SSH access to my servers and github. In this configuration the private key only exists on the yubikey and cannot be transferred to the host computer. All cryptographic operations that require the private key are preformed on the yubikey. Here’s how I set it up.
Install software
Assuming you have homebrew
and brew cask
installed:
brew cask install yubikey-neo-manager
brew install gnupg2 gpg-agent pinentry-mac
Configure the yubikey
- Start
yubikey-neo-manager
- Insert your yubikey
- Select the device
- and click
Change connection mode
to enableCCID
I disabled all other modes since I only cared about using this as an OpenGPG smart card.
Create a key
gpg2 --card-edit
gpg/card> admin
gpg/card> generate
At this point a new keypair is on your yubikey. Your public key will be added to your gpg keychain. Note that you won’t be able to retrieve your public or (private key) from the yubikey. You may want to backup or publish your public key to a key server - but I’ll leave that as an exercise to the reader.
gpg/card> quit
To turn your newly minted GPG public key into an ssh formatted public key:
gpgkey2ssh <key id>
and you will get…
ssh-rsa AAAa1NUMBERSandLETTERS COMMENT
I suggest changing to COMMENT
to something more descriptive, like your name or email address then save that off your github or digitalocean.
Add the following to your .bash_profile
so that your ssh agent (gpg-agent
) will know to also use your yubikey.
gpg-agent &> /dev/null
if [ "$?" -ne "0" ]; then
gpg-agent --daemon &> /dev/null
fi
if [ -f "${HOME}/.gpg-agent-info" ]; then
. "${HOME}/.gpg-agent-info"
export GPG_AGENT_INFO
export SSH_AUTH_SOCK
export SSH_AGENT_PID
fi
export GPG_TTY=$(tty)
And setup your ~/.gnupg/gpg-agent.conf
to be:
# from brew install pinentry-mac
pinentry-program /usr/local/bin/pinentry-mac
# enables SSH support (ssh-agent)
enable-ssh-support
# writes environment information to ~/.gpg-agent-info
write-env-file
use-standard-socket
# default cache timeout of 600 seconds
default-cache-ttl 600
max-cache-ttl 7200
Restart your terminal and test it out.
$ ssh-add -l
2048 6e:2f:6d:0d:ad:23:1c:1e:f0:e1:05:4e:f8:de:ad:1f cardno:000603633910 (RSA)
$ ssh -T git@github.com
Hi tam7t! You've successfully authenticated, but GitHub does not provide shell access.
Fixing issues
Occasionally the gpg-agent
daemon seems to lock up causing ssh
to timeout and commands like ssh-add -l
to hang. I’ve added the following functions to my .bash_profile
to fix these 💩.
kill-gpg-agent() {
killall -9 gpg-agent
}
start-gpg-agent() {
eval $(gpg-agent --daemon)
}
restart-gpg-agent() {
kill-gpg-agent
echo 'remove your key'
sleep 3
start-gpg-agent
}
I had to do restart-gpg-agent
about 4-5 times a day originally, but in the past month I’ve only had to do it a handful of times.