Wrapper scripts for LXC unprivileged containers

December 22, 2022 Roberto Puzzanghera 0 comments

Index


Handling LXC unprivileged containers can be quite annoying. In fact, you have to run each command as the user who owns the container and sometime define the configuration file and other parameters by means of a long command to type and remember. If you have many unprivileged containers and have to perform tasks as start/stop/attach frequently, your patience will come to an end very quickly. This is the reason why at a certain point I started to write my own wrapper scripts for the most common LXC commands. Nothing special but it seems that nobody have published any tools to simplify the LXC common tasks with unprivileged containers, so here is my contribute.

Let's start with some examples.

Running lxc-ls as root shows all the unprivileged containers as stopped even when they are running:

root@host:~# lxc-ls --fancy 
NAME     STATE   AUTOSTART GROUPS IPV4 IPV6 UNPRIVILEGED  
c1       STOPPED 1         -      -    -    true          
c2       STOPPED 1         -      -    -    true          
c3       STOPPED 0         -      -    -    true          
c4       STOPPED 1         -      -    -    true          

An output like this would be more convenient:

root@host:~# lxl # which is my lxc-ls wrapper that I'll show later on
Unprivileged running containers of user user1 
NAME   STATE   AUTOSTART GROUPS IPV4      IPV6 UNPRIVILEGED  
c1     RUNNING 1         -      10.0.0.1  -    false         
Unprivileged running containers of user user2
NAME   STATE   AUTOSTART GROUPS IPV4      IPV6 UNPRIVILEGED  
c2     RUNNING 1         -      10.0.0.2  -    false         
Unprivileged running containers of user user3
NAME   STATE   AUTOSTART GROUPS IPV4      IPV6 UNPRIVILEGED  
c3     RUNNING 1         -      10.0.0.3  -    false         
c4     RUNNING 0         -      10.0.0.4  -    false         
c5     RUNNING 1         -      10.0.0.5  -    false         

Let's consider a few more LXC commands. As you may have noticed, lxc-start aborts the containers' startup sequence due to id mapping issues if you try to start an unprivileged container as root:

root@host:~# lxc-start -n c1 
lxc-start: c1: lxccontainer.c: wait_on_daemonized_start: 867 Received container state "ABORTING" instead of "RUNNING" 
lxc-start: c1: tools/lxc_start.c: main: 306 The container failed to start 
lxc-start: c1: tools/lxc_start.c: main: 309 To get more details, run the container in foreground mode 
lxc-start: c1: tools/lxc_start.c: main: 311 Additional information can be obtained by setting the --logfile and --logpriority options

Also, you may have to type long commands like this to attach to a container in the proper way

sudo -u user1 lxc-attach \
  -n c1 \
  --keep-env \
  --set-var HOSTNAME=c1.domain \
  --set-var USER=root \
  --set-var HOME=/ \
  -- /bin/bash --rcfile /etc/profile

What if you can do it with a simple command apparently with no need to become the owner of that container? For example

root@host:~# lxu c1 # container c1 UP (container is started, its owner determined dinamically)
root@host:~# lxa c1 # attaching container c1 (your env variables and /etc/profile will be passed)
root@c1:/root#      # inside the container c1
root@c1:/root# exit # exiting from the container c1
root@host:~#        # back to the host prompt again
root@host:~# lxd c1 # container c1 DOWN

Let's see an example where the creation of a container is made simple. This is how I create a Slackware 15.0 container owned by the user roberto (unprivileged), and with an ip address 10.0.0.22. I want to use my personal list of packages (SLACKPKG_TEMPLATE) and also a personal LXC template (LXC_TEMPLATE) to perform this task:

SLACKPKG_TEMPLATE=my_15.0_template \
LXC_TEMPLATE=my_lxc_template \
RELEASE=15.0 \
my_lxc-create -n <container_name> -i 10.0.0.22 -u roberto

or simply

my_lxc-create -n <container_name> -i 10.0.0.22 -u roberto

if you set your defaults parameters inside the script. Of course, if you're not a Slackware user, you can load the LXC template for your distro and use this same script.

Creating a container owned by root is even simpler. It will be sufficient not to specify any user:

my_lxc-create -n <container_name> -i 10.0.0.22

...and so on for other common LXC commands like lxc-console, lxc-destroy, lxc-copy etc.

I'll show also below how to install all of your containers (unprivileged included!) in the same directory, say /lxc, rather than installing each one in the owner's home directory.

My wrapper scripts will work both for privileged containers (owned by root itself) and unprivileged containers. I wrote them for my Slackware linux distro, but I think that they can be useful for any other Linux flavors, as they can be easily adapted.

Credits

If you are a Slackware user and you are looking for unprivileged containers documentation, you should start by reading the Christoph Willing's guide. I'm very grateful to him, because without his pages I would almost certainly still be trapped in Linux-VServer, which is not maintained anymore. The Cristoph Willing's guide explains in detail how to use libcgroup to run unprivileged containers and it's the starting point on this topic. In the following I will assume that you are already familiar with the creation and configuration of unprivileged containers as explained there. In fact, my installation is mostly an automation of the tasks explained in that guide.

Also the Stéphane Graber's articles are a suitable reading at the beginning.

A special credit is addressed to Matteo Bernardini (Ponce) for his great support to the Slackware community, not only concerning LXC.

Add a comment