Synergy Server in Qubes

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…

  1. 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.

  1. 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.

  1. 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

  1. 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

Excellent guide. Trying a modified version of it on Synergy 3.
Just one issue, you say you used qvm-port-forward, but that command doesn’t exist by default on my install, and the only version I could find of it doesn’t take that syntax.
Just wondering if you had a copy handy, or a link to the version you’re using.
Thank you!

I searched and found it again.
qusal/salt/dom0/files/bin/qvm-port-forward at main · ben-grande/qusal · GitHub is the script itself.
It was linked to in the bottom of this issue: [Contribution] qvm-expose-port · Issue #4028 · QubesOS/qubes-issues · GitHub

On a weird note, I came downstairs to a machine that didn’t seem locked but wouldn’t take any keyboard input. I had to kill lightdm. Since then the screensaver has worked as expected, so I’m not sure what happened.

-Matt

So I have no idea what I’m doing wrong, but I can’t run that script in dom0. It throws a ‘required file not found’ when run. Doesn’t really indicate what file it’s looking for either, so I’m at a loss there.
Thank you for putting the output here. I’ll try inputing that manually sometime.

Nevermind I am dumb and copied that script into a text file in Windows and moved that onto Qubes. Linux being Linux that text file is completely different from a normal text file and couldn’t possibly be executable.
Anyway. I’ll get around to fixing my process tomorrow and try again.
This is mostly here on the off chance somebody else has as weird a process as I do.

The network hooks stuff doesn’t seem to actually work for the “sys-synergy” qube - I have to execute it manually upon startup.

Not sure where that came from. Still a little new to all of this.

-Matt