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
- Site du projet LXC/LXD : https://linuxcontainers.org/
- Blog de Stéphane Graber : https://stgraber.org/