Password management in Qubes is difficult, you either copy and paste everything between VMs and get more phishable as well as missing out on passkeys, or you install a password manager on the needed VMs and lose your entire vault if one of them gets popped. The good news is a similar solution as split SSH is possible. Client VMs can request credentials as if they were on the same system, and KeePassXC can be safely put into a separate vault VM and allow / deny as needed. This isn’t a perfect mitigation; vulnerabilities in KeePassXC itself could still exist, but it should hopefully reduce the risk of a compromised banking VM instantly nuking my LinkedIn account.
You will need three things:
- The client VM must forward the KeepassXC browser extension’s unix socket to the vault VM.
- The vault VM must be setup to receive it.
- dom0 must have qrexec-policies setup to allow this.
Client VM
Place the following under .config/systemd/user/socat.service:
[Unit]
Description=proxy keepassxc connection to vault VM
StartLimitIntervalSec=0
[Service]
Type=simple
SyslogIdentifier=socat
RuntimeDirectory=user/1000/app/org.keepassxc.KeePassXC
ExecStart=/usr/bin/socat 'UNIX-LISTEN:/run/user/1000/app/org.keepassxc.KeePassXC/org.keepassxc.KeePassXC.BrowserServer,fork' 'EXEC:qrexec-client-vm NAME_OF_VAULT_VM user.KeePassXCVault'
Restart=always
RestartSec=1s
[Install]
WantedBy=default.target
This will create a unix domain socket listening at the same location as where the KeePassXC app would, then forwards anything incoming on it using qrexec-client-vm. It may take a while for the vault VM to be reachable if it is shutdown, so the service retries indefinitely every second until successful. As the fork option is set, the service should not stop when the connection is terminated, as during a database close.
As I use Firefox, I also needed to place the following under ~/.mozilla/native-messaging-hosts/org.keepassxc.keepassxc_browser.json:
{
"allowed_extensions": [
"keepassxc-browser@keepassxc.org"
],
"description": "KeePassXC integration with native messaging support",
"name": "org.keepassxc.keepassxc_browser",
"path": "/usr/bin/keepassxc-proxy",
"type": "stdio"
}
keepassxc-proxy is part of the KeePassXC package, but since I just use the same template for both client and vault VMs, this isn’t a problem.
Vault VM
The vault VM must have KeePassXC installed with browser integration enabled. It is also best to have it setup with no net qube.
Place this file under /usr/local/etc/qubes-rpc/user.KeePassXCVault:
#!/bin/bash --
set -euo pipefail
socat - "UNIX-CONNECT:/run/user/1000/app/org.keepassxc.KeePassXC/org.keepassxc.KeePassXC.BrowserServer"
Make it executable using chmod +x.
dom0 policies
Place this file under `/etc/qubes/policy.d/99-user.policy:
user.KeePassXCVault * @tag:keepassxc-client NAME_OF_VAULT_VM allow notify=yes
This will allow any VM tagged with keepassxc-client to call user.KeePassXCVault rpc action on the vault VM. VMs can be tagged from dom with qvm-tag VM_NAME add keepassxc-client, or using qubes-ansible. You could of course have it for individual named VMs, or @anyvm and ask instead of allowing with notifications only.
You can now run systemctl --user enable --now socat.service. on the client VM. Installing the KeePassXC-Browser extension should work normally so long as you have your vault unlocked.
Related links: