Unprivileged containers in Slackware©

Part 3 - Networking unprivileged containers

    In Part 1 of this Unprivileged containers in Slackware article, we discussed how to arrange the system requirements to run unprivileged containers in Slackware. In Part 2 we stepped through creation of a standard privilieged container and a method to convert it to an unprivileged container i.e. able to be started by an "ordinary" user. In this third part, we describe a convenient method to provide networking for an arbitrary number of containers.

At this point, it's assumed that parts 1 & 2 have been successfully digested and you're happily running an unprivileged container (although the networking method described here applies equally to normal privileged containers too). Our method is comprised of the following steps:

  1. create a bridge to the existing network of the host machine
  2. set up dhcp and dns to service the bridged network
  3. set up the container's network

Step 1 - Create bridge

We will create a single bridge network interface (which we'll name lxcbr0 in this example) to the host machine's default network interface (wlan0 in this example). The new lxcbr0 interface will then be configured any other network interface (using ifconfig) and finally we use iptables to create a NAT behind the lxcbr0 interface. Any containers can then be attached to the bridge interface and access the wider internet via the host machine.

The bridge is set up by executing (as root) the following commands:
   echo 1 > /proc/sys/net/ipv4/ip_forward
   /sbin/brctl addbr lxcbr0
   /sbin/brctl setfd lxcbr0 0
   /sbin/ifconfig lxcbr0 192.168.100.1 netmask 255.255.255.0 promisc up
   /usr/sbin/iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE

Rather than running all these commands by hand after any reboot of the host machine, it is better to incorporate them in a script that can be run whenever needed. For that purpose, try this rc.lxc-bridge script, or something similar, which can be kept in the /etc/rc.d/ directory along with other boot scripts. Some care is needed when rebooting that the script is not executed before the wlan0 interface is active. If using NetworkManager, it is very convenient to use its dispatcher mechanism to run the script only when wlan0 (or other network interface that you choose) becomes available. See this start_lxc-bridge script as an example. It should be kept in the /etc/NetworkManager/dispatcher.d/ directory. If both scripts are executable then whenever the wlan0 interface becomes available, the script to bring up the lxcbr0 interface will be run.

Step 2 - Set up DHCP & DNS

If you prefer to set up each new container's networking individually then this step is not needed. Otherwise we'll make use of dnsmasq, which will already be installed on stock Slackware machines, to provide DHCP and DNS services for the containers. We just need to provide a suitable configuration for dnsmasq and make its startup file, /etc/rc.d/rc.dnsmasq, executable:
   chmod a+x /etc/rc.d/rc.dnsmasq
Edit the file /etc/dnsmasq.conf file at about line 660; uncomment the line to be conf-dir=/etc/dnsmasq.d. This enables us to leave that file otherwise unaltered and put our particular configuration needs, modest though they are, into a dedicated file in the /etc/dnsmasq.d. Create that file (I suggest /etc/dnsmasq.d/lxc-bridge.conf) with the following content:
   interface=lxcbr0
   dhcp-range=192.168.100.2,192.168.100.254,2h
Of course the interface name and dhcp range values should be consistent with those chosen for LXCBR_NAME and LXCBR0_NAT_NET in the rc.lxc-bridge file in Step 1.

Reboot the host machine or manually run (as root):
   /etc/rc.d/rc.lxc-bridge start
   /etc/rc.d/rc.dnsmasq start
to start the host side services needed.

Step 3 - Set up container

First, edit the container's config file so that it contains the following fields:
   lxc.network.type = veth
   lxc.network.flags = up
   lxc.network.link = lxcbr0
   lxc.network.name = eth0

To ensure the container will use DHCP at startup, edit its rc.inet1.conf file so that the entry USE_DHCP[0]="" becomes USE_DHCP[0]="yes". This can be done either while the container is running - edit its /etc/rc.d/rc.inet1.conf, or from the host machine's filesystem - edit ~/.local/share/lxc/test1/rootfs/etc/rc.d/rc.inet1.conf (for a container named test1).

When the container next starts, it should have normal networking available and enabled.

Extras

If you're creating lots of containers, it soon becomes tiresome having to modify each container's config file to add the lxc.network.* fields. Instead, these fields could be added automatically when lxc-create is run (back in Part 2, Step 1). This is achieved by listing the fields in LXC's /etc/lxc/default.conf file. Replace the line:
   lxc.network.type = empty
with:
   lxc.network.type = veth
   lxc.network.flags = up
   lxc.network.link = lxcbr0
   lxc.network.name = eth0

For good measure, you could also add a line something like:
   lxc.network.hwaddr = 00:16:3e:xx:xx:xx
which will appropriately replace each 'x' character whenever a container is created to provide it with a unique MAC address. That unique MAC address could later be used by dnsmasq to provide a consistent IP address for the container. This is achieved by adding the MAC address and desired IP address to the dnsmasq configuration file we created back at Step 2, /etc/dnsmasq.d/lxc-bridge.conf. Add something like:
   dhcp-host=00:16:3e:41:7a:d4,192.168.100.17
depending on requirements. Add a similar line for each container which requires a consistent IP address across reboots.

Contact

Please send any questions, comments, advice etc., to Chris Willing <chris.willing _at_ linux.com>