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.

Terminal
Terminal

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!

Skype
Skype

Netflix
Netflix

Saleae Logic Analyzer
Saleae Logic Analyzer

If you like this kind of content, you can subscribe to my newsletter, follow me on Twitter, or subscribe to my RSS channel.