GnuPG 2.1 enables you to forward the GnuPG-Agent to a remote system. That means that you can keep your secret keys on a local machine (or even a hardware token like a smartcard or on a GNUK).

You need at least GnuPG 2.1.1 on both systems.

GnuPG configuration

For really old versions of GnuPG (< 2.1.17) you need to edit your gpg-agent.conf to configure an extra socket. The extra socket is more restricted then the normal socket and Pinentry messages will differ when gpg-agent is accessed over this socket:

extra-socket /home/<user>/.gnupg/S.gpg-agent.extra

Replace <user> with your actual username. Current versions of GnuPG create the socket by default.

GnuPG on the remote system

It is important to note that to work properly GnuPG on the remote system still needs your public keys. So you have to make sure they are available on the remote system even if your secret keys are not.

SSH Configuration

Note: With GnuPG >= 2.1.13 the location of the agents socket changed. To find out the name of the local socket, always use:

gpgconf --list-dir agent-extra-socket

this is the name of the extra socket on the local box. On the server the standard socket needs to be configured. Find this one out with

gpgconf --list-dir agent-socket

OpenSSH >= 6.7

To your /.ssh/config you can add:

Host gpgtunnel
HostName server.domain 
RemoteForward <socket_on_remote_box>  <extra_socket_on_local_box>

If you can modify the servers settings you should put:

StreamLocalBindUnlink yes

Into /etc/ssh/sshd_config to enable automatic removal of stale sockets when connecting to the remote machine. Otherwise you will first have to remove the socket on the remote machine before forwarding works.

With StreamLocalBindUnlink yes you can connect with ssh gpgtunnel and just use GnuPG as usual.

Note: On Systems where systemd controls the directories under /var/run/user/<uid> it may be that the socket forwarding fails because /var/run/user/<uid>/gnupg is deleted on logout. To workaround this you can put gpgconf --create-socketdir in the startup script of your shell e.g. ~/.bashrc or ~/.zshrc.

Remote gpg will try to start gpg-agent if it's not running. Remote gpg-agent which will delete your forwarded socket and set up it's own. To avoid this you can pass --no-autostart to remote gpg command.

OpenSSH < 6.7

Before OpenSSH 6.7 you need to use socat which is a bit more fragile and requires a loop to stay open.

# Execute locally
(while true; do
socat TCP-LISTEN:16668,bind=127.0.0.1 UNIX-CONNECT:$HOME/.gnupg/S.gpg-agent.extra;
done) &

# Connect to the remote system with Port forwarding.
ssh -R16668:localhost:16668 server.domain

# Connect the socket on the remote system
(while true; do
    socat UNIX-LISTEN:$HOME/.gnupg/S.gpg-agent,unlink-close,unlink-early TCP4:localhost:16668;
done) &