Unprivileged containers in Slackware

Part 1 - Setting up

In Part 1 of this Unprivileged containers in Slackware article, we'll discuss how to arrange the system requirements to run unprivileged containers in Slackware using libcgroup facilities rather than cgmanager which has now been deprecated. Instructions for using cgmanager for running unprivileged containers are available in the previous version of this article.

In Part 2 of the article we'll go through the steps to construct and run an unprivileged container in Slackware, while Part 3 will describe a convenient way to provide networking for the container.

Much of the information in the original version of this page was based on Stephane Graber's Introduction to unprivileged containers, one of a series of his excellent blog posts about LXC. Although we'll be concentrating here on how to make unprivileged containers work specifically in a Slackware environment, reading Stephane's introduction is highly recommended.

 Slackware-14.2 is the oldest version of Slackware that supports unprivileged containers since it was the first to provide a version of the shadow package that includes support for subuids and subgids (needed for unprivileged containers). By default, Slackware-14.2 has cgmanager enabled to support unprivileged containers; see the previous version of this article for instructions to run unprivileged containers using the default cgmanager. This article describes how to bypass  cgmanager and instead use libcgroup to run unprivileged containers.

The tools provided in the libcgroup package greatly simplify setup of a user's environment to support unprivileged containers, compared with what was required using cgmanager. All that needs to be done is to configure and then run the provided system services in /etc/rc.d:
1. rc.cgconfig which uses the configuration file at /etc/cgconfig.conf to run the cgconfigparser utility to parse and setup the control group filesystem.
2. rc.cgred, the CGroups Rules Engine Daemon, which uses the rules found in /etc/cgrules.conf to automatically assign processes into cgroups based on UID/GID.
These services are enabled by making them executable (run: sudo chmod a+x /etc/rc.d/rc.cg{config,red}).

Their configuration files are initially empty (everything commented out) but that is still enough to run unprivileged containers started by root, although not enough to run unprivileged containers start by an ordinary user, which is our aim. To do that, the relevant configuration files need some content. In the examples below, the ordinary user chris will become setup and authorized to run unprivileged containers. Change the user name chris in these configuration files to suit your local circumstances


Step 0. But first ...


On Slackware version 14.2, ensure the libcgroup package is at least libcgroup-0.41-x86_64-5_slack14.2 (or libcgroup-0.41-i586-5_slack14.2), since this version corrects some details in the service and configuration files. Later Slackware versions will already have the corrections in place.

OK, we're set to start configuring now ...


Step 1. Setting up /etc/cgconfig.conf

The /etc/rc.d/rc.cgconfig service uses the /etc/cgconfig.conf file to insert control group branches into the control group filesystem. We will define, in /etc/cgconfig.conf, the control groups we want added to enable the ordinary user chris to run unprivileged containers. Add the following lines to /etc/cgconfig.conf:

group qwerty { 
    perm {
        task {
            uid = chris;
            gid = users;
        }
    admin {
          uid = chris;
          gid = users;
      }
  }

  cpuset {
      cgroup.clone_children = 1;
      cpuset.mems = 0;
      cpuset.cpus = 0-3;
  }
  cpu {}
  cpuacct {}
  blkio {}
  memory { memory.use_hierarchy = 1; }
  devices {}
  freezer {}
  net_cls {}
  perf_event {}
  net_prio {}
  pids {}
}

This specifies that a control group with name qwerty (the name itself is not important), with administrative permissions granted to system user chris, should be created in each of the control groups mentioned (cpuset, cpu, cpuacct, blkio, ...). A full description of the possibilities is available by running man cgconfig.conf.

After any changes to /etc/cgconfig.conf, it's worth checking that the syntax is still all good by running the command:

cgconfigparser -l /etc/cgconfig.conf

Step 2. Setting up /etc/cgrules.conf


The /etc/rc.d/rc.cgred service uses the /etc/cgrules.conf file to authorize access to particular control groups to users or groups. Some commented out examples are provided in the default /etc/cgrules.conf file. In our case, we want to provide access for system user chris to use the control group named qwerty. This is achieved by adding the following line to /etc/cgrules.conf:
chris           *               qwerty/
which grants user chris access to all controllers (*) mentioned in the control group named qwerty. Run man cgrules.conf for more details.


Step 3. Setting up the user


To run an unprivileged container, a user must have subordinate id ranges for both uids and gids created; these provide uid & gid mappings to be used inside an unprivileged container. For user chris, run:
sudo /usr/sbin/usermod --add-subuids 100000-165536 chris
sudo /usr/sbin/usermod --add-subgids 100000-165536 chris

Specify the default uid & gid mappings to be used in an unprivileged container in the file ~chris/.config/lxc/default.conf - create it with the contents:
lxc.id_map = u 0 100000 65536
lxc.id_map = g 0 100000 65536

Confirming the setup


The rc.cgconfig and rc.rcred services can now be started (presuming they've been enabled with: sudo chmod a+x /etc/rc.d/rc.cg{config,red}). The order of execution is important: rc.cgconfig should be run first, therefore run:
sudo /etc/rc.d/rc.cgconfig start
sudo /etc/rc.d/rc.cgred start
They should run without error. Now inspect the available control groups by running the lscgroup command, which will display a tree of available control groups. If our new control group qwerty appears as a child of all the controllers specified in the configuration file e.g. freezer:/qwerty, that's a good start.

Ensure that the user chris now has entries in both /etc/subuid and /etc/subgid files (as the results of usermod commands above).

If you already have an unprivileged container from somewhere, then you should be able to run it now. Otherwise, we're now ready to create and run unprivileged containers, as described in Part 2.

To enable the rc.cgconfig and rc.cgred services to start automatically at boot time, they could be started from either /etc/rc.d/rc.local or from /etc/rc.d/rc.S (along with, or replacing rc.cgmanager). At around line 389 of /etc/rc.d/rc.S just after rc.cgmanager is started, add something like:
# Start libcgroup services
if [ -x /etc/rc.d/rc.cgconfig -a -x /etc/rc.d/rc.cgred -a -d /sys/fs/cgroup ]; then
  echo "Starting libcgroup services"
  /etc/rc.d/rc.cgconfig start
  /etc/rc.d/rc.cgred start
fi
Alternatively, add the same lines anywhere in /etc/rc.d/rc.local


Next step: Part 2 - Running unprivileged containers



Contact

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