Hello, everyone!
Starting this past early September, I've been working on and off to
create a new tool for secure webcam integration in Qubes OS out of
absolute
necessity for remote work at both my (new) job and school at
the university I'm newly attending. The tool is called Qubes Video
Companion and I'm proud to announce that it's evolved far past that
basic requirement and at version 1.0.4 is now publicly available for
testing!
Before resorting to the creation of an entirely new project, I
thought of many other potential solutions for allowing me to use a
webcam in Qubes but all of them fell flat in one way or another, to
list them all (skip through this list if you're not up for too much
reading, haha):
1. Do video conferencing in the sys-usb qube with the webcam device
attached
Unfortunately, the desktop I run Qubes OS on doesn't have a sys-usb
qube due to complications of its hardware. The primary issue being
that I require a USB sound card which has to stay attached to dom0
where the other Qubes audio components are. Not to mention, this
solution is also far from being optimal from a security standpoint
particularly because I also use a USB mouse and keyboard meaning I
would essentially have to hand over control of those devices to
sys-usb. This leaves the door open for keyboard injection into dom0
thereby compromising it (and the entire system) in the case sys-usb
is compromised due to, for example, a bug in the video conferencing
software. This also means enabling networking (removing the air gap)
for sys-usb which again is harmful to security.
Note that I don't personally consider not having a sys-usb qube for
protecting dom0 from physical USB device attacks as much of a
security issue because my desktop is always at home which is outside
of my threat model.
2. Do video conferencing in any qube with qrexec+usbip (with a
sys-usb qube; can't be done from dom0)
Even disregarding my personal hardware issues above, qrexec+usbip is
a mess to get working and does poor on performance according to this
GitHub issue:
https://github.com/QubesOS/qubes-issues/issues/4035.
Although, I've never been able to test it out myself. Additionally,
the security concerns of using TCP/IP and USBIP between qubes is
there. This also remotely exposes the webcam firmware which is yet
another security risk with this configuration.
3. Do video conferencing in any qube with a bought USB hub PCI card
This also means taking on all the security risks of attaching that
physical hardware directly to the VMs which considering they are to
run applications I don't trust such as Zoom (which my job requires;
I wouldn't use Zoom given the choice of course) — doesn't sit well
with me at all. Plus, hot swapping PCI devices, though supported by
Xen, is disabled in Qubes because of what a complex operation it is
security-wise leaving the door open to bugs (I read this was the
reason somewhere I think in the Qubes mailing list I think). This
means that in order to use my webcam on different qubes (my "school"
and "work" qubes for my purposes) I would have to reboot them each
time to attach and remove the USB hub with my webcam plugged into
it. This would be very inconvenient for my use case particularly
when I'm trying to get work done efficiently. I thought about
combining my school and work qubes into just one qube but at that
point why am I even bothering to use Qubes OS if I don't utilize its
most basic security feature. Lastly, I could also buy multiple USB
hub PCI cards but that was just too hacky for me.
4. Do video conferencing in any qube with FFmpeg for streaming video
At the time, there were preliminary outlines of a working solution
for webcam video streaming with FFmpeg. While this was a good start,
it just didn't cut it for my needs. The resolution was very low
(when I tried to raise it to 1920x1090 it didn't work) and even with
that low resolution the latency was below optimal and it just tore
up my CPU power when I needed it the most; hence this too was not a
realistic solution. Plus, FFmpeg isn't available in Fedora without
adding RPM Fusion repo due to patent issues which was just one more
little gripe I had with it. And to top it off, FFmpeg doesn't
exactly have an amazing security track record...
5. Dual booting and other OSs
Finally, as a very last resort (not being able to use my webcam
wasn't an option for me), I thought about dual booting (way too
inconvenient not to mention the security downfalls) and (just for a
very brief moment for the sake of completeness) other OSs. However,
I have already contributed one project (qvm-create-windows-qube) to
Qubes OS and have now been "locked in" to the Qubes ecosystem (haha)
by fantastic features such as Qubes Split GPG, a great community as
well as countless other qualities and would feel insecure running
any other operating system.
As far as using a webcam in Qubes OS went, I needed something that
just
worked, I needed — Qubes Video Companion. And that's how this
project was born.
Add in screen sharing (a welcomed bonus), an unspoofable video
sharing tray indicator, packaging and all the documentation to boot,
and you've got what Qubes Video Companion has evolved into!
I've tested performance and although I haven't tried qrexec+usbip
with a webcam myself (reasons stated above), I can say that by
comparing CPU performance (using CPU model performance comparison
websites) of the CPU specs of other Qubes users and the performance
they are getting with qrexec+usbip versus what I'm getting with
Qubes Video Companion and my lower power, older CPU (some of you
guys are rocking beast setups or just plain laptops that outperform
my desktop, haha) it would seem to me that Qubes Video Companion
wins on performance by a wide margin. I also saw reports of
qrexec+usbip using a lot of RAM. Well, with Qubes Video Companion
that's no problem because from the moment I start the video stream
from my webcam (at 1920x1080 30 FPS) I only see a 30-40 MB increase
in RAM on the sending and receiving side then it remains stable at
that amount!
I've already been using Qubes Video Companion on a regular basis for
a couple of months now (in time for when my job contract began) and
it's been working amazingly even in a Zoom call with over 60 other
people I had absolutely no problems (just had to assign all 4 of my
vCPUs to that qube and up the memory or else Zoom would have
trouble)!
Repo can be found here:
https://github.com/elliotkillick/qubes-video-companion
I spent a lot of time trying to make this a "just works" (and well)
experience (discounting just a couple of hiccups in the FAQ of the
README and GitHub Issues) as I would've wanted back in September (or
earlier) so please star the repo if my project achieves that for
you. Otherwise, create an issue and I'll try to diagnose and fix the
problem you're having as soon as I have a moment.
One of the main advantages of the architecture of Qubes Video
Companion is that it depends on GStreamer as opposed to FFmpeg. I
found out about GStreamer in the v4l2loopback GitHub Issues and saw
it was being used as part of the "set-caps" functionality in the
"v4l2loopback-ctl" command. This interested me and upon further
research, it became increasingly apparent to me that GStreamer has
multiple benefits over FFmpeg in the context of this project all of
which are documented in the README.
The timeline for working on this project starting from September was
(as I mentioned) very on and off until early January at which point
I had figured out all the GStreamer and video related components.
Going from knowing nothing about GStreamer or Video4Linux to the
level of knowledge required to create this project was an involved
process to say the least mostly consisting of trial and error (tons
of it), reading GStreamer documentation and mailing lists, Stack
Overflow and countless hours of work. I almost gave up at one point,
but a couple of months later after having success with another
somewhat related mini project I was doing at the time, I persevered
and I'm glad I did because I'm very happy with how this project
turned out! In particular, I got wrapped up in one red herring based
on something I saw in the very verbose GST_DEBUG logs. That and
vague GStreamer error messages that appear to only be of any
assistance if you get into reading the underlying GStreamer code
(such as the very helpful "Internal data stream error" which I never
want to see again, haha). Of course, in retrospect looking at the
final solution now, it all seems so obvious and straightforward but
it sure wasn't at the time. After all that, it was just a matter of
making the UI (in which I applied some of the object-oriented
programming (OOP) experience I acquired in one of my university
classes), adding polishing, packaging and all the documentation to
boot from mid-January to now (although, the UI was a last minute
thing I decided I wanted to add somewhere in around March)!
As for contribution to the Qubes Community Repo (if that is to be
desired) I first want to fix the issue with Firefox (see the GitHub
issue) because Mozilla has been a great supporter of Qubes with the
big grant they gave so it's the least we could do as well as to not
put Firefox at a disadvantage.
Hope you're doing well!
Best regards,
Elliot
P.S. As part of making this project, I found myself needing to do
video conferencing in one of my Kali Linux qubes ("debian-10" qube
turned into a "kali" qube). Unfortunately, the audio and microphone
PulseAudio components were broken in that qube by one of the
packages Kali Linux installs by default. I was able to fix this
issue and plan on making a pull request to the Qubes documentation
so others can fix this issue if they run into it with their "kali"
qubes.
Also, if you're looking for a solution to enable screen sharing with
Zoom then check out my issue here (although I didn't solve the issue
myself):
https://github.com/QubesOS/qubes-issues/issues/5863