Using LXC containers to (partially) replace Virtual Machines
Note: This post is the first that we write in English and even though till now all of them were written in Spanish, from now on we will publish content in both languages.
It doesn’t matter if it’s for doing pentests or participating in CTFs, preparing Virtual Machines for different systems and tools is a recurrent task.
For full blown virtualization the two main options are Virtual Box and the multiple flavors of VMWare, but (being an Ubuntu fan) I’ve been lately experimenting with LinuX Containers (LXC), a technology for Linux-based systems sometimes described as:
“something in the middle between a chroot and a full fledged virtual machine”
“lightweight virtualization technology. They are more akin to an enhanced chroot than to full virtualization like VMware or QEMU. They do not emulate hardware and containers share the same operating system as the host.”
Each option has its advantages and disadvantages:
- There is almost no overhead in running a container, since everything is managed by the host kernel.
- LXC containers use the host kernel and they are limited to Linux, so no Windows or OSX containers are feasible.
After this short introduction, in this post I will describe:
- How to create a Kali-like unprivileged container starting from a Debian container.
- How to “passthrough” devices to a container, like for example an USB RTL-SDR.
- How to run GUI applications in a container.
- Some general tips & tricks.
Everything explained here assumes that you are running an Ubuntu 14.04 (or a 12.04 with the 3.13 kernel) based host using the 1.0.x LXC branch (use this ppa for 12.04).
Most of the information showed here is just a recompilation from all this sources:
Configuring the host
We are gonna need at least to install this packages to host containers:
sudo apt-get install lxc systemd-services uidmap
And to be able to use unprivileged containers, we will have to follow the instructions posted by Stéphane Graber, one of the main maintainers of the project, in this post:
That can be resumed in:
Adding sub-UIDs & sub-GIDs for the current user with the following commands:
sudo usermod --add-subuids 100000-165536 $USER && \
sudo usermod --add-subgids 100000-165536 $USER && \
sudo chmod +x $HOME
Setting the default configuration for LXC in file ~/.config/lxc/default.conf:
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
And allowing the use of 10 virtual network-interfaces in the non-privileged containers adding this to file /etc/lxc/lxc-usernet:
$USER veth lxcbr0 10
Now that our system is ready to create unprivileged containers, we can do create our first one. For example, we can download a image from the official repository to create our container with:
lxc-create --template download --name wheezy-kali
And choose, for this example: debian as distribution, wheezy as release and amd64 as architecture.
Once the image has finished downloading and the container has been created, we can start it in non-interactive mode with:
lxc-start --name wheezy-kali --daemon
And then install some basic packages, like OpenSSH and support for X11 forwarding, directly from the host:
lxc-attach --name wheezy-kali -- apt-get update
lxc-attach --name wheezy-kali -- apt-get dist-upgrade -y
lxc-attach --name wheezy-kali -- apt-get install openssh-server xauth -y
We can list the basic information from containers (like their IPs) with this command:
Transforming a Debian container into Kali
To access the container we can use SSH, but first we will need to access it this way:
lxc-attach --name wheezy-kali
And set a password for the root user.
Once connected (or attached) to the Debian container, we can proceed with the apt sources modification, editing the /etc/apt/sources.list file to add the Kali repositories:
#deb [arch=amd64] http://http.debian.net/debian wheezy main contrib non-free #deb [arch=amd64] http://security.debian.org/ wheezy/updates main contrib non-free deb [arch=amd64] http://http.kali.org/kali kali main non-free contrib deb-src http://http.kali.org/kali kali main non-free contrib deb [arch=amd64] http://security.kali.org/kali-security kali/updates main contrib non-free
And then updating our local copy with:
But an error will tell us that we don’t have the public key ID to verify the signatures of the packages in that repositories, so we should import and add it to apt with these commands:
gpg --keyserver pgp.mit.edu --recv-keys ED444FF07D8D0BF6
gpg --armor --export ED444FF07D8D0BF6 | apt-key add -
If we update the software sources now, the error will be gone and we can finally turn the container into a Kali one:
apt-get update && apt-get install kali-archive-keyring && apt-get dist-upgrade -y
As explained in multiple sources:
To passthrough devices we have to add lines like these ones in the config file of the containers (.local/share/lxc/container_name for unprivileged containers):
lxc.cgroup.devices.allow = c X:* rwm
lxc.mount.entry = /host_dir none bind,optional,create=dir
Using the minor and major nodes of the device/s that we want to grant access to.
Note: To find out the minor and major nodes of a device we can execute this on the host:
ls -lisah /sys/dev/*
For example, for using a RTL-SDR USB inside a container the following lines need to be added:
# RTL-SDR device
lxc.cgroup.devices.allow = c 189:* rwm
lxc.mount.entry = /dev/bus/usb/002 dev/bus/usb/002 none bind,optional,create=dir
lxc.mount.entry = /dev/rtl_sdr dev/rtl_sdr none bind,optional,create=file
Note: In the case of the RTL-SDR we will first have to configure the udev rules in the host system for it to work as a SDR:
And for an USB-to-serial converter I did have to add:
# USB-to-serial device
lxc.cgroup.devices.allow = c 188:0 rw
The only caveat with containers hardware passthrough is that anything that needs a modified kernel will only work if the host Kernel is modified.
Running GUI applications
Apart from running X11 applications trough a SSH we can also do it the “native” way, letting the unprivileged container access to the video-card devices (and even the audio server). For more information check this sources:
Tips & Tricks
Systemd based guests
When trying to containerize systemd based guests (like Arch Linux or Debian Jessie) we will need to use the current version of LXC (1.1.2 as of right now) and additional tools (like lxcfs). To get all those in supported versions of Ubuntu we will need to use the following PPA:
It should be noted that unprivileged containers and systemd are not working flawlessly right now, but as stated here:
More recent Debian is hitting a few systemd issues along the way but it eventually boots and seems functional, this will get resolved as Debian either updates to a more recent systemd or cherry-picks the various container fixes we committed to systemd upstream.
It should improve over time.
Offering network services from a container
To allow a service running inside a container to be reached from a network were the host is connected, we should add an iptables rule like this one in the host:
iptables --table nat --append PREROUTING --protocol tcp --in-interface eth0 --destination-port host_port --jump DNAT --to-destination $CONTAINER_IP:$CONTAINER_PORT
Bug in ZAP starting script (and maybe other Kali applications)
ZAP in Kali contains a bug that prevents it from starting in a host with a kernel different from the one that comes with Kali. In the file /usr/bin/zap the architecture is obtained with:
ARCH=`uname -r|cut -d "-" -f 3`
But a better way would be:
But until they fix it (https://bugs.kali.org/view.php?id=2238), we can modify the corresponding comparison for our architecture:
-elif [ $ARCH == amd64 ]
+elif [ $ARCH == x86_64 ]
Recommended packages (for the forgetful)
In every Debian-based system that I use I like to install some essential packages to fight my poor memory:
apt-get install bash-completion command-not-found