Conteneurs LXC en non privilégié sous Debian Jessie 1/2

Introduction: motivations

Je me suis intéressé récemment à la mise en place de conteneurs sur mon serveur, pour plusieurs raisons:

  • grande flexibilité pour l'installation de nouveaux services: on peut créer un conteneur et faire n'importe quoi dedans, ça n'ira pas mettre en l'air tout le reste de l'installation et donc en particulier les autres services hébergés sur la machine. Le corollaire est que cela permet d'envisager sereinement l'installation de services exigeant davantage qu'un simple 'apt-get install' ou un décompression d'un .zip sur un ftp;
  • sécurité: du fait du cloisonnement des services, la compromission d'un service particulier n'implique pas (ou moins facilement) la compromission de l'ensemble de la machine;
    • Nota: la sécurité des conteneurs est un point sur lequel je fais actuellement confiance aux paramètres par défaut. Le fait de lancer les conteneurs avec un utilisateur normal est plutôt favorable à la sécurité, même si une faille révélée dernièrement permettait facilement à un utilisateur normal de se procurer les droits administrateurs;
  • sauvegarde et remise en place grandement facilitée: le point qui m'intéresse le plus. Quand on a passé plusieurs heures à comprendre comment configurer un service, en cas de problème on est un peu contrarié: souvent on ne se souvient plus bien du travail fait à l'époque, sauf à avoir des notes, et on doit bien souvent de nouveau passer plusieurs heures à reconfigurer le service. Ainsi même si on a une sauvegarde des données, il y a un coût en temps assez important et démotivant. L'idée ici est de pouvoir faire périodiquement des sauvegardes du conteneur entier, de sorte que si on a un souci, la remise en place du service, éventuellement depuis une autre machine, se fait très rapidement;
  • un dernier usage est celui d'installer dans un environnement cloisonné des composants non libres/en lequel j'ai une confiance faible voire inexistante, de sorte que je peux les utiiser sans potentiellement compromettre l'intégralité de ma machine.

J'ai lorgné du côté des jails de FreeBSD au début de mon projet, mais mon utilisation de longue date de Debian et le coût lié à l'apprentissage d'un nouvel OS, sans la ceritude d'un réel avantage de ses jails sur les conteneurs sous Debian, a eu raison de ce choix.

J'ai vu passer sur mes flux RSS récemment quelques billets témoignant d'un certain intérêt pour les conteneurs LXC; dans la mesure où la mise n'a pas été immédiate pour moi, je partage mon expérience en espérant que cela puisse aider quelqu'un.

Plan de la potentielle série

  1. installation de l'environnement nécessaire à l'utilisation de conteneurs sous Debian Jessie
  2. utilisation des règles iptables et de nginx en reverse proxy pour rediriger correctement les flux vers les bons conteneurs

On traite ici du premier volet, le second volet viendra si je trouve le temps. Au pire, je mettrai ce que j'ai fait un peu en vrac, histoire au moins de présenter quelque chose qui a marché chez moi.

Sources

Les pages web qui m'ont été le plus utile à la mise en place des conteneurs chez moi:

Plan du présent article

  1. Environnement, paquets à installer, fichiers de configuration
  2. Gestion des conteneurs: création, sauvegarde, migration vers un autre hôte
  3. Divers

Mise en place

Hypothèses:

  • système Debian Jessie à jour, avec les backports disponibles parmi les sources (je suis parti sur la version 2.x de LXC, notamment parce qu'au moment où je tâtonnais ça m'a permis d'éliminer certaines sources potentielles de problèmes liés à la version 1.x; je ne sais plus quels problèmes, mais le fait est là).
  • l'utilisateur usant des conteneurs est 'sauron' (pour les contrôler tous, comprenez-vous), d'où les premières commandes;
  • les commandes envoyées en tant qu'administrateur sont précédées d'un dièse '#', les autres précédées d'un dollar '$' 'dans cet articles, toutes ces dernières sont exécutées par l'utilisateur créé, à savoir 'sauron').

Préliminaire

Note pour les changements d'utilisateur entre administrateur et utilisateur (étant supposé être administrateur initialement):

  • Connexion au compte utilisateur:

    # su sauron
    
  • Déconnexion de ce compte, pour revenir au compte root:

    $ exit
    

Commandes effectuées

Commandes commentées, dans l'ordre d'exécution

  1. Création de l'utilisateur pour les conteneurs:

    # adduser sauron
    
  2. Installation des paquets utiles; à noter que seul le paquet 'lxc' est absolument nécessaire à ma connaissance, les autres sont là pour fournir de la sécurité; je n'ai pas configuré ces derniers et n'ai pas encore creusé le sujet:

    # apt-get -t jessie-backports install lxc apparmor apparmor-profiles apparmor-profiles-extra apparmor-docs apparmor-utils
    
  3. Création des dossiers utiles:

    $ mkdir -p ~/.config/lxc  
    $ touch ~/.config/lxc/{lxc,default}.conf  
    $ mkdir -p ~/.local/share/{lxc,lxcsnaps}  
    $ mkdir -p ~/.cache/lxc
    
  4. Modif pour autoriser l'utilisation non privilégiée des conteneurs (pas spur de moi sur ce coup là, mais il me semble que c'est ça):

    # vim /etc/sysctl.d/80-lxc-userns.conf
    

    Ecrire dans ce nouveau fichier:

    kernel.unprivileged_userns_clone=1
    

    On prend en compte le changement que l'on vient d'effectuer:

    # sysctl --system
    # cat /proc/sys/kernel/unprivileged_userns_clone
    

    La dernière commande doit renvoyer "1"

  5. Pour les permissions:

    # apt-get install uidmap
    # usermod --add-subuids 100000-165536 sauron
    # usermod --add-subgids 100000-165536 sauron
    
  6. Configuration par défaut des conteneurs: on lui assigne des uids valides compte-tenu de ce qu'on vient d'attribuer à l'utilisateur. On en profite pour ajouter d'ores et déjà des lignes utiles au réseau:

    $ vim /home/sauron/.config/lxc/default.conf
    

    Dans ce nouveau fichier:

    lxc.id_map = u 0 100000 65536  
    lxc.id_map = g 0 100000 65536 
    lxc.network.type = veth
    lxc.network.link = lxcbr0
    lxc.network.flags = up
    
  7. Installation et configuration du paquet qui permet de gérer les accès au matériel (RAM, processeur,...) de la machine par les conteneurs non privilégiés:

    # apt-get install cgroup-tools
    # mkdir /etc/sysconfig
    # cp /usr/share/doc/cgroup-tools/examples/cgred.conf /etc/sysconfig/cgred.conf
    # vim /etc/cgconfig.conf
    

    Dans ce dernier fichier (/etc/cgconfig.conf), on renseigne la configuration suivante; on fera en particulier attention à la ligne "cpuset.cpus = 0-1;" dans le tutoriel de base c'était '0-3' car l'auteur a manifestement un quadri-coeur, quand je n'ai qu'un dual-core; attention donc à ne pas indiquer disposer de plus de coeurs que ce n'est le cas:

    group sauron {  
      perm {
        task {
          uid = sauron;
          gid = sauron;
        }
        admin {
          uid = sauron;
          gid = sauron;
        }
      }
    
      # All controllers available in 3.16.0-4
      # Listed by running: cat /proc/cgroups
      cpu {}
      blkio {}
      cpuacct {}
      cpuset {
        cgroup.clone_children = 1;
        cpuset.mems = 0;
        cpuset.cpus = 0-1;
      }
      devices {}
      freezer {}
      perf_event {}
      net_cls {}
      net_prio {}
    
      # The memory controller is not enabled by default in Debian Jessie despite being enabled in the kernel
      # If you enable it add the following
      memory { memory.use_hierarchy = 1; }
    }
    

    On modifie /etc/cgrules.conf:

    # vim /etc/cgrules.conf
    

    Dans ce fichier:

    # <user>:<process_name         <controllers> <destination>
    sauron   *        sauron
    

    On met en place le service systemd qui permet le démarrage et la gestion de cgconfig:

    # vim /lib/systemd/system/cgconfig.service
    

    Dans ce fichier:

    [Unit]
    Description=Control Group configuration service
    
    # The service should be able to start as soon as possible,
    # before any 'normal' services:
    DefaultDependencies=no  
    Conflicts=shutdown.target  
    Before=basic.target shutdown.target
    
    [Service]
    Type=oneshot  
    RemainAfterExit=yes  
    ExecStart=/usr/sbin/cgconfigparser -l /etc/cgconfig.conf -s 1664  
    ExecStop=/usr/sbin/cgclear -l /etc/cgconfig.conf -e
    
    [Install]
    WantedBy=sysinit.target
    

    # systemctl enable cgconfig

    Attention, ici écart avec le guide suivi: "systemctl start cgconfig" échoue. "cat /proc/cgroups" montre que 'memory' n'est pas activé dans mon cas; faire :

    # vim /etc/default/grub
    

    pour insérer

    GRUB_CMDLINE_LINUX_DEFAULT="cgroup_enable=memory swapaccount=1"
    

    juste avant la ligne en "GRUB_CMDLINE_LINUX=". On prend ensuite le changement en compte avec

    # update-grub
    

    puis on redémarre, et on vérifie que ça a fonctionné:

    # systemctl status cgconfig
    

    On créé un service également pour cgred:

    # vim /lib/systemd/system/cgred.service
    

    Dans ce fichier:

    [Unit]
    Description=CGroups Rules Engine Daemon  
    After=syslog.target
    
    [Service]
    Type=forking  
    EnvironmentFile=-/etc/sysconfig/cgred.conf  
    ExecStart=/usr/sbin/cgrulesengd $OPTIONS
    
    [Install]
    WantedBy=multi-user.target
    

    On l'active pour qu'il se lance automatiquement au démarrage de la machine, et on le démarre à la main pour cette fois ci:

    # systemctl enable cgred
    # systemctl start cgred
    
  8. On autorise notre utilisateur à créer des interfaces réseau virtuelles:

    # vim /etc/lxc/lxc-usernet
    

    Dans ce fichier:

    sauron veth lxcbr0 10
    
  9. On autorise l'exécution de l'ensemble des dossiers parents du dossier contenant les conteneurs:

    # chmod +x -R /home/sauron/
    

    pour que ça marche (cf https://www.stgraber.org/2014/01/17/lxc-1-0-unprivileged-containers/ pour une brève explication)

Voilà, normalement on a quelque chose de fonctionnel!

Problèmes rencontrés

Problème #1

Erreur obtenue au nouvel essai de création de conteneur avec sauron:

 Permission denied - failed to create directory '/run/user/0/lxc/'

Solution essayée:

unset XDG_RUNTIME_DIR XDG_SESSION_ID

Résout le problème :)

Problème #2

Message à la connexion au conteneur (via 'attach'):

"WARN: could not reopen tty: Permission denied" à la connexion au conteneur

(non résolu actuellement, pas encore trouvé la cause de l'erreur)

Problème #3

Arrivée sur un screen de login à la commande 'lxc-start -n $conteneur' et impossible de le quitter: il faut rajouter un '-d' pour le lancer en démon :

lxc-start -d -n $conteneur

(plus d'actualité chez moi, je ne sais pas pourquoi)

Utilisation

Création

Création du conteneur:

# su sauron
$ lxc-create -n test -t download
$ lxc-start -n test

Cela permet de créer un conteneur sur la base d'un template récupéré sur internet, conçu pour cette utilisation via LXC. A noter que pour un paranoïaque c'est bien moyen, dans la mesure où on ne sait pas exactement comment a effectivement été construit ce template.

Destruction

$ lxc-destroy -n test

Démarrage et connexion

$ lxc-start -n test 
$ lxc-attach -n test

Copie

Utile notamment pour les sauvegardes et la duplication d'un conteneur type qu'on aura pré-configuré:

$ lxc-copy -n test --newname=test_2

Lister ses conteneurs et leurs IP respectives

$ lxc-ls -f

Debug

En cas d'erreur au démarrage, pour comprendre:

$ lxc-start -F -n test

Le -F permet de tracer un peu ce qui se passe et, le cas échéant, pourquoi ça ne fonctionne pas.

Migration d'un conteneur

Suivant https://stackoverflow.com/questions/23427129/how-do-i-backup-move-lxc-containers : du 'tar -czvf' et du 'tar -xzvf'. Points importants, en supposant 'sauron' être l'utilisateur sur la machine hôte et 'web' être le nom du conteneur à bouger:

  • le faire depuis les dossiers contenant les conteneurs:

    $ cd /home/$user/.local/share/lxc/
    # tar --numeric-owner -czvf web.tar.gz ./web/*
    

    Attention, les options 'numeric-owner' dans les commandes ci-dessus et ci-dessous sont primordiales. Bouger ensuite l'archive dans /home/sauron/.local/share/lxc/ sur le nouvel hôte; puis depuis un shell sur la machine hôte:

    # cd /home/sauron/.local/share/lxc/
    # tar --numeric-owner -xzvf web.tar.gz
    # chown sauron:sauron web/web.log
    # chown sauron:sauron web/config
    # chown -R 100000:100000 web/rootfs
    
  • modifier le fichier de config pour modifier le chemin du rootfs:

    vim web/config
    

    on devra avoir une ligne : lxc.rootfs = /home/sauron/.local/share/lxc/web/rootfs

Divers

Exemple d'un fichier de configuration d'un conteneur

Contenu de mon fichier de configuration pour le conteneur 'web' (soit donc le fichier /home/sauron/.local/share/lxc/web/config). Je ne vais pas le commenter plus que ça dans la mesure où le sens que je peux lui donner est celui qui est évident (typiquement, on comprends assez bien la ligne en 'lxc.rootfs'), mais pour plus de détails je risque fort de ne pas être pertinent. Fichier donné à titre d'exemple uniquement donc.

lxc.include = /usr/share/lxc/config/debian.common.conf
lxc.include = /usr/share/lxc/config/debian.userns.conf
lxc.arch = x86_64
# Container specific configuration
lxc.id_map = u 0 100000 65536  
lxc.id_map = g 0 100000 65536 
# Network configuration
lxc.network.type = veth
lxc.network.link = lxcbr0
lxc.network.flags = up 
lxc.rootfs = /home/sauron/.local/share/lxc/web/rootfs
lxc.rootfs.backend = dir
lxc.utsname = web
# Autostart ; pour le démarrage automatique à la commande 'lxc-autostart' (permet de créer un service qui s'occupera de lancer le scponteneurs au démarrage)
lxc.start.auto = 1
lxc.start.delay = 5
lxc.start.order = 103
#lxc.cgroup.memory.limit_in_bytes = 512M
lxc.cgroup.memory.limit_in_bytes = 1G
lxc.cgroup.memory.memsw.limit_in_bytes = 2G

On notera qu'il est possible de renseigner dans ce fichier une limite imposée au conteneur en RAM, comme montré dans cet exemple (limite à 1G sur la RAM et à 2G sur la RAM+swap). Il n'est en revanche pas possible à ma connaissance de limiter en espace disque; quant au processeur je ne sais pas.

Conteneur pour application graphiques

Si on veut lancer une application graphique dans un conteneur en utilisant un affichage distant (celui du poste où s'exécute le conteneur, ou un autre), c'est comme pour n'importe quel serveur distant. Les paquets qui vont bien à installer sont les suivants:

apt-get install xauth x11-apps dbus dbus-x11

Il suffit ensuite de se connecter en SSH, avec le drapeau '-X' pour récupérer l'affichage:

ssh -X user@ip_conteneur

Pour ma part j'ai eu une erreur. Je vous mets texto ma note du moment; je n'ai plus le détail, mais les mot clé sont là:

erreur sur le 'pam' résolue en mettant 'no' en face de UsePAM (qu'est ce que ceci??)

Conclusion

Je vous ai donné dans ce billet quelques biles pour installer des conteneurs chez vous, en m'inspirant grandement d'un tutoriel existant. La valeur ajoutée de cet article par rapport au dit tutoriel est surtout d'être un peu plus au goût du jour (plus récent) et de faire un retour sur l'installation de LXC, et notamment des quelques soucis que j'ai pu rencontrer et des solutions que je leur ai trouvé. On notera deux grandes limites à ce billet:

  • je ne suis pas suffisamment calé pour affirmer comprendre tout ce que j'ai fait pour la mise en place;
  • pas de considération de sécurité pour le moment.

Si d'aventure ce billet a un peu de succès (comprendre: si quelque'un arrive au bout et manifeste son intérêt), je tâcherai de rédiger la suite de mon expérience sur le sujet, à savoir: quelques règles de base iptables et utilisation d'un Nginx comme reverse proxy, pour pouvoir servir tout ce petit monde de conteneurs de flux entrant depuis internet.

Vus : 1759
Publié par vhaguer : 5