Creating a LXC unprivileged container on Slackware

December 22, 2022 Roberto Puzzanghera 0 comments


lxc-my_template is a modification of the default LXC template for Slackware by Matteo Bernardini, already installed in /usr/share/lxc/templates/lxc-slackware. It mostly changes the following things (diff here):

  • it switches the release from current to 15.0
  • it defines the $domain variable
  • it restores the DIALOG variable of slackpkg to on, as it fortunately seems to be working again
  • it slightly modifies the package list
  • it removes the executable flag of a few not needed services
  • it installs some aliases to /etc/profile, so that they can be set when attaching the container
  • it sets the google's nameservers
  • it removes the LXC's options that will be imported via the /etc/lxc/lxc-common.conf file
  • it removes the proc and sysfs entries from the /etc/fstab file as suggested by the C.W. guide, as = proc:mixed sys:ro cgroup will be added in the config file.

Download lxc-my_template

wget -O /usr/share/lxc/templates/lxc-my_template
chown root:root /usr/share/lxc/templates/lxc-my_template
chmod +x /usr/share/lxc/templates/lxc-my_template

Now download the slackpkg templates, i.e. the file list for Slackware 15.0. I prepared two lists:

  1. 15.0.template, which contains almost all required packages for a tipical LAMP server. This is the default template in the script that we re going to use.
  2. my_minimal.template, mostly the Ponce's list with the addition of nano, file, glibc-zoneinfo, openssl and ca-certificates, the last two just to avoid gpg errors when running slackpkg.
mkdir -p /etc/slackpkg/templates
wget -O /etc/slackpkg/templates/15.0.template
wget -O /etc/slackpkg/templates/my_minimal.template
chown root:root /etc/slackpkg/templates/*

Let's see how to use my_lxc-create script, that you already downloaded before.

The usage is simple

Usage: /usr/local/bin/my_lxc-create script takes the following options: 
 -n  <name>              Set container name to use. **Required** 
 -r  <release>           Set Slackware release to use. Default: 15.0 
 -s  <slackpkg-template> slackpkg template (pkg list). Default: 15.0. Alternative: my_minimal 
 -t  <lxc-template>      Set lxc template to use. Default: my_template. Use slackware to load /usr/share/lxc/templates/lxc-slackware 
 -m  <mirror>            Set mirror to use. If not set the one specified in the lxc template will be used
 -o  <domain>            Domain 
 -d                      Use DHCP. Ignored if -i <ip> is passed 
 -i  <ip>                IP address of the container 
 -g  <gw>                Gateway 
 -u  <user>              Set the system user that will own the container. If not declared the container will be privileged 
 -h                      Print help options and exit

At the top of the script you may want to customize the default variables

GW=                     # gateway 
SLACKPKG_TEMPLATE=15.0          # slackpkg template (pkg list). Alternative: my_minimal 
LXC_TEMPLATE=my_template        # lxc template. Use "slackware" to load /usr/share/lxc/templates/lxc-slackware 
RELEASE=15.0                    # Release 
DHCP=no                         # Put yes if you want DHCP

You can always overwrite them passing the options to the program according to the usage shown above.

Let's see some examples. In the simplest form, my_lxc-create will build a privileged container with the default values:

my_lxc-create -n c1

Here no net has been configured, unless you have DHCP=yes in your defaults. The slackpkg template /etc/lxc/templates/15.0.template has been used, while the LXC container will be created with the /usr/share/lxc/templates/lxc-my_template.

Optionally you may want to dinamically change some parameters when running the same script. For example:

my_lxc-create -n c1 -s my_minimal -r current -d

In this case the my_minimal slackpkg template will be used to install Slackware-current (at the moment I'm writing the same template will be fine also for current). The container will have DHCP support (-d).

The same, but this time a static IP will be configured:

my_lxc-create -n c1 -s my_minimal -r current -i

Building an unprivileged container

Now let's see how to tell the same script to create an unprivileged container. It will be sufficient to pass the user who will be the owner of the container:

my_lxc-create -n c1 -i -u user1

So, when you add user1 as the -u option, my_lxc-create understands that the container must be unprivileged and owned by the system user user1. It will run the script my_lxc-turn_into_unprivileged to convert c1  to unprivileged. That user will be created and all the dirty settings will be done for you.

Let's see what happened in the config files. First of all, you can find the id mapping in the /lxc/c1/config file:

lxc.idmap = u 0 493217 65536 
lxc.idmap = g 0 493217 65536

where of course 65536 is the id range and 493217 is the first id available in the system. You will find a line like this both in /etc/subuid and in /etc/subgid


The cgroup files /etc/cgconfig.d/user1.conf and /etc/cgrules.d/user1.conf have been created with the settings for user1. A line for user1 has been added into /etc/lxc/lxc-usernet.

The command lscgroup shows the newly configured control group user1 (as said group and userid share the same name).

lscgroup | grep user1

You can check that the cgroup configuration is good by parsing your config file like this

cgconfigparser -l /etc/cgconfig.d/user1.conf

or by parsing all files in the /etc/cgconfig.d directory

cgconfigparser -L /etc/cgconfig.d

Playing with the newly created container

Put the new container UP (lxu), start a shell inside it (attach with lxa, no need to change user!) and play with it

root@host:~# lxu c1
root@host:~# lxa c1
root@c1:# ifconfig 
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500 
       inet  netmask  broadcast 
       ether a6:af:8e:1d:37:3b  txqueuelen 1000  (Ethernet) 
       RX packets 60309  bytes 11387162 (10.8 MiB) 
       RX errors 0  dropped 0  overruns 0  frame 0 
       TX packets 67259  bytes 20594117 (19.6 MiB) 
       TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0 

lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536 
       inet  netmask 
       loop  txqueuelen 1000  (Local Loopback) 
       RX packets 4253  bytes 2035914 (1.9 MiB) 
       RX errors 0  dropped 0  overruns 0  frame 0 
       TX packets 4253  bytes 2035914 (1.9 MiB) 
       TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0 

root@c1:# ping 
PING ( 56(84) bytes of data. 
64 bytes from ( icmp_seq=1 ttl=113 time=4.35 ms 
64 bytes from ( icmp_seq=2 ttl=113 time=4.39 ms 
--- ping statistics --- 
2 packets transmitted, 2 received, 0% packet loss, time 1002ms 
rtt min/avg/max/mdev = 4.351/4.371/4.391/0.020 ms 
root@c1:# exit 

List the status of your containers (lxl is a wrapper of lxc-ls)

# lxl
All containers 
c1       STOPPED 0         -      -    -    true
c2       STOPPED 0         -      -    -    false          
Unprivileged running containers of user user1
c1      RUNNING 0         -  -    false         

Now destroy it (my_lxc-destroy):

root@host:~# my_lxc-destroy c1
c1 container is going to be destroyed. Confirm [y/n]? [n] y 
LXC container c1 is stopped. Continuing. 
User user1 is going to be deleted. Be sure that it is not a system user like apache, mysql etc. Confirm [y/n]? [n] y 
Deleting user1... 
Destroing c1 container...

At the same time the id mapping, the net and the cgroups settings have been cleared.

Add a comment