Behind Locked DoorsSep 20, 2022, by Ryan Heywood
Foreword: This blog post will likely change as time goes on and I learn more about Qubes and how I can adapt my home-built SSH solution to be on par with the provided SSH solution. I hope to keep the structure of the post mostly consistent.
Image generated from Stable Diffusion: A locked chamber, imposing, cyberpunk, vaporwave
Recently, I've started using an operating system at work called QubesOS. The operating system is pretty fancy, it gives me a way to separate all of my applications into little environment-specific containers. It's very useful as a contractor, as I can have many clients at once. With the default 4 workspaces that Qubes provides me, that gives me the ability to move between my personal Qube, my Distrust Qube, and a couple Qubes I have set up for clients. It offers me an excellent way to make sure that both the visual component - the programs that I'm using, whether it be Firefox, Thunderbird, or a terminal - and the data components, such as the actual project files themselves, are all self-contained.
However, each of these clients, in one way or another, needs me to be able to interact with them cryptographically in a way that I can trust. This can be unlocking a password database, signing a GitHub commit, connecting to a server via SSH, or logging into a service that incorporates a WebAuthn authentication flow. I need a way to be able to take all four of these qubes and hook them into a central point of authority that I'm able to trust with access to cryptographic operations involving my private key.
QubesOS by default comes with a qube called the "vault". This is a component whose compromise would come second only to dom0. It is the centralized authority for all of my sensitive cryptographic operations. If I want to do anything that requires access to PGP, SSH, or WebAuthn, it would be run through this component first. For my personal system, it's based off the Debian 11 template, so while most of these commands should have a Fedora alternative (or otherwise, if Qubes starts offering new TemplateVMs), the instructions I give are targeted towards a Debian system.
The first thing I wanted to do was to make sure that my smartcard could
properly attach to the Vault. I used the sys-usb setup to connect my
smartcard, a Yubikey, to the qube. I was able to confirm that the device was
visible from inside the qube by running
lsusb. From there, since
gpg-agent were installed by default, I was able to run
gpg --card-status to
confirm that the
Note: At this point, if the
gpg --card-status command fails, you should
stop what you're doing and make sure that
gpg2 is installed in your vault
qube and that the
gpg-agent systemd user service has been started.
The Qubes team has created a tool called qubes-gpg-client and
qubes-gpg-client-wrapper which allows an almost seamless replacement for
gpg2 for programs such as Git, Thunderbird, and otherwise. It is
close enough to the point where I personally symlink
/usr/bin/qubes-gpg-client-wrapper. I also make sure to put
export QUBES_GPG_DOMAIN=vault in my
.zshrc file, to ensure any program I run will
know which qube to connect to. This can be tested by running
QUBES_GPG_DOMAIN=vault qubes-gpg-client-wrapper --list-keys, which will
prompt me for access before performing the operation, which is incredible when
compared to alternative solutions. Be sure to run
qubes-gpg-import-key to add
your GPG key to the system, and run
qubes-gpg-client-wrapper -K to ensure you
have access to the private key.
While the Qubes team offers an out-of-the-box option for GnuPG and OpenPGP operations, their support for SSH is less than ideal. In a previous version of this article, I included an adapted version of the community Split SSH guide, but I now have a post detailing an improved version.
I also followed the instructions for the Qubes U2F Proxy to the letter to
get a WebAuthn workflow working to the same device I have attached for PGP and
SSH and despite being slightly slow at times, for the most part it works fine.
I'm able to test this by going to https://webauthn.io.
WebAuthn is a protocol created by the FIDO Alliance to create an origin-based
signature system that, when run in a trusted environment such as a browser or
desktop session, can't be spoofed. This is due to the fact each cryptographic
signature is tied to an origin. Therefore, if I have a WebAuthn setup
guthib.com can't spoof it because they'd also
make the device believe that it's requesting a signature for
A friend of mine recently blogged about 2FA methods. It's an interesting read that I think gives a good oversight into how the entire security ecosystem relies on the possibility of human mistake. SMS and TOTP 2FA are phishable by not having anything tied to the domain name, while notification 2FA causes notification fatigue while also not being phishing resistant as there's no way to verify the request is coming from an authentic source - while the issue mentioned in the aforementioned post could have been due to absentmindedness, there's no inherent way of verifying the origin of the request.
Note: I have had minor issues with CloudFlare which I haven't been able to debug yet, so if anyone runs into similar issues with CloudFlare and WebAuthn not working in Qubes U2F Proxy, I'd be happy to either hear a solution or at least hear that it's not just me.