Forward Local GPG Agent Socket through SSH Tunnel

 Published on 7 Sep 2022 .  Filed in Notes .  788 words

Table of Contents

Introduction

gpg-agent is a daemon used to manage GPG secret (private) keys for the gpg and gpgsm programs. These programs interact with the gpg-agent daemon through a socket to request the secret key and request for the secret key.

If the gpg-agent process has the key, it provides it to gpg. If not, it tries to load the encrypted key from your keyring and prompts you for the key’s passphrase. Once the agent has obtained the decrypted key, it will store in memory. Then it passes it to the GPG process. In addition to GPG secret keys, Gpg-agent can also store SSH keys and provide them to SSH processes, like the ssh-agent program that comes with SSH.

There are 2 sockets through which programs like gpg and gpgsm communicates in order to retrieve secret keys. First agent-socket is used by local process to communicate with gpg-agent and the second one is agent-extra-socket which is a more restricted socket can be used by remote machine in case of socket forwarding.

GPG v2.1 enables you to forward the gpg-agent to a remote system. That means that you can keep your secret keys on a local machine.

You need at least GnuPG 2.1.1 on both systems.

Configure SSH Server

We first need to configure SSH server on remote machine to enable automatic removal of stale sockets when connecting (or forwarding GPG socket) to the remote machine. Otherwise you will first have to remove the existing socket on the remote machine before forwarding works.

This can be automated by adding the following line to /etc/ssh/sshd_config file:

StreamLocalBindUnlink yes

Forward Local GPG Agent Socket

Let’s say the remote machine needs an SSH key stored on our local machine in the GPG keyring in order to sign a commit before pushing to Git.

So we forward gpg-agent socket which is located at /run/user/1000/gnupg/S.gpg-agent to remote machine through SSH tunnel using -R option which can forward any type of gpg-agent socket and remote machine uses the forwarded socket instead of local one. So the remote machine will interact with our forwarded gpg-agent which contain secret keys.

ssh -R <remote socket path>:<local socket path> hostname

We can find the location of GPG socket using the following command:

gpgconf --list-dir agent-socket

Then replace the default SSH socket used by the SSH program to retrieve SSH keys by the forwarded GPG Agent socket:

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

GPG agent tries to load the encrypted key from your local GPG keyring using forwarded socket and prompts you for the key’s passphrase if the key is protected. Once the agent has obtained the decrypted key, it will store in memory. Then it passes it to the SSH program.

Since the ssh-agent protocol does not contain a mechanism for telling the GPG agent on which display/terminal it is running, gpg-agent’s ssh-support will use the TTY or X display where gpg-agent has been started. So to update the display to the current one add the following line in your .bashrc file or whatever initialization file is used for all shell invocations:

export GPG_TTY=$(tty)
gpg-connect-agent updatestartuptty /bye >/dev/null

gpg-agent configuration options can be define in .gnupg/gpg-agent.conf file for permanent application. For example if we wants to decrypt a GPG secret key from a shell then we need to use a terminal based pinentry program. This can be changed by adding the pinentry-program /usr/bin/pinentry-tty to gpg-agent configuration file. Also make sure that pinentry-tty program exist on your system, if not install it using your package manager.

Here are some other options often used:

# Enable gpg-agent to manage keys instead of ssh-agent by SSH client
enable-ssh-support
    
# Change default pinentry program
# pinentry-program /usr/bin/pinentry-qt

# Set the time a cache entry is valid to n seconds.
default-cache-ttl 3600

# SSH password cache time
default-cache-ttl-ssh 3600
max-cache-ttl-ssh 3600

A full list of gpg-agent configuration options can be found here.

GPG Agent Command

The gpg-agent is automatically started on demand by gpg, gpgsm, gpgconf, or gpg-connect-agent and there is no reason to start it manually. But we can start it using:

gpg-connect-agent /bye
# OR
gpgconf --launch gpg-agent

Reload gpg-agent:

gpg-connect-agent reloadagent /bye

You can manually terminate the currently-running gpg-agent:

gpgconf --kill gpg-agent

Debug Socket Forwarding

When connecting to a remote machine, we can use the -v option to display information about the forwarding socket:

ssh -v -R <remote socket path>:<local socket path> hostname

Sometimes on a remote machine we need to delete the socket created by remote gpg-agent. This happens when the remote gpg-agent is started or restarted after forwarding the local gpg-agent through the SSH tunnel.

References

https://wiki.gnupg.org/AgentForwarding

https://unix.stackexchange.com/questions/188668/how-does-gpg-agent-work - How does GPG agent work?