Agent Forwarding (GNU Privacy Guard (GPG) & SSH) over SSH.
Most all the blog posts I found for this topic were five or more years old, and were referencing a time prior to GnuPG 2.1 which was where some real changes landed, which made this process possible, and safe. These days even LTS distributions such as Ubuntu 20.04 include GnuPG 2.2 which is yet simplier, and requires even fewer hoops be jumped through.
My objective seemed simple:
- From my laptop (macOS Monterey)..
- Forward my EC-DSA key with an SSH agent...
- ..and a GPG Key (actually a smartcard, but that doesn't matter) ..
- into a Linux VM over SSH so that I can work there, sign commits, and clone
It's possible to use a GPG key (and smartcard) as an SSH authentication token, but I'm not interested in that, I have separate SSH and GPG keys, and I'm happy with that set-up.
# Workstation: $ gpg --version gpg (GnuPG) 2.3.4 # anything over 2.1 is fine # VM (ssh target) $ gpg --version gpg (GnuPG) 2.2.19
Out of scope, but it's pretty doable. Follow this guide:
$ gpg --gen-key
It is an interactive program which will ask for your real name, and your user email address, complete those, and then show you some output, the important part is the long key ID:
Real name: Example User Email address: email@example.com You selected this USER-ID: "Example User <firstname.lastname@example.org>" ..... snip ..... pub ed25519 2022-04-06 [SC] [expires: 2024-04-05] 7B5CB440DA3A316537466897128986B90599B1B1 uid Example User <email@example.com> sub cv25519 2022-04-06 [E] [expires: 2024-04-05]
In this case
7B5CB440DA3A3... is the key ID, copy it to the clipboard, or export it to an environment variable, we'll need this a lot.
Run this and follow the prompt...
This will generate something like an
~/.ssh/id_ecdsa or something depending what you configure.
# On your local machine: $ gpgconf --list-dirs agent-ssh-socket /Users/<your username>/.gnupg/S.gpg-agent.ssh $ gpgconf --list-dir agent-socket /Users/leehambley/.gnupg/S.gpg-agent $ gpgconf --list-dirs agent-extra-socket /Users/<your username>/.gnupg/S.gpg-agent.extra % On the remote machine: $ gpgconf --list-dirs agent-ssh-socket /run/user/<your numeric user id, probably>/gnupg/S.gpg-agent.ssh $ gpgconf --list-dirs agent-socket /run/user/<your numeric user id, probably>/gnupg/S.gpg-agent
The GPG Agent and SSH Agent sockets should be self-explanatory enough, however the "extra" socket is peculiar, see this from the docs:
Also listen on native gpg-agent connections on the given socket. The intended use for this extra socket is to setup a Unix domain socket forwarding from a remote machine to this socket on the local machine. A gpg running on the remote machine may then connect to the local gpg-agent and use its private keys. This enables decrypting or signing data on a remote machine without exposing the private keys to the remote machine.
The extra socket then is a slightly less privilidged socket which safely allows forwarding to a remote machine without giving that remote machine full control over your local GPG agent (as a the normal socket would have)
# ~/.ssh/config Host thevmweworkin # this is standard SSH config, mostly HostName 192.168.64.11 User vagrant Port 22 UserKnownHostsFile /dev/null StrictHostKeyChecking no PasswordAuthentication no IdentitiesOnly yes LogLevel FATAL # This is the GPG/SSH forwarding RemoteForward /run/user/<your numeric user id, probably>/gnupg/S.gpg-agent /Users/<your username>/.gnupg/S.gpg-agent.extra RemoteForward /run/user/<your numeric user id, probably>/gnupg/S.gpg-agent.ssh /Users/<your username>/.gnupg/S.gpg-agent.ssh ForwardAgent yes ExitOnForwardFailure yes # ~/.gnupg/agent-config.conf cat ~/.gnupg/gpg-agent.conf default-cache-ttl 600 max-cache-ttl 7200 pinentry-program /opt/homebrew/bin/pinentry-mac # brew install gnupg for this, or don't specify pin entry extra-socket /Users/<your username>/.gnupg/S.gpg-agent.extra enable-ssh-support keep-display default-cache-ttl 600 max-cache-ttl 7200 keep-tty keep-display # Your ~/.zshrc or ~/.bash_profile, etc eval $(gpg-agent --daemon)
Run this to make sure your agent is running/restarted with the correct config:
$ gpg-connect-agent reloadagent /bye # will start an agent if you didn't have one running OK
Configure Git to require signing commits:
$ git config [--global] commit.gpgsign true $ git config --global user.signingkey 34EC1A4D011E7FDFFD6E3722A4F823DC30FA9DA7! # the exclamation mark makes Git use this key, and not try and detect a subkey to use
Configure SSH to remove local sockets of already running daemons, and allow you to overbind them:
/etc/ssh/sshd_conf # add the following: # https://superuser.com/questions/161973/how-can-i-forward-a-gpg-key-via-ssh-agent StreamLocalBindUnlink yes
# From the host machine gpg --output public.pgp --armor --export 34EC1A4D011E7FDFFD6E3722A4F823DC30FA9DA7 gpg --armor --export 34EC1A4D011E7FDFFD6E3722A4F823DC30FA9DA7 | pbcopy scp public.pgp thevm:~/public.gpg
Then go to your profile and paste the new GPG key into your profile.