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
git+ssh://
repositories
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.
Check you have new enough software
# Workstation:
$ gpg --version
gpg (GnuPG) 2.3.4 # anything over 2.1 is fine
# VM (ssh target)
$ gpg --version
gpg (GnuPG) 2.2.19
Generate or configure relevant keys:
Smart-card
Out of scope, but it's pretty doable. Follow this guide:
Generate a GPG Key
$ 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: user@example.com
You selected this USER-ID:
"Example User <user@example.com>"
..... snip .....
pub ed25519 2022-04-06 [SC] [expires: 2024-04-05]
7B5CB440DA3A316537466897128986B90599B1B1
uid Example User <user@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.
Generate an SSH key
Run this and follow the prompt...
$ ssh-keygen
This will generate something like an ~/.ssh/id_rsa
or ~/.ssh/id_ecdsa
or something depending what you configure.
Getting Relevant Socket Addresses
# 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)
Local Configuration
# ~/.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
Remote Configuration
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
Upload the key someplace (Github, Gitlab), and into the VM public keychain
# 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.
Top comments (0)