Conteneurs LXC sécurisés

Conteneurs LXC non-privilégiés

LXC (pour Linux Containers) est une interface utilisateur pour interagir avec les fonctionnalités de « virtualisation légère » du noyau Linux.

Contrairement à une virtualisation classique, le noyau du système hôte est partagé avec les systèmes invités et l’isolation est réalisée en espace utilisateur uniquement.

Afin d’isoler le système invité du système hôte, LXC utilise les fonctionnalités du noyau Linux telles que les namespaces, seccomp, capabilities ou cgroups.

Dans le cas des conteneurs LXC privilégiés, l’identifiant utilisateur (UID) à l’intérieur du conteneur correspond à l’identifiant du système hôte.
L’utilisateur UID=1000 du conteneur possède alors les mêmes droits que l’utilisateur possédant cet UID sur l’hôte.

Ceci étant valable pour l’utilisateur root (UID=0), il convient de traiter l’utilisateur root du conteneur avec le même niveau de précaution que le compte root du système hôte comme l’explique le bulletin d’actualité CERTFR-2016-ACT-025.

La fonctionnalité de conteneur LXC non-privilégié s’appuie sur le mécanisme des user namespaces disponible depuis la version 3.8 du noyau Linux et supporté pour la majorité des systèmes de fichiers depuis la version 3.12.

Les user namespaces permettent d’isoler les identifiants d’utilisateur et de groupe (UID et GID) : l’UID et le GID d’un même processus sont différents selon le user namespace dans lequel on se place. Ceci permet notamment à un processus d’avoir les privilèges super-utilisateur de l’UID 0 à l’intérieur d’un namespace tout en étant un processus non-privilégié à l’extérieur.

Pour disposer des user namespaces, l’utilisateur doit être autorisé à utiliser une plage d’identifiants utilisateur et de groupe additionnelle, renseignée dans les fichiers /etc/subuid et /etc/subgid.
Par exemple :

grep user /etc/subuid
user:100000:65536

signifie que l’utilisateur user peut utiliser les identifiants 100000 à 165535 comme lien entre le namespace hôte et ceux de ses systèmes invités. Une fois les UID et GID additionnels dont l’utilisateur bénéficie renseignés dans la configuration LXC, comme indiqué dans la documentation du projet Linux Container, l’utilisateur peut créer et démarrer son nouveau conteneur non-privilégié.

Depuis le conteneur :

root@conteneur:# ps faxo user,uid,gid,pid,comm
USER UID GID PID COMMAND
root 0 0 308 bash
root 0 0 333 \_ ps
root 0 0 1 systemd
root 0 0 38 systemd-journal
syslog 104 108 80 rsyslogd
root 0 0 83 cron
root 0 0 132 agetty
root 0 0 134 agetty
root 0 0 135 agetty
root 0 0 136 agetty
root 0 0 137 agetty
root 0 0 168 apache2
www-data 33 33 171 \_ apache2
www-data 33 33 172 \_ apache2

Depuis le système hôte :

root@hote:# ps faxo user,uid,pid,comm
 USER UID PID COMMAND
 ...
 user 1000 1000 4659 lxc-start
 100000 100000 100000 4669 \_ systemd
 100000 100000 100000 4801 \_ systemd-journal
 100104 100104 100108 4855 \_ rsyslogd
 100000 100000 100000 4858 \_ cron
 100000 100000 100000 4912 \_ agetty
 100000 100000 100000 4914 \_ agetty
 100000 100000 100000 4915 \_ agetty
 100000 100000 100000 4916 \_ agetty
 100000 100000 100000 4917 \_ agetty
 100000 100000 100000 4948 \_ apache2
 100033 100033 100033 4951 \_ apache2
 100033 100033 100033 4952 \_ apache2
 ...

Du point de vue de la sécurité, cela signifie que même si un processus root trouvait un moyen de s’échapper du conteneur, il obtiendrait le niveau de privilège de l’utilisateur nobody à l’extérieur.

Il est également possible de lire la correspondance UID/GID entre le user namespace père et le user namespace courant depuis l’intérieur du conteneur :

cat /proc/self/uid_map
0 100000 65536

Introduction à LXD

LXD est un gestionnaire de conteneurs LXC pour en simplifier l’administration.
LXD utilise la bibliothèque LXC pour créer et gérer les conteneurs, il se matérialise par un démon root qui expose une API REST à travers une socket UNIX locale ou un port réseau.
Le client en ligne de commande utilise cette API ce qui permet de gérer de la même façon des conteneurs locaux et ceux situés sur une machine distante.

Une fonctionnalité intéressante de LXD est qu’il crée par défaut des conteneurs non-privilégiés, il est de plus possible de générer des associations d’UID/GID entre système hôte et système invité différentes par conteneur.
Cela implique que si un processus venait à s’échapper du conteneur, les ressources des autres conteneurs lui seraient également inaccessibles.
L’inconvénient de cette méthode est que cela complique le partage d’un espace de stockage entre les différents conteneurs.

Remarques

Le noyau du système d’exploitation hôte étant partagé avec les conteneurs, la sécurité d’un conteneur repose sur la mise à jour de ce dernier ainsi que de celle des applications LXC/LXD.

Au cours des dernières années, plusieurs vulnérabilités impliquant les user namespaces ont été publiées. Certaines distributions, telles que ArchLinux, l’ont ainsi désactivé.

Documentation

Rappel des avis émis

Dans la période du 25 septembre au 01 octobre 2017, le CERT-FR a émis les publications suivantes :