As a first-timer jumping into the new release, this was the first thing I did-- consider it a hack to “install” Qubes on my unsupported laptop[1].
The networking wasn’t that complicated in retrospect, but it was definitely a lot to take in on top of, well, all of the Qubes. A lot of the threads that come up when you search for “Remote Access” are inconclusive or dismissive, so I wanted to record what I did as of v4.2.1 (March 2024).
Disclaimer: Opening a port to dom0
is obviously a major pot-hole on the slippery slope of trading security for convenience. Don’t do this if you’ve legitimately been drawn to Qubes for it’s security posture.
I hit this thread (ours, this one, right here) pretty quickly, and dove for qubes-remote-desktop
via qubes-contrib
.
sudo qubes-dom0-update qubes-repo-contrib
sudo qubes-dom0-update qubes-remote-desktop
That, uh, didn’t work. The URL format seems to have changed over at https://contrib.qubes-os.org/, and when I patched /etc
to use the current paths, qubes-remote-desktop
wasn’t in the repo. I adjusted the paths and version numbers to pull from the v4.1
repo, and that got me my unit files.
qubes-remote-desktop
doesn’t seem to have been made with dom0
in mind, so I got a lot of help with the networking from @AWhite and @apparatus’ discussion on out-bound dom0
ports; just had to flip the direction around and, uhm, get qvm-run
and socat
to co-operate in a vaguely robust manner[2]. Maybe because I ditched their systemd .socket
unit? And I’m pretty sure some of these nft attributes are extraneous. But without further ado:
The Code
# sys-net:/rw/config/qubes-firewall-user-script
if nft add chain qubes custom-dnat-dom0 '{ type nat hook prerouting priority filter +1; policy accept; }'; then
nft add rule qubes custom-dnat-dom0 iif == "ens6" ip saddr 192.168.0.0/24 tcp dport 5900 ct state new,established,related counter dnat {{SYS_FIREWALL_IP}}
nft add rule qubes custom-forward iif == "ens6" ip saddr 192.168.0.0/24 ip daddr {{SYS_FIREWALL_IP}} ct state new,established,related counter accept
fi
# sys-firewall:/rw/config/qubes-firewall-user-script
if nft add chain qubes custom-dnat-dom0 '{ type nat hook prerouting priority filter +1; policy accept; }'; then
nft add rule qubes custom-dnat-dom0 iif == "eth0" ip saddr 192.168.0.0/24 tcp dport 5900 ct state new,established,related counter redirect to :5900
nft add rule qubes custom-input iif == "eth0" ip saddr 192.168.0.0/24 tcp dport 5900 ct state new,established,related counter accept
fi
# dom0:/home/{{USER}}/run.sh
cat << 'EOF' | sudo tee /etc/systemd/system/custom-dnat-dom0@.service
[Unit]
Description=custom-dnat-dom0
After=qubes-x0vncserver@%i.service
PartOf=qubes-x0vncserver@%i.service
[Service]
SyslogIdentifier=custom-dnat-dom0@%i
User=%i
Group=%i
Type=exec
ExecStart=bash -c \
"while true; do \
/usr/bin/socat \
EXEC:\"'qvm-run --pass-io sys-firewall \
socat tcp-listen:5900,keepalive stdio'\" \
tcp:localhost:5900; \
qvm-run --pass-io sys-firewall \
pkill -f 'socat tcp-listen:5900.\* stdio'; \
done"
ExecStopPost=qvm-run --pass-io sys-firewall \
pkill -f 'socat tcp-listen:5900.* stdio'
# XXX: x0vncserver doesn't wait until it's listening to fork.
ExecStartPre=/bin/sleep 2
[Install]
WantedBy=qubes-x0vncserver@%i.service
EOF
sudo systemctl daemon-reload
sudo systemctl enable custom-dnat-dom0@{{USER}}
sudo systemctl enable --now qubes-x0vncserver@{{USER}}
If I were going to do any more work on/like this, I’d come back to these notes:
- This thread suggests qubes-remote-support, and possibly future developments in GuiVM+VNC support.
- A lot of people seem to be using x2go over SSH for this.
- It’s obvious from this example that the networking could be greatly simplified once I grok policies.
- Didn’t discover
qvm-connect-tcp
or this util until I was done, but it was a better learning experience that way. - Some pains were taken to not modify
qubes-remote-desktop
’s existing unit files. An ideal re-packaging would probably tie into Qubes’ existing framework for RPC Services and support ad-hoc Qube, Protocol, and Access configurations. - could vnc do 2FA (both auth & pass-through)?
1: ie. I know this isn’t ideal in several ways, and that there exist suggestions such as forwarding only the display.
2: I believe qvm-run
closes std{in,out}
on EOF
such that we can’t continue to read from it, and that it was leaving orphaned processed in sys-firewall
when the Qube-side socat
was configured to fork. Doesn’t seem designed for procedural use, and it doesn’t help that socat
’s not a perfect citizen of the command-line either.