SSH Authentication with GPG

Why Authenticate with GPG

First let me clarify that an SSH key pair is a perfectly secure way to
authenticate to a system. They can have the proper amount of encryption and be password protected and all that good stuff that goes along with being secure. So the question is why bother with doing a lot of work to change something that is already working?

The simple answer is because we can. I was completely happy with my SSH key pair and really never thought twice about it. Then one day I bought a Yubikey and everything changed. I made a master GPG key and from that create subkeys for signing, encryption, and authentication. I literally changed my entire GPG and SSH workflow simply because I got a new toy and I could.

Where to Start

GPG keys and SSH keys aren’t all that different; you have a private key, public
key, and a password to protect them. There is plenty of documentation available on how to create an authentication key with GPG so I’m not going to get into that here. You can reference a previous blog post of mine if you want a quick reference. What is needed to use an authentication GPG key for SSH is something that will be covered.

Exporting the Public Key

When exporting the public key for use with ssh the gpg --export command won’t give us what we want. The public key needs to be in SSH format, not GPG format. Luckily GPG has some built in functionality that makes this type of public key export just as simple.

1 gpg --export-ssh-key <key id> > .ssh/id_rsa.pub

The above command will export the public GPG key in SSH format to an id_rsa.pub file in the .ssh directory. The last thing to do with the public key is to be sure it is present in any remote system you want to authenticate to, ie. GitHub.

Configuring .bashrc

Now that the easy part is done we can start working on the more challenging part. ssh-agent doesn’t work with GPG keys, but gpg-agent can be made to work with SSH. To do this we must first add a few lines to the .bashrc file to make sure
we don’t have the two stomping on each other. The below block of code will unset the ssh-agent PID environment variable and setup the SSH authentication socket to use gpg-agent.

1 # GPG-Agent
2 unset SSH_AGENT_PID
3 if [ "${gnupg_SSH_AUTH_SOCK_by:-0}" -ne $$ ]; then
4 export SSH_AUTH_SOCK="${HOME}/.gnupg/S.gpg-agent.ssh"
5 fi
6
7 export GPG_TTY=$(tty)

Configuring GPG-Agent

gpg-agent by default does not support SSH so we have to be sure to configure it at startup. In the .gnupg directory there is a gpg-agent.conf file that needs
some modification in order to have gpg-agent do what we need it to. The below block starts the daemon with SSH support and configures the pinentry program for the TTY input. The default-cache-ttl and max-cache-ttl are default configurations that don’t require modification.

1 enable-ssh-support
2 pinentry-program /usr/local/bin/pinentry-curses
3 default-cache-ttl 60
4 max-cache-ttl 120

Now that we have the configuration file set we can run three simple commands to get things up and running.

1 killall ssh-agent gpg-agent
2 unset GPG_AGENT_INFO SSH_AGENT_PID SSH_AUTH_SOCK
3 eval $(gpg-agent --daemon --enable-ssh-support)

After running the above commands everything should be working as expected and you should be able to do SSH authentication with GPG.

Validating Configuration

You can validate that everything is working by simply trying to SSH to GitHub.

ssh -vT git@github.kdc.capitalone.com

If things are working you should see

1 debug1: Authentication succeeded (publickey).
2 Authenticated to github.com ([192.30.255.112]:22).

3 debug1: channel 0: new [client-session]
4 debug1: Entering interactive session.

5 debug1: pledge: network
6 debug1: Sending environment.
7 debug1: Sending env LC_CTYPE = UTF-8
8 Hi discreet! You've successfully authenticated, but GitHub does not provide shell access.

If for some reason you see public key denied refer to the many troubleshooting articles on the interwebs.