NOTE: The setting described below have security implications. See https://groups.google.com/d/msg/qubes-users/3amtMOIBTgI/rXLXmbM5AAAJ for discussion. At the moment, I am not recommending this approach. I’ll update here if I am able to improve on it. Caveat emptor!


The Qubes operating system aims to be reasonably secure through isolation.

One example of isolation: one Qubes virtual machine runs the windowing environment, while other VMs run applications displayed in that environment. This separation from the X11 environment means that an application, like skype or bluejeans, cannot share this screen. This is by design, and is a good example of the security that makes me a fan of Qubes.

Sometimes, however, I want to screenshare. This post describes how it can be done in Qubes (4.x).

The strategy is to run a virtual network computing (VNC) desktop. The applications we explicitly display there can be screenshared.

To get the most out of this post, readers should already be familiar with Qubes and the difference between template vs. application VMs.

Prepare the Template VM

I made a clone of Qubes’ fedora-25 template called f25-media (because I’ve installed codecs and applications to display various media formats). I use that template for the VM which runs the VNC server. So to start, install the VNC software there.

[user@f25-media ~]$ sudo dnf install tigervnc tigervnc-server

Prepare the Screenshare App VM

I set up a VM dedicated to running vncserver. The idea is to be able to share apps running in more than on app VM, in a single screen sharing session. If you only ever need to screenshare from a single app VM, you could instead just run vncserver there, and skip some of the configuration described here..

I called this app VM screenshare and based it on the f25-media template.

Inter-VM networking

The screenshare VM is hosting VNC, other VMs will display apps there, so those app VMs need a network connection to screenshare. By default, Qubes prevents such networking, so we need to explicitly enable it.

You can allow this inter-vm network traffic by following the official instructions.

I chose another approach which I think is simpler. I created a VM called inter-vm-proxy. In /rw/config/qubes-firewall-user-script I set up these rules…

intervm_internalnet="10.137.0.32" # Change this to the IP address of `inter-vm-proxy` VM.

iptables -I FORWARD 1 -i vif+ -o vif+ -s $intervm_internalnet/24 -d $intervm_internalnet/24 -m state --state NEW -p tcp -m tcp -j ACCEPT
iptables -I FORWARD 1 -i vif+ -o vif+ -s $intervm_internalnet/24 -d $intervm_internalnet/24 -p udp -m udp -j ACCEPT

Note, you’ll also need to accept incoming connections on the screenshare VM. Use a similar approach in /rw/config/qubes-firewall-user-script, this time on screenshare app VM…

ip="10.137.0.33" # Change this to the IP address of `screenshare` app VM.
iptables -I INPUT -s $ip/24 -j ACCEPT

Configure screenshare VM to use inter-vm-proxy for networking.

Also, before you need to screenshare from other app VMs, configure them to use inter-vm-proxy, too! While this may seem like an extra step, I think of it as additional security. Unless I configure an app VM to use inter-vm-proxy network, I won’t accidentally screenshare from it.

Screenshare VM Window Manager

The screenshare VM needs a window manager for the VNC environment. I decided to try dwm from suckless. It has a reputation for being lightweight, which I’m more than OK with, for this purpose.

First install dependencies. Note this can be done on the screenshare VM (as opposed to the template VM). We won’t need these again after compiling dwm.

sudo dnf install libX11-devel libXft-devel libXinerama-devel

Next, compile and install dwm. It will be installed in /usr/local, which lives in screenshare app VM, not the template.

[user@screenshare ~]$ git clone https://git.suckless.org/dwm
[user@screenshare ~]$ cd dwm
[user@screenshare ~]$ make
[user@screenshare ~]$ sudo make install

vncserver Configuration

Edit ~/.vnc/config to make vncserver easier to start on the command line:

securitytypes=none
alwaysshared
listen=tcp

(I’m counting on Qubes’ firewall to keep the environment reasonably secure, despite the insecure settings shown here.)

Also edit ~/.vnc/xstartup. Here’s what I ended up with:

#!/bin/sh

# Use `xhost +` to allow Qubes app VMs to access this vnc session.
xhost +

# The following from the dwm README.
while xsetroot -name "`date` `uptime | sed 's/.*,//'`"
do
    sleep 1
done &
exec dwm

Then run the VNC server and viewer…

[user@screenshare ~]$ vncserver
[user@screenshare ~]$ vncviewer :1

(If you prefer to enable vncserver as a service that starts automatically, the ArchWiki comes through with a detailed HOWTO for TigerVnc.)

Test

Perform two quick tests to make sure the VNC environment is OK. First from the machine where vncserver is running…

[user@screenshare ~]$ DISPLAY=:1 xterm

Next from another app VM. Change the IP address shown below to be your screenshare VM…

[user@another-vm ~]$ DIPLAY=10.137.0.33:1 xterm

If the first test works, but the second does not, you’ll need to troubleshoot the inter-vm firewall settings, and make sure you’ve run xhost +.

Finally, Screenshare

Now use the $DISPLAY environment to start up applications that you want to share in a conference. For example, start a browser:

[user@another-vm ~]$ DISPLAY=10.137.0.33:1 firefox

In that browser, navigate to a conference website and join a meeting. Screensharing should work, and only the windows displayed in the VNC environment will be visible in the screenshare.

Thanks to Qubes, you’ve isolated the shared windows from others securely visible only to you!