Skip to content
This repository was archived by the owner on Jun 24, 2023. It is now read-only.

Commit 314b1ee

Browse files
committed
Initial commit
Effectively solve the following issues: QubesOS/qubes-issues#4035 QubesOS/qubes-issues#2079 QubesOS/qubes-issues#6426
0 parents  commit 314b1ee

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+2207
-0
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
*.swp
2+
*.gz
3+
__pycache__

.gitlab-ci.yml

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Copyright (C) 2021 Elliot Killick <[email protected]>
2+
# Licensed under the MIT License. See LICENSE file for details.
3+
4+
stages:
5+
- Static Analysis
6+
7+
shellcheck:
8+
image: koalaman/shellcheck-alpine:latest
9+
stage: Static Analysis
10+
before_script:
11+
- shellcheck --version
12+
script:
13+
- bash -c 'shopt -s globstar nullglob; shellcheck --external-sources --source-path=SCRIPTDIR **/*.sh'
14+
15+
pylint:
16+
image: python:latest
17+
stage: Static Analysis
18+
before_script:
19+
- python --version
20+
- pylint --vesrion
21+
- pip install -r ci/requirements.txt
22+
script:
23+
- bash -c 'shopt -s globstar nullglob; pylint3 --exit-zero **/*.py'

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (C) 2021 Elliot Killick <[email protected]>
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

Makefile

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Copyright (C) 2021 Elliot Killick <[email protected]>
2+
# Licensed under the MIT License. See LICENSE file for details.
3+
4+
PKGNAME = qubes-video-companion
5+
6+
BINDIR ?= /usr/bin
7+
DATADIR ?= /usr/share
8+
QREXECDIR ?= /etc/qubes-rpc
9+
10+
INSTALL_DIR = install -d
11+
INSTALL_PROGRAM = install -D
12+
INSTALL_DATA = install -Dm 644
13+
14+
help:
15+
@echo "make build Build components"
16+
@echo "make install-vm Install all components necessary for VMs"
17+
@echo "make install-dom0 Install all components necessary for dom0"
18+
@echo "make install-both Install components necessary for VMs and dom0"
19+
@echo "make install-policy Install qrexec policies"
20+
@echo "make install-license Install license to $(DATADIR)/licenses/$(PKGNAME)"
21+
@echo "make clean Clean build"
22+
23+
build:
24+
$(MAKE) -C doc manpages
25+
26+
install-vm: install-both
27+
$(INSTALL_DIR) $(DESTDIR)$(BINDIR)
28+
$(INSTALL_PROGRAM) video/$(PKGNAME) $(DESTDIR)$(BINDIR)
29+
$(INSTALL_DIR) $(DESTDIR)$(DATADIR)/$(PKGNAME)/video
30+
$(INSTALL_PROGRAM) video/setup.sh video/receiver.sh video/destroy.sh video/common.sh $(DESTDIR)$(DATADIR)/$(PKGNAME)/video
31+
$(INSTALL_DATA) scripts/webcam.html $(DESTDIR)$(DATADIR)/$(PKGNAME)/scripts
32+
$(INSTALL_DIR) $(DESTDIR)$(DATADIR)/$(PKGNAME)/scripts/v4l2loopback
33+
$(INSTALL_PROGRAM) scripts/v4l2loopback/install.sh $(DESTDIR)$(DATADIR)/$(PKGNAME)/scripts/v4l2loopback
34+
$(INSTALL_DATA) scripts/v4l2loopback/author.asc $(DESTDIR)$(DATADIR)/$(PKGNAME)/scripts/v4l2loopback
35+
$(MAKE) -C doc install
36+
37+
install-dom0: install-both install-policy
38+
39+
install-both:
40+
$(INSTALL_DIR) $(DESTDIR)$(QREXECDIR)
41+
$(INSTALL_PROGRAM) qubes-rpc/services/qvc.Webcam qubes-rpc/services/qvc.ScreenShare $(DESTDIR)$(QREXECDIR)
42+
$(INSTALL_DIR) $(DESTDIR)$(DATADIR)/$(PKGNAME)/ui
43+
$(INSTALL_PROGRAM) ui/*.py ui/*.sh $(DESTDIR)$(DATADIR)/$(PKGNAME)/ui
44+
$(INSTALL_DIR) $(DESTDIR)$(DATADIR)/$(PKGNAME)/scripts
45+
$(INSTALL_PROGRAM) scripts/set-webcam-format.sh $(DESTDIR)$(DATADIR)/$(PKGNAME)/scripts
46+
$(INSTALL_DIR) $(DESTDIR)$(DATADIR)/doc/$(PKGNAME)
47+
$(INSTALL_DATA) README.md doc/pipeline-design.md $(DESTDIR)$(DATADIR)/doc/$(PKGNAME)
48+
$(INSTALL_DIR) $(DESTDIR)$(DATADIR)/doc/$(PKGNAME)/visualizations
49+
$(INSTALL_DATA) doc/visualizations/* $(DESTDIR)$(DATADIR)/doc/$(PKGNAME)/visualizations
50+
51+
install-policy:
52+
$(INSTALL_DIR) $(DESTDIR)$(QREXECDIR)/policy
53+
$(INSTALL_DATA) qubes-rpc/policies/* $(DESTDIR)$(QREXECDIR)/policy
54+
55+
install-license:
56+
$(INSTALL_DIR) $(DESTDIR)$(DATADIR)/licenses/$(PKGNAME)
57+
$(INSTALL_DATA) LICENSE $(DESTDIR)$(DATADIR)/licenses/$(PKGNAME)
58+
59+
clean:
60+
$(MAKE) -C doc clean

README.md

+213
Large diffs are not rendered by default.

ci/requirements.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# WARNING: These requirements are for use by the CI system only (gitlab.com)
2+
# Otherwise, use the system package manager to ensure utmost security
3+
PyGObject

debian/changelog

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
qubes-video-companion (1.0.0-1) unstable; urgency=low
2+
3+
* Initial release
4+
5+
-- Elliot Killick <[email protected]> Thu, 18 Feb 2021 11:40:26 -0500

debian/control

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
Source: qubes-video-companion
2+
Section: video
3+
Priority: optional
4+
Maintainer: Elliot Killick <[email protected]>
5+
Build-Depends: debhelper-compat (= 12), pandoc
6+
Standards-Version: 4.5.0
7+
Homepage: https://github.com/elliotkillick/qubes-video-companion
8+
9+
Package: qubes-video-companion
10+
Architecture: all
11+
Multi-Arch: foreign
12+
Depends: gir1.2-ayatanaappindicator3-0.1,
13+
gstreamer1.0-plugins-good,
14+
gstreamer1.0-tools,
15+
python3,
16+
${misc:Depends}
17+
Description: Securely stream webcams and share screens across virtual machines
18+
Qubes Video Companion is a tool for securely streaming webcams and sharing
19+
screens across virtual machines.
20+
.
21+
It accomplishes this by creating a uni-directional flow of raw video that is
22+
passed from one virtual machine to another through file descriptors thereby
23+
allowing both machines to be completely air-gapped with no networking stacks
24+
exposed. This design makes the side of the video sending virtual machine 100%
25+
immune to attack and only leaves a very small attack surface on the side of the
26+
video receiving virtual machine.
27+
.
28+
The project emphasizes correctness and security all the while also sporting
29+
superb performance by maintaining a small footprint of the available
30+
computational resources and low latency even at Full HD and greater resolutions
31+
at 30 or more frames per second.
32+
.
33+
This package contains all components of Qubes Video companion excluding the
34+
Qubes RPC policies which dom0 enforces.

debian/copyright

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
2+
Upstream-Name: Qubes Video Companion
3+
Upstream-Contact: Elliot Killick <[email protected]>
4+
Source: https://github.com/elliotkillick/qubes-video-companion
5+
6+
Files: *
7+
Copyright: 2021 Elliot Killick <[email protected]>
8+
License: MIT
9+
MIT License
10+
.
11+
Copyright (C) 2021 Elliot Killick <[email protected]>
12+
.
13+
Permission is hereby granted, free of charge, to any person obtaining a copy
14+
of this software and associated documentation files (the "Software"), to deal
15+
in the Software without restriction, including without limitation the rights
16+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17+
copies of the Software, and to permit persons to whom the Software is
18+
furnished to do so, subject to the following conditions:
19+
.
20+
The above copyright notice and this permission notice shall be included in all
21+
copies or substantial portions of the Software.
22+
.
23+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29+
SOFTWARE.

debian/rules

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/usr/bin/make -f
2+
3+
%:
4+
dh $@
5+
6+
override_dh_auto_install:
7+
make install-vm DESTDIR=$(shell readlink -f .)/debian/qubes-video-companion

debian/source/format

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.0 (quilt)

doc/Makefile

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Copyright (C) 2021 Elliot Killick <[email protected]>
2+
# Licensed under the MIT License. See LICENSE file for details.
3+
4+
DATADIR ?= /usr/share
5+
MANDIR ?= $(DATADIR)/man
6+
MAN1DIR ?= $(MANDIR)/man1
7+
8+
INSTALL_DIR = install -d
9+
INSTALL_DATA = install -Dm 644
10+
11+
MANPAGES=$(patsubst %.rst,%.1.gz,$(wildcard *.rst))
12+
13+
help:
14+
@echo "make manpages Generate manpages"
15+
@echo "make install Generate manpages and install them to $(MAN1DIR)"
16+
17+
manpages: $(MANPAGES)
18+
19+
%.1: %.rst
20+
pandoc -s -f rst -t man -o $@ $<
21+
22+
%.1.gz: %.1
23+
gzip -f $<
24+
25+
install: manpages
26+
$(INSTALL_DIR) $(DESTDIR)$(MAN1DIR)
27+
$(INSTALL_DATA) $(MANPAGES) $(DESTDIR)$(MAN1DIR)
28+
29+
clean:
30+
rm -f $(MANPAGES)

doc/pipeline-design.md

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Video Senders (Qubes RPC Services)
2+
3+
## -q (quiet)
4+
### gst-launch-1.0 needs to be quiet because the debug messages are printed on standard output instead of standard error
5+
- This means the debug info will become part of the video output upon being sent to the `fdsink` element which is unwanted behavior that results in video artifacts
6+
7+
## queue
8+
### Force push mode scheduling which is better for a constant stream of data
9+
- https://gstreamer.freedesktop.org/documentation/additional/design/scheduling.html
10+
11+
## format=I420
12+
### This pixel format was chosen for two reasons
13+
1. I420 seemed to be the default output format for a lot of elements and was always listed at the top of pixel format lists in the GStreamer documentation as well as over places such as on the official FOURCC website which defines these pixel formats
14+
- So, presumably I420 is the most battle-hardened format which is least likely to contain bugs
15+
- GStreamer documentation mentions to also prefer I420 over YV12 which are the same but with the V and U planes switched around
16+
2. I420 is directly compatible with many GStreamer elements including `v4l2sink` without needing a leading `videoconvert` (probably followed by a `tee`)
17+
- This greatly improves performance and security
18+
- https://gstreamer.freedesktop.org/documentation/additional/design/mediatype-video-raw.html
19+
- https://gstreamer.freedesktop.org/documentation/application-development/basics/elements.html
20+
- https://www.fourcc.org/yuv.php
21+
22+
## use-damage=false in `qvc.ScreenShare`
23+
### XDamage causes `ximagesrc` to send out small updates about what parts of the screen has changed as opposed to just sending the whole screen
24+
- This may be preferable on a network because of the decrease in bandwidth and latency but otherwise it just results in very high CPU usage and doesn't fit our use case
25+
26+
27+
## BGRx -> I420 pixel format `videoconvert` in `qvc.ScreenShare`
28+
### `ximagesrc` only outputs in BGRx so it must be converted to I420 which is a supported input format for `v4l2sink` (on the `receiver.sh` side)
29+
- This video conversion is done on the side of the sending machine as to ensure the attack surface of the recipient stays as small as possible
30+
31+
# Video Receiver (`receiver.sh`)
32+
33+
## capsfilter
34+
### This is used to limit our attack surface to the given capabilities
35+
- All the capabilities after the colorimetry are technically unnecessary for this to be functional but are used to limit our attack surface
36+
- This filter accounts for all the capabilities possible on a raw video stream according to the below documentation
37+
- https://gstreamer.freedesktop.org/documentation/coreelements/capsfilter.html
38+
- https://gstreamer.freedesktop.org/documentation/additional/design/mediatype-video-raw.html
39+
- https://gstreamer.freedesktop.org/documentation/application-development/basics/pads.html#what-capabilities-are-used-for
40+
41+
## colorimetry=2:4:7:1
42+
### This is the default colorimetry format for I420 and the only one that works with it without having to specify a `chroma-site`
43+
- Having `chroma-site` set to `none` reduces our attack surface and likely improves our performance as well
44+
45+
## use-sink-caps=true
46+
### Use the capabilities defined by the previous element, in this case, what is explicitly defined by the capsfilter
47+
48+
## sync=false
49+
### Disable syncing video to the system clock
50+
- This fixes the frame jittering/lagging that happens when passing raw video between machines
51+
- `sync=false` is the default on the `fdsink` sink on the video sender
52+
- Tried making both sender and receiver `sync=true` but that resulted in jittering again
53+
- Also tried making only the `fdsink` sink on the sender `sync=true` and the `v4l2sink` sink on the receiver `sync=false` but that resulted in higher CPU usage and greater video latency
54+
- Some times it takes some time to start jittering/lagging depending on what the video source is but it will happen
55+
- I think it's because the two VMs are on slightly different clocks so trying to synchronize to that doesn't work out very well
56+
- This fix isn't necessary when passing raw video directly between file descriptors on the same machine
57+
- This may be better fixed by passing timestamp information through the raw video itself
58+
- However, it appears to be that raw video is incapable of holding timestamp information
59+
- Timestamping is done on raw video in the GStreamer pipeline but once it leaves there (through `fdsink`) any timestamping information may be void (unsure)
60+
- Although, research has found a few somewhat hacky solutions for FFmpeg that allowed for timestamping on raw video; perhaps GStreamer has something similar
61+
- Or maybe we could synchronize the clocks of the machines better thus allowing us to remove `sync=false`
62+
- Or even just synchronize the clocks that the GStreamer processes see on each machine right before running them
63+
- Perhaps using the `datefudge` or `faketime` command which uses `LD_PRELOAD` to manipulate the system time for a given command

doc/qubes-video-companion.rst

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
=====================
2+
qubes-video-companion
3+
=====================
4+
5+
NAME
6+
====
7+
qubes-video-companion - securely stream webcams and share screens across virtual machines
8+
9+
SYNOPSIS
10+
========
11+
| qubes-video-companion <video_source>
12+
13+
DESCRIPTION
14+
===========
15+
Qubes Video Companion is a tool for securely streaming webcams and sharing screens across virtual machines.
16+
17+
It accomplishes this by creating a uni-directional flow of raw video that is passed from one virtual machine to another through file descriptors thereby allowing both machines to be completely air-gapped with no networking stacks exposed. This design makes the side of the video sending virtual machine 100% immune to attack and only leaves a very small attack surface on the side of the video receiving virtual machine.
18+
19+
The project emphasizes correctness and security all the while also sporting superb performance by maintaining a small footprint of the available computational resources and low latency even at Full HD and greater resolutions at 30 or more frames per second.
20+
21+
OPTIONS
22+
=======
23+
video_source
24+
The video source to stream and receive video from. Either "webcam" or "screenshare".
25+
26+
AUTHORS
27+
=======
28+
| Elliot Killick <elliotkillick at zohomail dot eu>
22.5 KB
Binary file not shown.

doc/visualizations/qvc.Webcam.pdf

22.3 KB
Binary file not shown.
Binary file not shown.
23.4 KB
Binary file not shown.

icons/logo.png

48.3 KB
Loading

qubes-rpc/policies/qvc.ScreenShare

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
@anyvm @dispvm allow
2+
@anyvm @anyvm ask
3+
@anyvm dom0 ask

qubes-rpc/policies/qvc.Webcam

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
@anyvm sys-usb ask
2+
@anyvm dom0 ask

qubes-rpc/services/qvc.ScreenShare

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#!/bin/bash
2+
3+
# Copyright (C) 2021 Elliot Killick <[email protected]>
4+
# Licensed under the MIT License. See LICENSE file for details.
5+
6+
[ "$DEBUG" == 1 ] && set -x
7+
8+
set -E # Enable function inheritance of traps
9+
trap exit ERR
10+
11+
export DISPLAY=":0"
12+
13+
first_connected_screen_input_dimensions="$(xrandr | grep -w "connected" | head -1 | cut -d '+' -f 1 | awk '{ print $NF }')"
14+
15+
width="$(echo "$first_connected_screen_input_dimensions" | awk -F x '{ print $1 }')"
16+
height="$(echo "$first_connected_screen_input_dimensions" | awk -F x '{ print $2 }')"
17+
18+
fps="30"
19+
frame_rate="$fps/1"
20+
21+
echo "$width $height $fps"
22+
23+
echo "Starting screen sharing at ${width}x${height} ${fps} FPS..." >&2
24+
25+
source "/usr/share/qubes-video-companion/ui/ui.sh"
26+
trap 'remove_ui $!' EXIT
27+
create_ui screenshare
28+
29+
gst-launch-1.0 -q ximagesrc use-damage=false ! \
30+
queue ! \
31+
"video/x-raw,width=$width,height=$height,framerate=$frame_rate,format=BGRx" ! \
32+
videoconvert ! \
33+
"video/x-raw,format=I420" ! \
34+
fdsink

qubes-rpc/services/qvc.Webcam

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/bin/bash
2+
3+
# Copyright (C) 2021 Elliot Killick <[email protected]>
4+
# Licensed under the MIT License. See LICENSE file for details.
5+
6+
[ "$DEBUG" == 1 ] && set -x
7+
8+
set -E # Enable function inheritance of traps
9+
trap exit ERR
10+
11+
webcam_device="/dev/video0"
12+
# This command exits with code 255 even though no error is reported
13+
v4l2_webcam_info="$(v4l2-ctl -d "$webcam_device" --all)" || [ $? == 255 ]
14+
15+
dimensions="$(echo "$v4l2_webcam_info" | grep 'Width/Height' | awk '{ print $3 }')"
16+
width="$(echo "$dimensions" | awk -F / '{ print $1 }')"
17+
height="$(echo "$dimensions" | awk -F / '{ print $2 }')"
18+
19+
frame_rate="$(v4l2-ctl -d "$webcam_device" --all | grep 'Frames per second' | awk '{ print $5 }' | tr -d '()')"
20+
fps="$(echo "$frame_rate" | awk -F / '{ print $1 }')" # Support a minimum of one frame per second
21+
22+
echo "$width $height $fps"
23+
24+
echo "Starting webcam stream at ${width}x${height} ${fps} FPS..." >&2
25+
26+
source "/usr/share/qubes-video-companion/ui/ui.sh"
27+
trap 'remove_ui $!' EXIT
28+
create_ui webcam
29+
30+
gst-launch-1.0 -q v4l2src ! \
31+
queue ! \
32+
"video/x-raw,width=$width,height=$height,framerate=$frame_rate,format=I420" ! \
33+
fdsink

0 commit comments

Comments
 (0)