SSH with gpg-agent on systemd
Tags: ssh, gpg, systemd, yubikey
Published: 2024-07-12 19:43:05

There's a lot of information floating around on the web explaining how to replace `ssh-agent` with `gpg-agent`. This is important if you - like me - want to use a [yubikey](https://www.yubico.com/) or smart card device to store the private key of your SSH key pair.

Unfortunately, none of the advice worked for me with the following system setup:

- systemd v249
- GNOME desktop 42.9
- Ubuntu 22.04

Although the above is quite specific, I expect the information below to much more generally applicable.

## Prerequisite for Yubikeys and smart cards

GPG needs `pcscd` and `scdaemon` to be able to access private keys stored on a yubikey or smart card:

```bash
$ sudo apt install pcscd scdaemon
$ systemctl enable --now pcscd
$ systemctl enable --now scdaemon
```

After running these commands, `gpg --card-status` should provide you with information about your smart card.

## Configuring GPG with ssh-agent support

To enable ssh-agent support in gpg-agent, add the following to your gpg-agent.conf:

```plain
# ~/.gnupg/gpg-agent.conf
enable-ssh-support
```

## Enabling `gpg-agent` (per user)

`gpg-agent` is a per-user service, which systemd facilitates with the `--user` flag on the `systemctl` command:

```bash
$ systemctl enable --user --now gpg-agent.socket
$ systemctl enable --user --now gpg-agent-ssh.socket
```

If you forget to add the `.socket` extension, systemctl will throw an error about a missing `[Install]` section in the service definition.  Please note that the sockets are to be enabled; the service itself is to be left untouched.

Verifying status of the GPG agent can be done using:

```bash
$ systemctl status --user gpg-agent.socket
$ systemctl status --user gpg-agent-ssh.socket
```

Although the service is now running, the environment shows an incorrect value for `SSH_AUTH_SOCK` (`/run/user/<uid>/keyring/ssh`).

## Fixing SSH_AUTH_SOCK

There are several configurations blocking successful use of `gpg-agent` for SSH, causing the value of `SSH_AUTH_SOCK` to be incorrect.

### Disable `ssh-agent` user service

If it is enabled, this service needs to be disabled, as it is conflicting:

```bash
$ systemctl disable --user --now ssh-agent
```

### Disable Xsession.options ssh-agent

The `Xsession` script tries to start an `ssh-agent`. This can be disabled by modifying `/etc/X11/Xsession.options` and `/etc/X11/Xsession.options.d/*.conf`, making sure the last file loaded contains:

```plain
no-use-ssh-agent
```

This is best achieved by creating a file named `/etc/X11/Xsession.options.d/zz-no-ssh-agent.conf` with that content.

### Disable GNOME Keyring (gnome-keyring-ssh.desktop)

GNOME Keyring tries to start an SSH agent if none is running. The way to disable this is by executing the following commands - which are different from those published all around:

```plain
$ cp /etc/xdg/autostart/gnome-keyring-ssh.desktop ~/.config/autostart/gnome-keyring-ssh.desktop
$ echo "X-GNOME-Autostart-enabled=false" >> ~/.config/autostart/gnome-keyring-ssh.desktop
$ echo "Hidden=true" >> ~/.config/autostart/gnome-keyring-ssh.desktop
```

The "Hidden=true" addition is advertized all around the web, but didn't work for me.

### Setting SSH_AUTH_SOCK

Using systemd to manage `gpg-agent` has one drawback: systemd doesn't set environment variables in the user shell session.  This can be solved by adding this snippet to the user's `bashrc`:

```bash
# to be appended to ~/.bashrc
if [[ -z "$SSH_AUTH_SOCK" ]]
then
  if [[ -e "/run/user/$(id -u)/gnupg/S.gpg-agent.ssh" ]]
  then
    export SSH_AUTH_SOCK="/run/user/$(id -u)/gnupg/S.gpg-agent.ssh"
  fi
fi
```

## Conclusion

Yubikey and smart card support with private keys on the key or card does not come out of the box, even in this day and age, but with a bit of configuration, setting up private keys on a smart card and making it work under GNOME, is doable.