I’ve searched and seen various threads, but nobody has actually followed-up with what they ended up doing.
First: This does allow a piece of software to read your keyboard and mouse movements.
With that out of the way…
- Installing Synergy
Use sudo qubes-dom0-update openssl
to install openssl which is a requirement of synergy.
Get the synergy package for Fedora Core (v37 for 4.2) on the dom0. I used the qvm-run -p <qube, like personal> 'tar c <folder with synergy in it>' | tar xv
sudo dnf install .rpm
Once that’s done, start up Synergy. Enter your serial number when prompted. If it is running, stop the server. Go to Edit → Preferences. Set Network IP to 127.0.0.1.
Enable Encryption, set Key length to 4096, as it will error if the key is too short. Save. Start the server.
- Allow single port connection from forwarding qube to dom0
We need to allow our one port to connect to the dom0 on 127.0.0.1.
Create file /etc/qubes/policy.d/30-synergy.policy with the content:
qubes.ConnectTCP +24800 sys-synergy dom0 allow
Where 24800 is the default port, and sys-synergy is the qube we will create shortly.
- Create qube and setting up Forwarding to dom0
Create a new Qube in Qubes manager - Click New qube, name it sys-synergy
, whatever color you feel comfortable with, and whatever template you like. Use sys-firewall (your default hopefully). Check the box for settings… Check box to start qube on boot.
We are going to use the method covered in the firewall docs and create a systemd service.
Here’s the layout:
user@sys-synergy:~$ cd /rw/config/
user@sys-synergy:/rw/config$ ls -l
total 24
drwxr-xr-x 2 root root 4096 May 2 23:30 network-hooks.d
-rwxr-xr-x 1 root root 484 May 2 23:21 qubes-firewall-user-script
-rwxr-xr-x 1 root root 649 May 3 07:49 rc.local
-rw-r--r-- 1 root root 200 May 2 23:21 suspend-module-blacklist
-rw-r--r-- 1 root root 139 May 3 07:52 synergy@.service
-rw-r--r-- 1 root root 127 May 3 07:51 synergy.socket
user@sys-synergy:/rw/config$ cat rc.local
#!/bin/sh
# This script will be executed at every VM startup, you can place your own
# custom commands here. This includes overriding some configuration in /etc,
# starting services etc.
#
# Executable scripts located in /rw/config/rc.local.d with the extension
# '.rc' are executed immediately before this rc.local.
# Example:
# /rw/config/rc.local.d/custom.rc
#
# Example for overriding the whole CUPS configuration:
# rm -rf /etc/cups
# ln -s /rw/config/cups /etc/cups
# systemctl --no-block restart cups
cp -r /rw/config/synergy.socket /rw/config/synergy@.service /lib/systemd/system/
systemctl daemon-reload
systemctl start synergy.socket
user@sys-synergy:/rw/config$ cat synergy.socket
[Unit]
Description=Forward Synergy to Dom0
[Socket]
ListenStream=0.0.0.0:24800
Accept=true
[Install]
WantedBy=sockets.target
user@sys-synergy:/rw/config$ cat synergy\@.service
[Unit]
Description=Synergy
[Service]
ExecStart=qrexec-client-vm 'dom0' qubes.ConnectTCP+24800
StandardInput=socket
StandardOutput=inherit
- Set up firewall rules
I’m currently using the “qvm-port-forward” script, which adds rules on sys-synergy, sys-firewall, and sys-net.
On dom0:
qvm-port-forward -a add -q sys-synergy -p 24800 -n tcp
If you have another qube you need to control (like a Windows HVM) - on sys-firewall:
sudo nft add rule ip qubes custom-forward ip saddr <IP of sys-synergy> ip daddr <IP of Windows HVM> ct state new,established,related counter accept
If passed the persistent
flag, it creates rules in /rw/config/network-hooks.d/90-port-forward-<name>
.
I ran the script with that flag, and here are the files it created:
sys-net:
user@sys-net:/rw/config/network-hooks.d$ cat 90-port-forward-<sys-firewall IP>-tcp-24800.sh
#!/bin/sh
get_handle(){
chain=${1}
rule=${2}
nft --handle --stateless list chain ip qubes ${chain} | tr -d '"' | grep '^\s\+${rule} \# handle ' | awk '{print $NF}' | tr "\n" " "
}
forward_handle=$(get_handle custom-forward "iifname ens6 ip saddr <Allowed Source Network> ip daddr <sys-firewall IP> tcp dport 24800 ct state established,related,new counter accept")
if test -n "${forward_handle:-}"; then
for h in ${forward_handle}; do
nft delete rule ip qubes custom-forward handle ${h}
done
fi
dnat_handle=$(get_handle custom-pf-10-138-31-131 "iifname ens6 ip saddr <Allowed Source Network> tcp dport 24800 ct state established,related,new counter dnat to <sys-firewall IP>")
if test -n "${dnat_handle:-}"; then
for h in ${dnat_handle}; do
nft delete rule ip qubes custom-pf-10-138-31-131 handle ${h}
done
fi
nft 'add chain ip qubes custom-pf-10-138-31-131 { type nat hook prerouting priority filter +1; policy accept; }
add rule ip qubes custom-pf-10-138-31-131 iifname ens6 ip saddr <Allowed Source Network> tcp dport 24800 ct state established,related,new counter dnat to <sys-firewall IP>
add rule ip qubes custom-forward iifname ens6 ip saddr <Allowed Source Network> ip daddr <sys-firewall IP> tcp dport 24800 ct state established,related,new counter accept'
sys-firewall:
— Changes are made on the DispVM template itself, as sys-firewall is disposable…
user@default-dvm:/rw/config/network-hooks.d$ cat 90-port-forward-<sys-synergy IP>-tcp-24800.sh
#!/bin/sh
get_handle(){
chain=${1}
rule=${2}
nft --handle --stateless list chain ip qubes ${chain} | tr -d '"' | grep '^\s\+${rule} \# handle ' | awk '{print $NF}' | tr "\n" " "
}
forward_handle=$(get_handle custom-forward "iifname eth0 ip saddr <Allowed Source Network> ip daddr <sys-synergy IP> tcp dport 24800 ct state established,related,new counter accept")
if test -n "${forward_handle:-}"; then
for h in ${forward_handle}; do
nft delete rule ip qubes custom-forward handle ${h}
done
fi
dnat_handle=$(get_handle custom-pf-10-137-0-16 "iifname eth0 ip saddr <Allowed Source Network> tcp dport 24800 ct state established,related,new counter dnat to <sys-synergy IP>")
if test -n "${dnat_handle:-}"; then
for h in ${dnat_handle}; do
nft delete rule ip qubes custom-pf-10-137-0-16 handle ${h}
done
fi
nft 'add chain ip qubes custom-pf-10-137-0-16 { type nat hook prerouting priority filter +1; policy accept; }
add rule ip qubes custom-pf-10-137-0-16 iifname eth0 ip saddr <Allowed Source Network> tcp dport 24800 ct state established,related,new counter dnat to <sys-synergy IP>
add rule ip qubes custom-forward iifname eth0 ip saddr <Allowed Source Network> ip daddr <sys-synergy IP> tcp dport 24800 ct state established,related,new counter accept'
sys-synergy:
user@sys-synergy:/rw/config/network-hooks.d$ cat 90-port-forward-<sys-synergy IP>-tcp-24800.sh
#!/bin/sh
get_handle(){
chain=${1}
rule=${2}
nft --handle --stateless list chain ip qubes ${chain} | tr -d '"' | grep '^\s\+${rule} \# handle ' | awk '{print $NF}' | tr "\n" " "
}
input_handle=$(get_handle custom-input "tcp dport 24800 ip daddr <sys-synergy IP> ct state new counter accept")
if test -n "${input_handle:-}"; then
for h in ${input_handle}; do
nft delete rule ip qubes custom-input handle ${h}
done
fi
nft add rule ip qubes custom-input tcp dport 24800 ip daddr <sys-synergy IP> ct state new counter accept
I got this all working yesterday, and wanted to get this out there for those it may help. This was a blocker for me even thinking about using Qubes on a daily basis, as I share my keyboard / mouse between my personal desktop and my work laptop. But I do like the idea of being able to compartmentalize things, so I looked to find a reasonable solution, and I think this works reasonably well. But I also acknowledge that this is still unacceptable for some.
-Matt