Skip to content

Instantly share code, notes, and snippets.

@al-maisan
Created March 22, 2026 08:51
Show Gist options
  • Select an option

  • Save al-maisan/c1808d3486f48e516df5eba950db36a0 to your computer and use it in GitHub Desktop.

Select an option

Save al-maisan/c1808d3486f48e516df5eba950db36a0 to your computer and use it in GitHub Desktop.
GPG Agent Forwarding over SSH (YubiKey + remote hosts)

GPG Agent Forwarding over SSH

Forward a local GPG agent (e.g. YubiKey) to remote hosts so you can sign, encrypt, and authenticate via SSH without copying private keys.

Local machine (client)

~/.ssh/config

Host myserver
    Hostname 1.2.3.4
    User myuser
    StreamLocalBindUnlink yes
    RemoteForward /run/user/1000/gnupg/S.gpg-agent         /run/user/1000/gnupg/S.gpg-agent
    RemoteForward /run/user/1000/gnupg/S.gpg-agent.ssh     /run/user/1000/gnupg/S.gpg-agent.ssh
    RemoteForward /run/user/1000/gnupg/S.gpg-agent.extra   /run/user/1000/gnupg/S.gpg-agent.extra
    RemoteForward /run/user/1000/gnupg/S.gpg-agent.browser /run/user/1000/gnupg/S.gpg-agent.browser

Note: StreamLocalBindUnlink yes on the client side only affects local sockets. The server must also have it enabled (see below).

Remote host (server)

1. Enable StreamLocalBindUnlink in sshd

This is critical — without it, stale sockets from previous sessions block new connections.

echo "StreamLocalBindUnlink yes" | sudo tee -a /etc/ssh/sshd_config
sudo systemctl reload sshd

2. Prevent local gpg-agent from auto-starting

Create ~/.gnupg/gpg.conf:

no-autostart

3. Socket path mismatch

On some systems, gpgconf --list-dirs agent-socket returns ~/.gnupg/S.gpg-agent instead of /run/user/1000/gnupg/S.gpg-agent. If this happens, create symlinks:

ln -sf /run/user/1000/gnupg/S.gpg-agent ~/.gnupg/S.gpg-agent
ln -sf /run/user/1000/gnupg/S.gpg-agent.ssh ~/.gnupg/S.gpg-agent.ssh
ln -sf /run/user/1000/gnupg/S.gpg-agent.extra ~/.gnupg/S.gpg-agent.extra
ln -sf /run/user/1000/gnupg/S.gpg-agent.browser ~/.gnupg/S.gpg-agent.browser

4. Import public key

The remote host needs your public key (private key stays on the hardware token):

gpg --keyserver keyserver.ubuntu.com --recv-keys YOUR_KEY_FINGERPRINT

5. Use GPG auth key for SSH

To use the GPG authentication subkey for git and SSH on the remote host:

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

Add to ~/.bashrc to make it permanent.

Verification

ssh myserver
gpg --card-status        # should show your card/YubiKey
gpg --list-keys          # should show your public key
ssh -T git@github.com    # should authenticate (if SSH_AUTH_SOCK is set)

Troubleshooting

Symptom Cause Fix
Warning: remote port forwarding failed Stale sockets from previous session Enable StreamLocalBindUnlink yes in server's sshd_config
No agent running gpg looking at wrong socket path Create symlinks from ~/.gnupg/S.gpg-agent to /run/user/1000/gnupg/S.gpg-agent
No such device / No SmartCard daemon scdaemon/pcscd not installed locally sudo apt install scdaemon pcscd (local machine)
selecting card failed Local gpg-agent not running gpgconf --launch gpg-agent (local machine)
Forwarding works once but breaks on reconnect Server sshd not cleaning stale sockets StreamLocalBindUnlink yes in /etc/ssh/sshd_config + reload
Local gpg-agent on remote creates conflicting sockets gpg-agent auto-starts on remote no-autostart in ~/.gnupg/gpg.conf on remote

Tested on

  • Client: Fedora 43 with YubiKey 5
  • Server: Ubuntu 24.04 LTS
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment