Intro
Life would be so much easier if all the software was open source and came packaged with Debian. Much of the stuff I use is available this way, but there are still some programs that come as binary blobs with custom installers. I don't like that. I don't trust that. Every now and then, you hear the stories of software coming from reputable companies and misbehaving in dramatic ways. It would be great to contain the potential damage, but running virtual machines on a laptop is a pain. As it turns out, things may work pretty well with Docker, but as usual, the configuration is not so trivial.
The X Server
The solutions I found on the Internet either share the main X server with the container or use VNC. The first approach is problematic because apparently the X architecture has been designed by happy hippies and has no notion of security. If two applications share a screen, for instance, one can sniff the keystrokes typed into the other, and all this is by design. The VNC solution, on the other hand, is terribly slow: windows smudge when moved, and the Netflix playback is lagging.
Starting a Xephyr instance on the host and sharing its socket with the container seems to solve the sniffing problem. The programs running inside the container can't listen to the keystrokes typed outside of it anymore. Xephyr is also fast enough to handle high-resolution movie playback smoothly.
You can start Xephyr like this:
Xephyr :1 -ac -br -screen 1680x1050 -resizeable
The server will run as display :1
in a resizable window of initial size
defined by the screen
parameter. Adding the following to the Docker command
line makes the server visible inside of the container:
-e DISPLAY=:1 -v /tmp/.X11-unix/X1:/tmp/.X11-unix/X1
The only remaining pain point is the fact that you cannot share the clipboard by
default. Things copied outside of the container do not paste inside and vice
versa. The xsel
utility and a couple of lines of bash code can solve this
problem easily:
1CLIP1=""
2CLIP2=`xsel -o -b --display :1`
3while true; do
4 CLIP1NEW=`xsel -o -b --display :0`
5 CLIP2NEW=`xsel -o -b --display :1`
6 if [ "x$CLIP1" != "x$CLIP1NEW" ]; then
7 xsel -o -b --display :0 | xsel -i -b --display :1
8 CLIP1=$CLIP1NEW
9 fi;
10 if [ x"$CLIP2" != x"$CLIP2NEW" ]; then
11 xsel -o -b --display :1 | xsel -i -b --display :0
12 CLIP2=$CLIP2NEW
13 fi
14 sleep 1
15done
Audio
Making the audio work both ways (the sound and the microphone) is
surprisingly easy with PulseAudio. The host just needs to configure the
native protocol plug-in and ensure that port 4713
is not blocked by the
firewall:
pactl load-module module-native-protocol-tcp auth-ip-acl=172.17.0.2
All you need to do in the container is making sure that the PULSE_SERVER
envvar points to the host. It is less straightforward than you might expect when
you run a desktop environment and don't want to start all your programs in a
terminal window. For XFCE, I do the following in the script driving the
container:
1mkdir -p /home/prisoner/.config/xfce4
2chown prisoner:prisoner -R /home/prisoner
3XINITRC=/home/prisoner/.config/xfce4/xinitrc
4rm -f $XINITRC
5
6echo -e "#!/bin/sh\n\n" >> $XINITRC
7echo -e "export PULSE_SERVER=$PULSE_SERVER\n\n" >> $XINITRC
8tail -n +2 /etc/xdg/xfce4/xinitrc >> $XINITRC
9
10sudo -u prisoner startxfce4
This code prepends the appropriate export
statement to XFCE's xinitrc
file
and assumes that the PULSE_SERVER
variable is known inside the container:
-e PULSE_SERVER=172.17.42.1
I also disable the local PulseAudio server in XFCE. It has strange interactions with the one running on the host.
Jail
Doing all this by hand every time you want to run the container is way too painful. I wrote a script to automate the process. It can:
- set up the PulseAudio server
- forward static devices (i.e.,
/dev/video0
) - forward USB devices if they are present (i.e.,
21a9:1006
as/dev/bus/usb/001/016
) - run the Xephyr X server instance for the container
- forward the clipboards
- set up docker's command line to make everything work together
This is what it all looks like:
]==> jail.sh
[i] Running jail: 20161121-213528
[i] Container: jail:v01
[i] Hostname: jail
[i] Home: /home/ljanyst/Contained/jail/home
[i] Setting up the local PulseAudio server (172.17.42.1)... OK
[i] Attaching device /dev/video0
[i] USB device 21a9:1006 not present
[i] Running Xephyr display at :1 (1680x1050)... OK
[i] Running docker... OK
[i] Removing container a545365592c1... OK
[i] Killing clipboard forwarder, PID: 2776... DONE
[i] Killing Xephyr, PID: 2767... DONE
[i] All done. Bye!