Sandboxed development with LXC

This tutorial allows you to easily run commands through an LXC container.

When hacking on various applications it is often a requirement to install various dependencies and make other changes to your system to run these programs. I have found this often ends up creating an unstable system, and over time things start breaking. By using an LXC container, all these dependencies can be installed within a sandboxed system without affecting your host environment.

For examples of how this works have a look at the Usage section.

Initial Setup

First install LXC with you preferred package manner.

Then run the following commands, while replacing USER with your username:

sudo usermod --add-subuids 100000-165536 USER
sudo usermod --add-subgids 100000-165536 USER
echo 'USER veth lxcbr0 10' | sudo tee -a /etc/lxc/lxc-usernet

Create ~/.config/lxc/default.conf (again, changing USER):

lxc.network.type = veth
lxc.network.link = lxcbr0
lxc.network.flags = up
lxc.network.hwaddr = 00:16:3e:xx:xx:xx
lxc.id_map = u 0 100000 65536
lxc.id_map = g 0 100000 65536

# GUI
lxc.mount.entry = /dev/dri dev/dri none bind,optional,create=dir
lxc.mount.entry = /dev/snd dev/snd none bind,optional,create=dir
lxc.mount.entry = /tmp/.X11-unix tmp/.X11-unix none bind,optional,create=dir
lxc.mount.entry = /dev/video0 dev/video0 none bind,optional,create=file

lxc.hook.pre-start = /home/USER/.local/share/lxc/setup-pulse.sh

Then create ~/.local/share/lxc/setup-pulse.sh:

#!/bin/sh
PULSE_PATH=$LXC_ROOTFS_PATH/home/ubuntu/.pulse_socket

if [ ! -e "$PULSE_PATH" ] || [ -z "$(lsof -n $PULSE_PATH 2>&1)" ]; then
    pactl load-module module-native-protocol-unix auth-anonymous=1 \
        socket=$PULSE_PATH
fi

Create A Container

To create the container itself, run:
lxc-create -t download -n mygui -- -d ubuntu -r bionic -a amd64

Replace the lxc.idmap lines in ~/.local/share/lxc/mygui/config with:

lxc.id_map = u 0 100000 1000
lxc.id_map = g 0 100000 1000
lxc.id_map = u 1000 1000 1
lxc.id_map = g 1000 1000 1
lxc.id_map = u 1001 101001 64535
lxc.id_map = g 1001 101001 64535

lxc.mount.entry = /home/USER/MYPROJECTS home/ubuntu/mnt none bind,create=dir

Changing /home/USER/MYPROJECTS to the path where your projects are stored (or whichever folder you want to be accessible in the container).

Finish setting up the container by running these commands:

sudo chown -R 1000:1000 ~/.local/share/lxc/mygui/rootfs/home/ubuntu
echo 'export PULSE_SERVER=/home/ubuntu/.pulse_socket' >> ~/.local/share/lxc/mygui/rootfs/home/ubuntu/.profile
lxc-start -n mygui -d
lxc-attach -n mygui -- apt install pulseaudio -y
echo "enable-shm = no" | lxc-attach -n mygui -- tee -a /etc/pulse/client.conf
lxc-attach -n mygui -- su - ubuntu -c 'pulseaudio -k'
lxc-stop -n mygui

Create Shortcut

Now we create our shortcut command.

Download the shortcut script.
Replace /home/USERNAME/MYPROJECTS at the top of the file with the same path you used earlier.
Save it to /usr/local/bin/lr.

Finally, make it executable and create a symlink:

sudo chmod +x /usr/local/bin/lr
sudo ln -s /usr/local/bin/lr /usr/local/bin/lrs

Usage

Now you have an LXC container to use, you can easily run programs you are working on with the shortcuts. The lr command can be used to run anything as a normal user within the LXC container, and when used within your shared folder will be run from the same location. To test this, try running:

cd ~/MYPROJECTS
ls
lr ls

The last command will run the ls command inside your LXC container, but both ls commands should produce the same output, as the lr will make sure the commands are run from the same location in the shared folder.


Additionally, we have the lrs command which can be thought of as lr sudo and will run any command as root. Try installing and running Firefox in your container:

lrs apt install firefox
lr firefox


As an example for using this as a development tool, if you wanted to run a Java app without installing Java on your host machine, you could do so like this:

First, install the dependencies:
lrs apt install openjdk-11-jre

Then run the code from our projects folder:
cd ~/MYPROJECTS
lr java -jar myjavaprogram.jar

Resetting

If you’ve messed up your LXC container and want to wipe it. Just run:

lxc-destroy -n mygui

Then repeat the create a container section.

One thought on “Sandboxed development with LXC

Leave a Reply

Your email address will not be published. Required fields are marked *