KeePassXC is a password manager. I make heavy use of its browser integration in Firefox. And I also store my private SSH keys (e.g. for git or deployment) in a .kdbx database.

KeePassXC has an SSH Agent integration. It communicates with the SSH Agent via a socket that is created by the SSH Agent on startup. One great feature of KeePassXC is the loading and unloading of SSH keys to and from the SSH Agent, either manually or upon unlocking and locking of a .kdbx database.

This setup worked well until I recently. After a system upgrade, KeePassXC could not anymore remove keys from the SSH Agent.

Understand the problem

KeePassXC connects to the socket advertised in the environment variable SSH_AUTH_SOCK. When looking at that variable, I noticed that the socket (/run/user/1000/gnupg/S.gpg-agent.ssh) was created by the GPG-Agent. The gpg-agent service is a drop-in replacement for the ssh-agent service and it does not support unloading SSH keys.

There we have it. KeePassXC was loading all of its keys to gpg-agent which refused to forget them.

Remove keys from PGP-Agent

PGP-Agent stores private keys in the .gnupg/private-keys-v1.d/ directory. These files are named keygrip.key. You can find the keygrip of the keys you are using with GnuPG (for signing and encryption) with

paul@pycache:~$ gpg  --list-secret-keys --with-keygrip
/home/paul/.gnupg/pubring.kbx
-----------------------------
sec   rsa4096 2019-09-15 [SC] [expires: 2035-09-13]
      WG0OVWA1M0GPQDAWNPMTW67MEKD3NZAPK72SZ
      Keygrip = 5SUUGGFI8U8SWEPBSPZ3NY82JFTE0NYV1PV3N
uid           [ultimate] Paul Müller <dev@pycache.de>
ssb   rsa4096 2024-08-10 [S] [expires: 2034-08-08]
      Keygrip = 8DGONGBBF7YH5K9NGNLVB1VQ7PWBHE637EDJC

Remove those keys that (are not listed here and) you want to use with ssh-agent, managed by KeePassXC, from that private key directory.

Set up vanilla ssh-agent

To replace gpg-agent with the vanilla ssh-agent (which supports unloading keys), first make sure that you have ssh-agent installed. On Debian, ssh-agent is included in the openssh-client package. You can test this by starting a new instance of ssh-agent:

paul@pycache:~$ eval `ssh-agent -s`
Agent pid 64141

Why the eval? Because ssh-agent prints a script to stdout that sets all necessary environment variables when executed. If you ran just ssh-agent -s, you would get the following output:

SSH_AUTH_SOCK=/tmp/ssh-LiIlmPqRAPpA/agent.64140; export SSH_AUTH_SOCK;
SSH_AGENT_PID=64141; export SSH_AGENT_PID;
echo Agent pid 64141;

Now set the SSH_AUTH_SOCK override option in the KeePassXC settings to the environment variable that you have in your current shell:

paul@pycache:~$ print $SSH_AUTH_SOCK
/tmp/ssh-LiIlmPqRAPpA/agent.64140

Now you can use ssh-add -l in the same shell to list all private keys loaded. Loading and unloading them in KeePassXC should now work here.

Setting up autostart of ssh-agent for KDE Plasma

This is a slim approach to what Lioh Möller from gnulinux.ch (archive.org) proposes. We don’t need to ask for passwords, since KeePassXC handles everything.

Simply create these files, log out and back in, and everything should work fine with the default settings in KeePassXc.

Start the agent via ~/.config/plasma-workspace/env/ssh-agent-startup.sh:

#!/bin/sh
[ -n "$SSH_AGENT_PID" ] || eval "$(ssh-agent -s)"

Stop the agent via ~/.config/plasma-workspace/shutdown/ssh-agent-shutdown.sh:

#!/bin/sh
[ -z "$SSH_AGENT_PID" ] || eval "$(ssh-agent -k)"

And make sure those files are executable (chmod +x path_to_script.sh).