LAN remote access

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.