Book Image

Containerization with LXC

By : Konstantin Ivanov
Book Image

Containerization with LXC

By: Konstantin Ivanov

Overview of this book

In recent years, containers have gained wide adoption by businesses running a variety of application loads. This became possible largely due to the advent of kernel namespaces and better resource management with control groups (cgroups). Linux containers (LXC) are a direct implementation of those kernel features that provide operating system level virtualization without the overhead of a hypervisor layer. This book starts by introducing the foundational concepts behind the implementation of LXC, then moves into the practical aspects of installing and configuring LXC containers. Moving on, you will explore container networking, security, and backups. You will also learn how to deploy LXC with technologies like Open Stack and Vagrant. By the end of the book, you will have a solid grasp of how LXC is implemented and how to run production applications in a highly available and scalable way.
Table of Contents (17 chapters)
Containerization with LXC
Credits
About the Author
About the Reviewer
www.PacktPub.com
Customer Feedback
Dedication
Preface

Running unprivileged LXC containers


Let's briefly touch on security with LXC. Starting with LXC version 1.0, support for unprivileged containers was introduced, allowing for unprivileged users to run containers. The main security concern running LXC containers as root is that UID 0 inside the container is the same as UID 0 on the host; thus, breaking out of a container will grant you root privileges on the server.

In Chapter 1, Introduction to Linux Containers, we talked in detail about the user namespace and how it allows for a process inside the user namespace to have a different user and group ID than that of the default namespace. In the context of LXC, this allows for a process to run as root inside the container, while having the unprivileged ID on the host. To take advantage of this, we can create a mapping per container that will use a defined set of UIDs and GIDs between the host and the LXC container.

Let's look at an example of setting up and running a LXC container as an unprivileged user.

Start by updating your Ubuntu system and installing LXC:

root@ubuntu:~# apt-get update&& apt-get upgrade
root@ubuntu:~# reboot
root@ubuntu:~# apt-get install lxc

Next, create a new user and assign it a password:

root@ubuntu:~# useradd -s /bin/bash -c 'LXC user' -m lxc_user
root@ubuntu:~# passwd lxc_user
Enter new UNIX password:
Retype new UNIX password:
passwd: password updated successfully
root@ubuntu:~#

Make a note of what the range of UIDs and GIDs on the system is for the new user we created:

root@ubuntu:~# cat /etc/sub{gid,uid} | grep lxc_user
lxc_user:231072:65536
lxc_user:231072:65536
root@ubuntu:~#

Note the name of the Linux bridge that was created:

root@ubuntu:~# brctl show
bridge name bridge id         STP enabled interfaces
lxcbr0            8000.000000000000 no
root@ubuntu:~#

Specify how many virtual interfaces can be added to the bridge for a user, in this example, 50:

root@ubuntu:~# cat /etc/lxc/lxc-usernet
# USERNAME TYPE BRIDGE COUNT
lxc_user veth lxcbr0 50
root@ubuntu:~#

Next, as the lxc_user, create the directory structure and config files as follows:

root@ubuntu:~# su - lxc_user
lxc_user@ubuntu:~$ pwd
/home/lxc_user
lxc_user@ubuntu:~$ mkdir -p .config/lxc
lxc_user@ubuntu:~$ cp /etc/lxc/default.conf .config/lxc/
lxc_user@ubuntu:~$ cat .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 231072 65536
lxc.id_map = g 0 231072 65536
lxc_user@ubuntu:~$

The preceding id_map options will map the range of virtual IDs for the lxc_user.

We can now create the container as usual:

lxc_user@ubuntu:~$ lxc-create --name user_lxc --template download
...
Distribution: ubuntu
Release: xenial
Architecture: amd64
Downloading the image index
Downloading the rootfs
Downloading the metadata
The image cache is now ready
Unpacking the rootfs
...
lxc_user@ubuntu:~$

The container is in the stopped state; to start it, run the following command:

lxc_user@ubuntu:~$ lxc-ls -f
NAME     STATE   AUTOSTART GROUPS IPV4 IPV6
user_lxc STOPPED 0         -      -    -
lxc_user@ubuntu:~$ lxc-start --name user_lxc
lxc_user@ubuntu:~$ lxc-ls -f
NAME     STATE   AUTOSTART GROUPS IPV4 IPV6
user_lxc RUNNING 0         -      -    -
lxc_user@ubuntu:~$

Notice that the processes in the container are owned by the lxc_user instead of root:

lxc_user@ubuntu:~$ ps axfwwu
lxc_user  4291  0.0  0.0  24448  1584 ?        Ss   19:13   0:00 /usr/lib/x86_64-linux-gnu/lxc/lxc-monitord /home/lxc_user/.local/share/lxc 5
lxc_user  4293  0.0  0.0  32892  3148 ?        Ss   19:13   0:00 [lxc monitor] /home/lxc_user/.local/share/lxc user_lxc
231072    4304  0.5  0.0  37316  5356 ?        Ss   19:13   0:00  \_ /sbin/init
231072    4446  0.1  0.0  35276  4056 ?        Ss   19:13   0:00      \_ /lib/systemd/systemd-journald
231176    4500  0.0  0.0 182664  3084 ?        Ssl  19:13   0:00      \_ /usr/sbin/rsyslogd -n
231072    4502  0.0  0.0  28980  3044 ?        Ss   19:13   0:00      \_ /usr/sbin/cron -f
231072    4521  0.0  0.0   4508  1764 ?        S    19:13   0:00      \_ /bin/sh /etc/init.d/ondemand background
231072    4531  0.0  0.0   7288   816 ?        S    19:13   0:00      |   \_ sleep 60
231072    4568  0.0  0.0  15996   856 ?        Ss   19:13   0:00      \_ /sbin/dhclient -1 -v -pf /run/dhclient.eth0.pid -lf /var/lib/dhcp/dhclient.eth0.leases -I -df /var/lib/dhcp/dhclient6.eth0.leases eth0
231072    4591  0.0  0.0  15756  2228 pts/1    Ss+  19:13   0:00      \_ /sbin/agetty --noclear --keep-baud pts/1 115200 38400 9600 vt220
231072    4592  0.0  0.0  15756  2232 pts/2    Ss+  19:13   0:00      \_ /sbin/agetty --noclear --keep-baud pts/2 115200 38400 9600 vt220
231072    4593  0.0  0.0  15756  2224 pts/0    Ss+  19:13   0:00      \_ /sbin/agetty --noclear --keep-baud pts/0 115200 38400 9600 vt220
231072    4594  0.0  0.0  15756  2228 pts/2    Ss+  19:13   0:00      \_ /sbin/agetty --noclear --keep-baud console 115200 38400 9600 vt220
231072    4595  0.0  0.0  15756  2232 ?        Ss+  19:13   0:00      \_ /sbin/agetty --noclear --keep-baud pts/3 115200 38400 9600 vt220
lxc_user@ubuntu:~$

The root filesystem and config file location is different when running the container as a non root user:

lxc_user@ubuntu:~$ ls -la .local/share/lxc/user_lxc/
total 16
drwxrwx---  3   231072 lxc_user 4096 Dec 15 19:13 .

drwxr-xr-x  3 lxc_user lxc_user 4096 Dec 15 19:13 ..
-rw-rw-r--  1 lxc_user lxc_user  845 Dec 15 19:13 config
drwxr-xr-x 21   231072   231072 4096 Dec 15 03:59 rootfs
-rw-rw-r--  1 lxc_user lxc_user    0 Dec 15 19:13 user_lxc.log
lxc_user@ubuntu:~$ cat .local/share/lxc/user_lxc/config | grep -vi "#" | sed '/^$/d'
lxc.include = /usr/share/lxc/config/ubuntu.common.conf
lxc.include = /usr/share/lxc/config/ubuntu.userns.conf
lxc.arch = x86_64
lxc.id_map = u 0 231072 65536
lxc.id_map = g 0 231072 65536
lxc.rootfs = /home/lxc_user/.local/share/lxc/user_lxc/rootfs
lxc.rootfs.backend = dir
lxc.utsname = user_lxc
lxc.network.type = veth
lxc.network.link = lxcbr0
lxc.network.flags = up
lxc.network.hwaddr = 00:16:3e:a7:f2:97
lxc_user@ubuntu:~$