LXC
LXC jppLa grande mode actuelle est aux containers, après quelques essais avec Docker j'ai été déçu, ce n’est pas facile à installer, ni à utiliser et j’y ai trouvé des limitations fortes.
Au hasard de mes lectures j’ai trouvé des articles sur un logiciel connu depuis longtemps mais peu utilisé : LXC.
Cette possibilité de créer des « Machines » légères, qui, contrairement à d’autres systèmes, utilisent le noyau de la machine support. L’empreinte disque de ces « machines » est donc bien moindre et il n’y a pas grand-chose à installer sur un système normal car presque tout est déjà disponible.
Les outils LXC sont très légers et semblent performants, un ensemble de modèles (templates ?) de création de conteneurs est disponible et permet déjà de réaliser des installations faciles. L’isolation entre les containers (ou conteneurs?) semble au moins aussi bonne que celle de Docker et consorts.
J’ai donc décidé de réaliser quelques tests en commençant par l’installation d’une Debian 10 "de base" donc tout à fait standard.
Afin de ne pas être ennuyé par les problèmes réseau, et pour ne pas « casser » la config de la machine de test j’ai monté une nouvelle carte réseau agencée en mode pont (bridge pour les fanatiques) dédiée à LXC.
Les articles suivants devraient vous permettre de « démarrer » rapidement un conteneur LXC en mode privilégié, on passera ensuite au mode non-privilégié qui est quand même plus sécurisant.
LXC n'ext pas à proprement parler un hyperviseur mais plus un système de conteneurisation qui s'appuie,entre autres choses, sur les Cgroups pour compartimenter les machines. LXC est maintenu par la société Canonical créatrice de la distribution Ubuntu.
LXC : quelques commandes
LXC : quelques commandes jppD’abord quelques commandes « lxc » (les principales pour commencer) :
La plupart des commandes sont constituées du préfixe « lxc- » suivi d’un suffixe qui précise l’action à exécuter, le tout est assez simple à mémoriser. Pour les autres commandes voir dans /usr/bin.
Quelques paramètres généraux utilisés par la plupart des commandes :
- -n suivi du nom de l’instance
- -o nom d’un fichier pour stocker le log de l’opération
- -P chemin vers le répertoire de stockage, si absent lxc utilise par défaut /var/lib/lxc
Les commandes principales :
- lxc-checkconfig
Avant toute choses s'assurer que votre kernel dispose bien des options nécessaires à LXC.
- lxc-create :
Création d’une nouvelle instance, les paramètres précisent le template à utiliser ( t nom_du_template). Exemple :
lxc-create -name debian-a -t debian
Le nom à indiquer est le nom du template (/usr/share/lxc/templates) sans le préfixe « lxc- ».
L’exemple indiqué crée une machine nommée « debian-a » avec le template « lxc-debian ». - lxc-start : Démarre l’instance dont le nom est donné en paramètre.
- lxc-stop : Stoppe la machine dont le nom est donné en paramètre.
- lxc-attach nom de l’instance
Vous «attache » une console avec un shell sur la machine donnée en paramètre. - lxc-copy -n machine_modèle -N nouvelle machine
Avec plein d’autres options pour faire des copies éphémères, changer le répertoire de stockage de l’image ….
- lxc-freeze nom de l'instance
Permet de geler une instance dont le nom est donné en paramètre, l’instance est bloquée mais peut être débloquée par lxc-unfreeze - lxc-snapshot
Réalise un snapshot de la machine dont le nom est donné en paramètre. Cela peut être très utile en phase de création d’une nouvelle instance.
Le paramètres « -L » permet de lister les snapshots existants d’une instance.
Le paramètre « -r » permet de restaurer un snapshot. - lxc-destroy -n nom de la machine
Détruit l’instance donnée en paramètre, options « -f » pour force et « -s » pour détruire aussi les snapshots. - lxc-ls
Liste les machines existantes et leur état, l’option « -f » rend le tout plus joli et lisible.
J'ai même créé un script remplaçant qui appelle une copie de lxc-ls avec l'option "-f". - lxc-info
Affiche les informations principales de l'instance donnée en paramètre.
Pour les détails de paramétrage voir les pages « man » qui sont bien faites.
LXC : le réseau
LXC : le réseau jppLXC le réseau.
La partie réseau est gérée par « lxc-net » qui est un démon que se lance au démarrage du système.
Sa configuration n’est pas complexe mais, pour être fonctionnelle rapidement, nécessite la création d’une interface « pont » dédiée et donc la présence des "br-utils".
Ici et pour ne pas modifier une configuration réseau « qui marche » déjà avec deux ponts j’ai préféré rajouter une carte réseau dédiée dans ma machine de développement. Le seul problème que j’ai rencontré est le fait que l’interface supplémentaire s’est « intercalée » entre les deux interfaces existantes et qu’il a fallu que je modifie la config d'un des deux ponts déjà existants … "ensp3s0" est devenu "ensp4s0" dans ma config.
J’ai ajouté dans /etc/network/interfaces :
allow-hotplug enp3s0 iface br2 inet static address 192.168.3.1 netmask 255.255.255.0 network 192.168.3.0 broadcast 192.168.3.255 bridge_ports enp3s0 bridge_maxwait 1
Contenu du fichier de config de « lxc-net « dans / etc / default / lxc-net :
USE_LXC_BRIDGE="true"
LXC_BRIDGE="br2"
LXC_ADDR="192.168.3.1"
LXC_NETMASK="255.255.255.0"
LXC_NETWORK="192.168.3.0/24"
LXC_DHCP_RANGE="192.168.3.0,192.168.3.255"
LXC_DHCP_MAX="254"
LXC_DHCP_CONF_FILE=""
LXC_DOMAIN=""
En ce qui concerne le fichier de config de mon instance « debian-a » déjà créée par « lxc-create -n debian-a -t debian » j’ai ajouté à la fin du fichier /var/lib/lxc/debian-a/config :
# pour le réseau eth0
lxc.net.0.type = veth
# lxc.net.0.veth.mode = bridge
lxc.net.0.link = br2
lxc.net.0.name = eth0
lxc.net.0.ipv4.address = 192.168.3.11/24
lxc.net.0.ipv4.gateway = 192.168.3.1
lxc.net.0.flags = up
Comme lxc-net utilise dnsmasq il est nécessaire de désactiver un autre serveur DNS, ici j’ai du désactiver bind9.
D’autre part lxc-net utilise dnsmasq mais il lui transfère un paramètre indésirable (-U dnsmasq) qui déclenche chez moi une erreur de dnsmasq, il m’ a donc fallu :
renommer « dnsmasq » en « dnsmasq_real », faire un script « dnsmasq » qui contient :
#!/bin/bash
#
# Faux dnsmasq pour analyser et filtrer les paramètres reçus
#
# ------------------------------------------------------------------------------
shift
shift
/usr/sbin/dnsmasq_real $*
Afin d’éliminer les deux paramètres superflus. Une fois cette « manip » réalisée dnsmasq est bien présent comme le montre un "ps" après un reboot et l’adresse de mon pont « br2 » est bien celle attendue.
On va pouvoir passer à la suite.
LXC : test première instance
LXC : test première instance jppAssurons nous d'abord que notre instance est bien configurée :
lxc-ls -f NAME STATE AUTOSTART GROUPS IPV4 IPV6 UNPRIVILEGED debian-a STOPPED 0 - - - false |
L’instance est bien là et stoppée, essayons de la démarrer et de nous y connecter :
lxc-start debian-a lxc-attach debian-a root@debian-a:/# ls -al total 56 drwxr-xr-x 18 root root 4096 avril 12 14:26 . drwxr-xr-x 18 root root 4096 avril 12 14:26 .. lrwxrwxrwx 1 root root 7 avril 6 16:18 bin -> usr/bin drwxr-xr-x 2 root root 4096 févr. 1 17:09 boot drwxr-xr-x 7 root root 520 avril 12 14:26 dev drwxr-xr-x 51 root root 4096 avril 10 22:58 etc ...... drwxr-xr-x 2 root root 4096 avril 6 16:18 opt dr-xr-xr-x 301 root root 0 avril 12 14:26 proc drwx------ 3 root root 4096 avril 10 22:58 root drwxr-xr-x 11 root root 340 avril 12 14:27 run lrwxrwxrwx 1 root root 8 avril 6 16:18 sbin -> usr/sbin drwxr-xr-x 2 root root 4096 avril 6 17:00 selinux drwxr-xr-x 2 root root 4096 avril 6 16:18 srv dr-xr-xr-x 12 root root 0 avril 12 14:26 sys drwxrwxrwt 7 root root 4096 avril 12 14:27 tmp drwxr-xr-x 13 root root 4096 avril 6 16:18 usr drwxr-xr-x 11 root root 4096 avril 6 16:18 var |
Cette instance ressemble bel et bien à une machine standard, voyons pour la partie réseau :
root@debian-a:/# ifconfig lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536 |
Là aussi tout semble correct, faisons un tout petit test (ssh sur une autre machine physique du réseau) :
ssh root@xxxxxxxxx Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent permitted by applicable law. |
C’est OK, le réseau marche … essayons de faire la mise à jour du système puisque /etc/apt/sources/list a l’air correct :
root@debian-a# apt-get update Get:1 http://security.debian.org stable/updates InRelease [65,4 kB] Get:2 http://deb.debian.org/debian stable InRelease [122 kB] Get:3 http://deb.debian.org/debian stable/main amd64 Packages [7 907 kB] Get:4 http://security.debian.org stable/updates/main amd64 Packages [187 kB] Get:5 http://security.debian.org stable/updates/main Translation-en [100 kB] Get:6 http://deb.debian.org/debian stable/main Translation-en [5 970 kB] Get:7 http://deb.debian.org/debian stable/main Translation-fr [2 478 kB] Fetched 16,8 MB in 3s (6 103 kB/s) Reading package lists... Done |
Tout est donc OK, cette instance semble parfaitement fonctionnelle et occupe #686Mo sur disque.
On peut s'y connecter en ssh (il faut fixer un nouveau mot se passe "root", lxc-attach vous connecte par ssh en "root) comme sur n'importe quelle autre machine :
ssh root@192.168.3.11 The programs included with the Debian GNU/Linux system are free software; Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent |
Mais elle n'est pas très sécurisée car elle fonctionne avec les droits "root" ce qui n'est pas forcément le plus judicieux et que d'autre part rien ne l'empêche de monopoliser l'ensemble de la mémoire, du CPU des IO ...
Si on utilise la commande "top" la mémoire disponible est celle de la machine pĥysique sous-jacente, "cat /proc/meminfo" donne bien l'intégralité de la mémoire physique.
De même "df" montre l'intégralité du disque contenant le répertoire /var/lib/lxc.
Il reste donc encore des choses à faire pour rendre les choses plus sûres et maîtrisables.
Un conteneur non privilégié
Un conteneur non privilégié jppInitialisation d'un conteneur non privilégié :
Comme la plupart des "templates" ne permettent pas de charger des images utilisables en mode non-privilégié il est recommandé d'utiliser le template "download".
Celui-ci nécessite quelques paramètres supplémentaires pour fonctionner en mode automatique. Si ces paramètres ne sont pas fourni le template vous demande les données en affichant pour chaque choix une liste des possibilités, la liste des distributions disponibles est impressionnante ...
Mais avant de créer le premier conteneur non privilégié il faut effectuer quelques mises à jour (utilisateur "testlxc" créé pour la circonstance) :
- Créer un répertoire pour stocker nos conteneurs, sinon ceux-ci seront créés dans .local/share/lxc. Ici j'ai créé un répertoire $HOME/VM/LXC, volontairement en deuxième niveau en dessous de $HOME.
- Créer le fichier /etc/lxc/lxc-usernet contenant :
# user type_de_réseau pont nombre
testlxc veth br2 10
- Créer le répertoire $HOME/.config/LXC
- Créer dans ce répertoire un fichier "lxc.conf" contenant :
lxc.lxcpath = /home/testlxc/VM/LXC
lxc.default_config = /home/testlxc/.config/lxc/default.conf
- Créer un fichier "default.conf" contenant :
lxc.apparmor.profile = generated
lxc.apparmor.allow_nesting = 1
###
lxc.idmap = u 0 296608 65536
lxc.idmap = g 0 296608 65536
Ces valeurs sont issues de :
grep testlxc /etc/subuid (pour la ligne "u")
grep testlxc /etc/subgid (pour la ligne "g")
Ouf, c'est fini pour la configuration.
Pour les tests j'ai utilisé un script (TC) avec log détaillé dans un fichier, surtout pour les premiers tests :
LOGPRI=DEBUG
TEMPLATE=download
NOM=debian-d
LOTG=" --logfile=/dev/stdout --logpriority=${LOGPRI} "
COMPL=' -- --dist debian --release buster --arch amd64 '
REP=' -P /home/testlxc/VM/LXC/'
lxc-create -n ${NOM} ${REP} -t ${TEMPLATE} ${LOG} ${COMPL} 2>&1 | tee TC.LOG
Et on exécute notre script :
./TC
No such file or directory - Failed to open tty
No such file or directory - Failed to open tty
Using image from local cache
Unpacking the rootfs
.....
You just created a Debian buster amd64 (20200419_05:24) container.
To enable SSH, run: apt install openssh-server
No default root or user password are set by LXC.
Les deux "No such file..." n'ont pas l'air de gêner ...
On va voir de qui se passe dans ce nouveau conteneur :
lxc-start debian-d
lxc-attach debian-d
root@debian-d:/# ls -al
total 68
drwxr-xr-x 21 root root 4096 Apr 19 05:29 .
drwxr-xr-x 21 root root 4096 Apr 19 05:29 ..
drwxr-xr-x 2 root root 4096 Apr 19 05:27 bin
drwxr-xr-x 2 root root 4096 Feb 1 17:09 boot
drwxr-xr-x 6 root root 500 Apr 20 11:43 dev
drwxr-xr-x 40 root root 4096 Apr 20 11:43 etc
drwxr-xr-x 2 root root 4096 Feb 1 17:09 home
drwxr-xr-x 10 root root 4096 Apr 19 05:26 lib
drwxr-xr-x 2 root root 4096 Apr 19 05:25 lib64
....
drwxr-xr-x 2 root root 4096 Apr 19 05:25 srv
dr-xr-xr-x 12 nobody nogroup 0 Apr 20 11:43 sys
drwxrwxrwt 7 root root 4096 Apr 20 11:43 tmp
drwxr-xr-x 10 root root 4096 Apr 19 05:25 usr
drwxr-xr-x 11 root root 4096 Apr 19 05:25 var
Ceci ressemble bien au contenu d'une machine réelle.
On personnalise rapidement le mot de passe de "root" afin de sécuriser un peu ce conteneur.
on en sort par "exit" puis "lxc-stop debian-d" ou par :
"systemctl poweroff" plus court !
Maintenant il reste à connecter notre machine au réseau ...
LXC : Non privilégie et sécurisé
LXC : Non privilégie et sécurisé jppLXC : Il est nécessaire de sécuriser un peu ce conteneur en limitant ses possibilités :
- Mémoire
- Cpu
- IO, pour cela on verra un peu plus tard.
Mais ce n'est pas une mince affaire de gérer les "cgroups" à travers LXC car il existe pas mal de contraintes dont la plupart doivent être levées au niveau du système.
Les tests sont donc assez longs et j'ai du passer un temps assez long avant de rédiger la suite de cet article ... le temps de consulter pas mal de documents et de faire quelques tests.