Secure Garden

Using GPG to Authenticate over SSH

Introduction

Secure Shell, Better known as SSH, is one of the most used tools to remotely manage servers. It allows developers and sysadmins around the world to login to a remote server as if they were there; however, as with any remote technology, there are security measures used to authenticate the user.

For anyone who as used SSH before, you probably know that there are two ways to authenticate the user:

  1. A password (very insecure and not recommended)
  2. A SSH public-private key pair

SSH uses ssh-agent to manage ssh keys. On some systems, ssh-agent isn’t used and SSH just directly serves identity files to the server.

PGP uses a very similar public-private key pair system, and is often used for various purposes. I won’t get into all the amazing ways PGP keys can be used (if you’re reading this, you probably already know some); however, it’s important to point out the different type of keys you can have:

  • Certification key [C]
  • Encryption key [E]
  • Signing key [S]
  • Authentication key [A]

Each one of these key types are used in different applications, but we’ll focus on the authentication key. If you don’t already have an authentication sub-key, you should generate one1.

We can ultimately use the PGP authentication key to replace our SSH keys. By replacing ssh-agent with gpg-agent we can have SSH use our GPG keys when connecting via SSH.

This is actually a relatively simple setup to get working. We have to:

  1. Point SSH to use gpg-agent for SSH authorization
  2. Configure gpg-agent to serve the correct authentication key to SSH

Configure SSH to use gpg-agent

SSH uses the SSH_AUTH_SOCK environment variable which points to UNIX socket used by the authentication agent. To get SSH to use gpg-agent, we just need to have SSH_AUTH_SOCK point to gpg-agent’s socket.

Setting this up is rather simple. We can get gpg-agent’s socket by running: gpgconf --list-dirs agent-ssh-socket. We will also need to launch gpg-agent by running: gpgconf --launch gpg-agent.

To piece this together, add the following to your shell’s startup file2:

export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket)
gpgconf --launch gpg-agent

Configuring gpg-agent

GPG still needs to be configured to properly serve the key to SSH.

First add enable-ssh-support to ~/.gnupg/gpg-agent.conf to enable SSH support:

echo 'enable-ssh-support' >> ~/.gnupg/gpg-agent.conf

At this point, SSH should be setup to use GPG keys; however, it’s good practice to specify which GPG key should be served. To do this, you have to first get the keygrip3 from your authentication subkey (the one that should be used for SSH).

To get the keygrip run: gpg -K --with-keygrip which will produce an output with keygrips for your (sub)keys. Find the keygrip that corresponds to your authentication key, which will be shown with [A].

ssb   rsa4096 2020-01-01 [A] [expires: 2022-01-01]
      Keygrip = 1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZABCD

Save the keygrip for the authentication key to ~/.gnupg/sshcontrol:

$ echo '1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZABCD' >> ~/.gnupg/sshcontrol

Getting Public SSH Key / Concluding notes

Everything should now be setup to authenticate using GPG keys. To get the public key that should be added to any server you wish to connect to, run gpg --export-ssh-key <user-id> with your email or GPG key fingerprint. This will output a valid SSH public key that can be added to the authorized_keys on the remote server(s).

The sshcontrol file should contain the keys that should be serve by gpg-agent, but should any need to be disabled in the future, you can prepend the keygrip with an ! mark to disable it.

If gpg-agent is invoked (trying to authenticate a SSH session) without a sshcontrol file, the file will be generated with the following comment:

# List of allowed ssh keys.  Only keys present in this file are used
# in the SSH protocol.  The ssh-add tool may add new entries to this
# file to enable them; you may also add them manually.  Comment
# lines, like this one, as well as empty lines are ignored.  Lines do
# have a certain length limit but this is not serious limitation as
# the format of the entries is fixed and checked by gpg-agent. A
# non-comment line starts with optional white spaces, followed by the
# keygrip of the key given as 40 hex digits, optionally followed by a
# caching TTL in seconds, and another optional field for arbitrary
# flags.   Prepend the keygrip with an '!' mark to disable it.

  1. There are many different guides on how to add a authorization subkey; however, I followed the YubiKey guide by drduh. ↩︎

  2. The startup file for Bash is ~/.bashrc, and Zsh uses ~/.zshrc↩︎

  3. For an explanation between fingerprints and keygrips see this blog post↩︎