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
xsel utility and a couple of lines of bash code can solve this
1 CLIP1="" 2 CLIP2=`xsel -o -b --display :1` 3 while 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 15 done
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
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
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
1 mkdir -p /home/prisoner/.config/xfce4 2 chown prisoner:prisoner -R /home/prisoner 3 XINITRC=/home/prisoner/.config/xfce4/xinitrc 4 rm -f $XINITRC 5 6 echo -e "#!/bin/sh\n\n" >> $XINITRC 7 echo -e "export PULSE_SERVER=$PULSE_SERVER\n\n" >> $XINITRC 8 tail -n +2 /etc/xdg/xfce4/xinitrc >> $XINITRC 9 10 sudo -u prisoner startxfce4
This code prepends the appropriate
export statement to XFCE's
and assumes that the
PULSE_SERVER variable is known inside the container:
I also disable the local PulseAudio server in XFCE. It has strange interactions with the one running on the host.
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.,
- forward USB devices if they are present (i.e.,
- 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!