I've been using an Onlykey security key for quite a while now, and while it definitely leaves quite a lot to be desired, it certainly has a few wins. I like that it's open source, that it offers a physical PIN entry, its derived key functionality, to name a few. It supports FIDO2/CTAP of course, but it also offers SSH and GPG agent functions.
Likewise, Qubes OS has a RPC proxying functions that let you separate untrusted guests (such as those you do dev work in) from more trusted ones which hold your secrets. Their split GPG function that leverages this is very popular, and many people have extended this to SSH, dm-crypt, and a bunch of other cool stuff.
Being forever unsatisfied as I am, I've put considerable energy into chaining as much of these things together as possible. I've wanted to have my Onlykey hold the private GPG keys, proxy it from the USB VM to another "vault VM" that holds the Onlykey tooling, and finally have my untrusted VMs proxy their requests to that; but I've struggled. The Onlykey's tooling doesn't expose a standard GPG agent API, and the Qubes split GPG implementation isn't supposed to support passwords on the keys...
But this evening I finally got it all to work, and it was surprisingly easy. I discovered a project that basically is another proxy between the generic GPG agent and the one offered by Onlykey: https://github.com/svareille/OnlyKey-gpg-agent
With all the pieces in place, we can get set up
Setting up
As with last time, I'll define a few variables up front that I'll reference throughout:
EXAMPLE_ONLYKEY_TEMPLATE=
EXAMPLE_ONLYKEY_APPVM=
EXAMPLE_GPG_CLIENT_VM=
dom0
First let's create everything we need in dom0:
Install
qubes-gpg-split-dom0Add the following policy to
/etc/qubes/policy.d:qubes.Gpg * @tag:onlykey-gpg-client @default ask default_target=$EXAMPLE_ONLYKEY_APPVMCreate the server domains:
# make the template qvm-create \ --label=black \ --class=TemplateVM \ --memory=400 \ --maxmem=800 \ --vcpus=2 \ --template=fedora-43-minimal \ "${EXAMPLE_ONLYKEY_TEMPLATE}" # make the appvm qvm-create \ --label=gray \ --class=AppVM \ --memory=400 \ --maxmem=800 \ --vcpus=2 \ --netvm="" \ --template="${EXAMPLE_ONLYKEY_TEMPLATE}" \ "${EXAMPLE_ONLYKEY_APPVM}" # This can be done to reduce the AppVM's footprint qvm-features "${EXAMPLE_ONLYKEY_APPVM}" minimal-usbvm 1Tag any qubes who should be allowed to proxy requests to it:
qvm-tags "${EXAMPLE_GPG_CLIENT_VM}" a onlykey-gpg-client
Preparing the Onlykey Template Qube
The following will be done as root in the $EXAMPLE_ONLYKEY_TEMPLATE
First, install the required packages:
dnf in -y \ gnome-keying libusb1-devel pipx qubes-ctap qubes-usb-proxy systemd-develNext, install the Onlykey tools from pip, proxying as necessary:
PIPX_GLOBAL_BIN_DIR=/opt https_proxy=http://127.0.0.1:8082 \ pipx install --global onlykey onlykey-agentThen install the other stuff you need for the Onlykey to work:
https_proxy=http://127.0.0.1:8082 \ curl -sL https://raw.githubusercontent.com/trustcrypto/trustcrypto.github.io/pages/49-onlykey.rules \ > /etc/udev/rules.d/49-onlykey.rules udevadm control --reload udevadm triggerNext, fetch the helper agent:
cd /tmp https_proxy=http://127.0.0.1:8082 \ curl -sLO https://github.com/svareille/OnlyKey-gpg-agent/releases/download/v1.1.2/ok-gpg-agent-linux.tar.gz echo "fd0d0dc493cef830132f84c7d66f5713db9cbf03195354bfe633ab1834b10340 ok-gpg-agent-linux.tar.gz" | sha256sum -c tar xzf ok-gpg-agent-linux.tar.gz mv bin/* /usr/bin/
Setting up the Onlykey AppVM
First, you'll wanna set up the
ok-gpg-agentservice which will take over for the generic GPG agent. (We don't use the Onlykey GPG agent at all actually)# As root cat <<-EOF | /usr/local/share/systemd/user/ok-gpg-agent.service [Unit] Description=GnuPG cryptographic agent and passphrase cache Requires=gpg-agent.socket Conflicts=gpg-agent.service [Service] ExecStart=/usr/bin/ok-gpg-agent ExecReload=/usr/bin/gpgconf --reload gpg-agent Environment=GNUPGHOME=/home/user/.gnupg/onlykey EOF # As uid 1000 systemctl --user enable --now ok-gpg-agent.service[!NOTE] Of course, you can do this in a DispVM and transfer the bins after
Loading the keypairs is out of the scope of today's article, but I'll write a guide soon cause there's a ton of misinfo out there. You'll have to set up your
ok-agent.tomlin any case, so refer to the repo on how to do that.
Setting up the client AppVM
In their templates, ensure that
qubes-gpg-splitis installedFrom the desired AppVMs, simply set
QUBES_GPG_DOMAINto@default, and then usequbes-gpg-clientorqubes-gpg-client-wrapperas described in the documentation
Wrapping up
With all that done, you should be able to plug in your Onlykey, proxy it to your Onlykey vault/sys qube, and start using it as you would the usual split GPG once it's unlocked! It even works with the physical tap!