LXC démystifié


Cet article décortique l'installation de LXC. Le but est de pouvoir créer des conteneurs qui ont accès à internet, et qu'on peut résoudre en nom-de-conteneur.lxc. Je vais faire de mon mieux pour avoir une approche aussi instructive que ludique, un petit peu dans le style du site du zéro pour ceux qui l'ont connu, même si le sujet reste peut-être un chouilla poilu pour ceux qui comme moi apprennent le réseau.

Les conteneurs

Un conteneur est un outil basique qui consiste en un dispositif qui enferme un espace partiellement ou complètement pour être utilisé pour contenir, stocker et transporter des objets ou matériels. Les choses gardées à l'intérieur du conteneur sont protégées en étant à l'intérieur de sa structure.

Les Humains utilisent des conteneurs depuis au moins 100 000 ans. Les premiers furent probablement inventés pour stocker de la nourriture, permettant aux premiers humains de garder leur nourriture plus longtemps, la transporter plus facilement, et la protéger des autres animaux.

Le développement des conteneurs à nourriture était d'une "immense importance pour l'évolution des populations", ainsi qu'une "attitude complètement innovante", jamais vu chez aucun autre primate.

LXC

LXC est une méthode de conteneurisation de système d'exploitation pour faire tourner plusieurs systèmes Linux isolés (conteneurs) sur un hôte de contrôle en utilisant un seul kernel.

La différence avec Docker, c'est que Docker est une méthode de conteneurisation d'application. Le premier processus que Docker exécute dans un conteneur, c'est celui de l'application. Pour LXC, c'est le logiciel d'init du système d'exploitation. Par exemple pour un conteneur Debian Jessie, le PID 1 est systemd.

Philosophiquement, LXC est plutôt comparable aux méthodes de virtualization. C'est à dire qu'on lance un système d'exploitation complet de manière isolée du système hôte. En réalité, on démarre bel et bien un autre os sur le même noyau.

Installation de LXC

Il suffit d'installer le paquetage lxc pour installer les commandes sur son système:

# Alpine Linux
apk add lxc lxc-templates
# Arch Linux
pacman -S lxc
# Fedora
dnf install lxc lxc-templates lxc-extra
# Gentoo
emerge app-emulation/lxc
# Ubuntu linux
apt-get install lxc lxc-net

Voila pour le petit tour des gestionnaires de paquetage. En aucun cas ceci ne doit être considéré comme exhaustif et à jour et vous devez donc aussi regarder la doc de votre distro au sujet de lxc.

Si vous n'avez pas encore choisi de distribution Linux, ou pour celles qui ne savent pas pourquoi on m'appelle "le roi des trolls" et parce qu'on avait dit que cet article devait avoir un coté ludique, voici un petit guide:

  • Gentoo pour détruire la couche d'ozone,
  • SourceMage pour faire l'originale,
  • Fedora pour le kiffe de Red Hat,
  • Debian pour apprendre Linux,
  • Ubuntu pour s'en servir,
  • Arch linux pour roxey du poney,
  • Alpine pour roxey du poney sans tête ("headless" server),

Comme je le disais, tout ce que vous avez à faire pour vous servir de lxc sur Ubuntu est:

sudo add-apt-repository --yes ppa:ubuntu-lxc/stable
sudo apt-get update
sudo apt-get install lxc dnsmasq lxc-dev

sudo sed -i 's/^#LXC_DOMAIN="lxc"/LXC_DOMAIN="lxc"/' /etc/default/lxc-net
sudo service lxc-net restart

echo server=/lxc/10.0.30.1 | sudo tee -a /etc/NetworkManager/dnsmasq.d/lxc
sudo service NetworkManager restart

Note

Debian stretch inclut également un service lxc-net.

Création de conteneur

La commande lxc-create permet de créer un conteneur. Cette commande prend en argument, notamment:

  • le nom du conteneur,
  • le nom du template (script bash) à utiliser pour le provisionner.
  • les arguments à passer au template, après un double tiret (--).

Par exemple, pour créer un conteneur Debian jessie, on peut utiliser une commande telle que:

lxc-create --name test --template debian -- --release jessie

!DANGER!

Attention tout de même au nom du conteneur. La commande lxc-create ne valide pas le nom qui doit pourtant respecter la RFC 952 pour que tout roule niveau réseau [a-z0-9.-]{1,24}. Exit donc les underscores et les noms trop longs.

Rootfs

C'est le script de template qui provisionne le rootfs, par défaut avec le backend dir dans /var/lib/lxc/nom-du-conteneur/rootfs/.

Á noter que l'une des techniques que nous avions utilisé pour accélérer l'exécution des tests automatiques est de créer le conteneur dans un tmpfs, qui est un système de fichier temporaire stocké en RAM.

D'autres backends de stockages sont disponibles, notamment LVM, Ceph RBD et ZFS. Ceux-ci ne seront pas couverts par cet article.

Templates

Dans l'exemple ci-dessus, c'est le template /usr/share/lxc/templates/lxc-debian qui sera utilisé avec l'option --release jessie pour créer le conteneur test.

Pour consulter la liste des options disponibles pour un template on peut utiliser -- --help:

lxc-create -n test -t debian --help

On obtient donc en sortie l'aide pour lxc-create et pour le template debian à la suite:

Usage: lxc-create --name=NAME --template=TEMPLATE [OPTION...]

lxc-create creates a container

Options :
  -n, --name=NAME               NAME of the container
  -f, --config=CONFIG           Initial configuration file
  -t, --template=TEMPLATE       Template to use to setup container
  -B, --bdev=BDEV               Backing store type to use
      --dir=DIR                 Place rootfs directory under DIR

  BDEV options for LVM (with -B/--bdev lvm):
      --lvname=LVNAME           Use LVM lv name LVNAME
                                (Default: container name)
      --vgname=VG               Use LVM vg called VG
                                (Default: lxc)
      --thinpool=TP             Use LVM thin pool called TP
                                (Default: lxc)

  BDEV options for Ceph RBD (with -B/--bdev rbd) :
      --rbdname=RBDNAME         Use Ceph RBD name RBDNAME
                                (Default: container name)
      --rbdpool=POOL            Use Ceph RBD pool name POOL
                                (Default: lxc)

  BDEV option for ZFS (with -B/--bdev zfs) :
      --zfsroot=PATH            Create zfs under given zfsroot
                                (Default: tank/lxc)

  BDEV options for LVM or Loop (with -B/--bdev lvm/loop) :
      --fstype=TYPE             Create fstype TYPE
                                (Default: ext3)
      --fssize=SIZE[U]          Create filesystem of
                                size SIZE * unit U (bBkKmMgGtT)
                                (Default: 1G, default unit: M)

Common options :
  -o, --logfile=FILE               Output log to FILE instead of stderr
  -l, --logpriority=LEVEL          Set log priority to LEVEL
  -q, --quiet                      Don't produce any output
  -P, --lxcpath=PATH               Use specified container path
  -?, --help                       Give this help list
      --usage                      Give a short usage message
      --version                    Print the version number

Mandatory or optional arguments to long options are also mandatory or optional
for any corresponding short options.

See the lxc-create man page for further information.

Template specific options can be passed to lxc-create after a '--' like this:

  lxc-create --name=NAME [-lxc-create-options] -- [-template-options]

Usage: /usr/share/lxc/templates/lxc-debian -h|--help -p|--path=<path> [-c|--clean] [-a|--arch=<arch>] [-r|--release=<release>]
                                     [--mirror=<mirror>] [--security-mirror=<security mirror>]
                                     [--packages=<package_name1,package_name2,...>]

Options :

  -h, --help             print this help text
  -p, --path=PATH        directory where config and rootfs of this VM will be kept
  -a, --arch=ARCH        The container architecture. Can be one of: i686, x86_64,
                         amd64, armhf, armel, powerpc. Defaults to host arch.
  -r, --release=RELEASE  Debian release. Can be one of: wheezy, jessie, stretch, sid.
                         Defaults to current stable.
  --mirror=MIRROR        Debian mirror to use during installation. Overrides the MIRROR
                         environment variable (see below).
  --security-mirror=SECURITY_MIRROR
                         Debian mirror to use for security updates. Overrides the
                         SECURITY_MIRROR environment variable (see below).
  --package=PACKAGE_NAME1,PACKAGE_NAME2,...
                         List of additional packages to install. Comma separated, without space.
  -c, --clean            only clean up the cache and terminate
  --enable-non-free      include also Debian's contrib and non-free repositories.

Environment variables:

  MIRROR                 The Debian package mirror to use. See also the --mirror switch above.
                         Defaults to 'http://httpredir.debian.org/debian'
  SECURITY_MIRROR        The Debian package security mirror to use. See also the --security-mirror switch above.
                         Defaults to 'http://security.debian.org/'

On regarde bien, et on voit donc les deux aides, celle de lxc-create et celle du template debian l'une à la suite de l'autre.

Aperçu du template debian

On voie à la fin du script de template /usr/share/lxc/templates/lxc-debian que ces fonctions sont appelées dans l'ordre:

install_debian "$rootfs" "$release" "$arch" "$LXC_CACHE_PATH"
configure_debian "$rootfs" "$name" $num_tty
copy_configuration "$path" "$rootfs" "$name" $arch $num_tty
configure_debian_systemd "$path" "$rootfs" "$config" $num_tty
post_process "${rootfs}" "${release}" "${arch}" "${hostarch}" "${packages}"

On va regarder un peu tout ça histoire de se rendre compte de tout le boulot qu'il y derrière et désillusionner celles qui croient que tout ça c'est magique.

install_debian

Cette première étape met en cache le rootfs de base pour ce template et ces options si il n'existe pas. Dans notre exemple, il s'agit de /var/cache/lxc/debian/rootfs-jessie-amd64/.

D'abord il importe la clefs GPG de la release si debian-archive-keyring n'est pas installé, dans notre exemple il utiliserait https://ftp-master.debian.org/keys/archive-key-8.asc pour provisionner /usr/share/keyrings/debian-archive-keyring.gpg manuellement si debian-archive-keyring n'est pas installé.

Ensuite, debootstrap télécharge les paquetages minimaux pour debian (--variant=minbase) dans le dossier de cache /var/cache/lxc/debian/rootfs-jessie-amd64/. Cette étape de téléchargement, de validation et d'extraction de binaires prend un petit peu de temps, mais n'est exécutée qu'une fois.

Enfin, /var/cache/lxc/debian/rootfs-jessie-amd64/ est copié pour devenir le rootfs du conteneur, par exemple dans /var/lib/lxc/test/rootfs/.

configure_debian

Cette fonction provisionne le rootfs ainsi:

  • mknod $rootfs/dev/tty{0,4},
  • écriture de $rootfs/etc/inittab,
  • lien de /proc/self/mounts de l'hôte vers $rootfs/etc/mtab.
  • désactivation de selinux avec echo 0 > "$rootfs/selinux/enforce",
  • configuration de eth0 en dhcp dans $rootfs/etc/network/interfaces,
  • régénération des locales avec chroot "$rootfs" locale-gen et chroot "$rootfs" update-locale après avoir écrit "$rootfs/etc/locale.gen" soit pour n'avoir que en_US.UTF-8 soit pour avoir $LANG,
  • désactivation des services inutiles dans le conteneur: checkroot.sh, umountfs, hwclock.sh, hwclockfirst.sh.
  • régénération des clefs d'hôte ssh,
  • copie de la timezone de l'hôte dans $rootfs/etc/timezone,
  • génération et output d'un mot de passe root.

Celles qui ont l'habitude installer des OS assez manuellement de type Gentoo Linux ou Arch reconnaitront globalement les étapes.

copy_configuration

Cette fonction génère la configuration du conteneur, soit /var/lib/lxc/test/config dans notre exemple, en plusieurs étapes:

  • si il n'y a qu'une seule interface réseau en veth dans la configuration, il s'assure qu'elle a une adresse MAC générée aléatoirement,
  • inclusion des fichiers /usr/share/lxc/config/debian.common.conf et /usr/share/lxc/config/debian.jessie.conf si ils existent en ajoutant lxc.include,
  • utilisation de /etc/lxc/default.conf, je n'ai pas trouvé le code source qui l'utilise (w00t ?) mais une chose est certaine c'est que les configurations de conteneurs incluent le contenu de ce fichier.
  • définition du nombre de ttys, du hostname et de l'arch dans la config.

configure_debian_systemd

Cette étape cuisine systemd dans le rootfs avec chroot, notamment en désactivant un certain nombre de services tels que udev et en fixant la configuration de ttys.

post_process

Installe l'architecture nécessaire si différente de l'hôte, écrit $rootfs/etc/apt/sources.list et installe les paquetages spécifiés en argument du template.

Conclusion

Ce n'est pas de tout repos que de créer un rootfs, on est bien content que LXC mette à disposition une variété de templates, notamment pour la distribution de serveur la plus light et sécurisée, qui est, comme chacune d'entre nous le sait, indéniablement: Alpine Linux.

Pour plus d'information, on peut consulter la liste des templates.

Configuration du conteneur

La config va par défaut à coté du rootfs, c'est à dire /var/lib/lxc/nom-du-conteneur/config. Il suffit de redémarrer le conteneur après l'avoir modifiée. Aussi, on peut changer les valeurs par défaut dans /etc/lxc/default.conf.

lxc.network

C'est la configuration de l'interface réseau du conteneur. Pour avoir accès à internet, les conteneurs doivent se connecter à une interface bridge sur l'hôte. Celle-ci peut avoir été créée par le paquetage (Ubuntu, paquetage lxc-net), alors on peu la créer manuellement, ce qu'on verra en détail dans la section "Configuration du réseau". Ici, nous nous contenterons d'examiner vite fait une configuration qui marche.

La commande brctl show permet d'obtenir la liste des interfaces bridges sur l'hôte:

$ brctl show
bridge name bridge id               STP enabled     interfaces
docker0             8000.02428c645c54       no
lxc             8000.fe29ecbfa16e   no              vethVH4RGS
lxd             8000.fe0df36fbbe4   no              veth56DNQM
                                        veth5KN2AM
                                        vethB5A3P7
                                        vethIDUBK3

Ici, on voit que j'ai trois interfaces bridges, une qui s'appelle lxc et une autre qui s'appelle lxd, et une pour docker. L'important, c'est d'avoir la bonne configuration réseau lxc.network par défaut:

lxc.network.type = veth
lxc.network.ipv4 = 0.0.0.0
# L'interface bridge est le link
lxc.network.link = lxc

Bien souvent, on verra lxcbr0 ou lxdbr0 dans les documentations, articles ou configurations par défaut. Personnellement, j'utilise Arch Linux et je fais ma config moi-même, avec plein d'amour dedans, et je préfère nommer mes interfaces lxc et lxd, parce que ce sont les mots que j'utilise partout dans mes configurations. Mais vous ferez bien comme vous voulez, car le but ici n'est pas de vous donner des trucs à copier-coller en mode tuto mais bel et bien s'instruire. On verra ça en détail dans le section de configuration réseau.

Ne vous inquiétez pas si vous n'avez pas d'interface bridge pour lxc, nous étudierons cela dans la section de configuration de l'hôte.

Démarrage du conteneur

La commande lxc-start permet de démarrer un conteneur LXC. L'important c'est de retenir que démarrer un conteneur LXC, c'est démarrer le programme d'init de sa distribution (en l'occurrence systemd) en PID1 dans ses namespaces:

  • mnt: mount point, systèmes de fichiers
  • pid: processus,
  • net: stack réseau,
  • ipc: System V IPC,
  • uts: nom d'hôte,
  • user: uids.

Concrètement, le PID1 du conteneur ne voie pas les mêmes mount points, processus, stack réseau, utilisateurs et nom d'hôte que l'hôte.

Pour plus de détails, je vous conseille la présentation de Rami Rosen, Resource Management: Linux kernel Namespaces and cgroups.

Executer dans le conteneur

Le concept de conteneur est très abstrait ici, puisque finalement, on a ici que des processus qui tournent dans des configurations différentes: root dir, namespaces, cgroups ...

On exécute pas vraiment une commande dans un conteneur, puisque c'est le kernel qui démarre les processus. On démarre donc un processus sur l'hôte avec le root fs du conteneur pour ensuite l'attacher aux bons namespaces et cgroups.

La commande qui abstrait tout ça pour l'utilisateur est lxc-attach:

lxc-attach -n nom-du-conteneur -- commande

Depuis la version 0.9, l'option --keep-env est activée par défaut. Cette option garde les variables d'environnement pour les processus attachés. Attention à cela car cela peut causer une fuite d'information indésirables dans le conteneur.

Par exemple, les utilisateurs d'Arch Linux n'ont plus besoin d'avoir /bin dans le $PATH depuis bien longtemps. Résultat, il manque pas mal de commandes quand on passe sur un conteneur d'une autre distro qui repose encore beaucoup sur /bin !

Cela dit, même si on en a pas besoin, je recommande tout de même de passer systématiquement --clear-env. Une autre raison de pratiquer cette option est qu'elle est vouée à être activée par défaut dans le futur.

Configuration du réseau

Introduction

Je serais franc avec vous: je ne suis pas expert réseau. Cela dit j'adore apprendre et voyons la réalité en face: si on veut participer à la construction d'internet, on sera plus crédible avec un minimum de compétences en réseau !

Pour une développeuse ou un développeur, cela peut sembler plus difficile voire être perçu comme un métier complètement différent qu'on ne peut pas faire parce qu'on a pas reçu la formation. En fait, j'aimerais vous convaincre que c'est presque plus facile que le développement, bien que l'approche soit fondamentalement différente et beaucoup plus concrète qu'abstraite, car bien entendu beaucoup plus bas niveau. On se retrouve plus à brancher des câbles réseau, définir des subnets et des routes, à partager son pool d'adresse ip qu'à instancier un objet de client http pour effectuer une requête et qui va complètement abstraire toutes ces histoires de câbles et d'adresses ips. On a pas le choix, on veut que les paquets réseaux aillent et viennent sur le réseau, en clair (ou pas) le procédé mental est plutôt zugzwang que zwischenzug.

On veut donc avoir des conteneurs qui ont une adresse IP et donc qui soient connectés sur un réseau. On a pas mal d'options ici, man 5 lxc.container.conf contient la liste exhaustive des options. On peut par exemple donner une interface physique au conteneur:

lxc.network.type = phys
lxc.network.link = eth2

C'est simple: on démarre le conteneur et il a une interface réseau physique, avec un vrai câble dedans, comme on a l'habitude. C'est sans doute très bien quand on a autant d'interfaces réseau et de câbles qu'on veut avoir de conteneurs. Dans ce cas, c'est terminé pour vous, vous pouvez arrêter de lire et reprendre une activité normale. Pour celles qui veulent pouvoir créer et détruire des conteneurs à tire larigot sans avoir à s'occuper de câblage physique par exemple sur un ordinateur portable l'aventure ne fait que commencer !

Aperçu

Pour que les conteneurs soient démarrés avec une interface ethernet virtuelle avec le module veth, on utilise lxc.network.type=veth. Comme ça lxc créée une interface réseau de type veth par conteneur. Comme il faut bien les connecter quelque part, on va se créer une interface de type bridge sur l'hôte, c'est à dire sur votre machine physique. Comme le dit man 8 brctl:

An ethernet bridge is a device commonly used to connect different networks of ethernets together, so that these ethernets will appear as one ethernet to the participants.

Une fois le bridge créé sur l'hôte, on peut demander à lxc de créer une interface veth dans le namespace du conteneur, connectée à l'interface bridge sur l'hôte par exemple lxcbr0, avec une configuration telle que celle que nous avons vu ci-dessus:

lxc.network.type = veth
lxc.network.link = lxcbr0

En faisant cela, c'est un peu comme si on branchait un câble entre l'interface réseau du conteneur et l'interface bridge de l'hôte. On a un lien de données ethernet niveau 2, mais toujours pas d'IP. C'est justement là qu'on a un choix à faire et donc que ça devient intéressant:

  • soit on connecte l'interface bridge à une interface physique, dans ce cas les veths qui passent par l'interface bridge se connecteront sur le réseau de l'interface physique, comme si c'était de vrais interfaces connectées directement sur le réseau,
  • soit, par soucis d'économie d'adresse ip, ou pour ne pas exposer les conteneurs sur le réseau physique, on peut choisir de ne connecter le bridge à aucune interface physique, et de faire router les paquets pas le kernel avec une configuration simple sur l'hôte: c'est comme si votre machine était une box / un router connecté sur internet et sur lequel se connectent différentes machines.

C'est à dire, dans le premier cas on a cette topologie réseau, où lxcbr0 fait le pont avec eth0, et donc toutes les interfaces veth connectées à l'interface de bridge se connectent directement sur le réseau physique, elles se "croient" câblées, on peut le représenter ainsi:

            [routeur physique]
            [   192.168.0.1  ]
            /        |        \
       physique   virtuel   virtuel
          /          |          \
       eth0-bridge-[veth123-----veth124]
     laptop        containr0    containr1
192.168.0.2        192.168.0.3  192.168.0.4

Pour imager, on peut dire que les interfaces veth "se croient" branchées par câble directement au routeur.

Dans la deuxième configuration proposée, on utilise l'hôte comme routeur avec un LAN virtuel de conteneurs:

  [routeur physique]
  [   192.168.0.1  ]
           |
         eth0
      192.168.0.2
           |
        laptop
           |
        lxcbr0
       10.0.30.1
       /      \
containr0    containr1
10.0.30.2    10.0.30.3

La première configuration n'étant pas très difficile, on va s'attaquer à la deuxième qui est beaucoup plus fun, puisqu'on doit configurer son laptop en router !

Pratique

Autoriser le kernel à router

On arrivera à router aucun paquet si la sysconfig net.ipv4.ip_forward est désactivée. Alors vérifions sa valeur:

$ sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 0

Donc on commence par activer cette conf, sur Arch Linux j'ai un petit fichier tout mignon dans /etc/sysctl.d/40-ip-forward.conf:

net.ipv4.ip_forward=1

Après je redémarre le service qui gère ça pour moi:

$ sudo systemctl restart systemd-sysctl.service

Et maintenant, je vérifie que ça marche:

$ sudo sysctl net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1

Super, ça devrait même persister au reboot, ce qui n'aurait pas été le cas si on avait juste activé cette conf en live avec une commande telle que:

$ sudo sysctl net.ipv4.ip_forward=1

Création de l'interface bridge

Certaines distributions telles que Ubuntu fournissent un service qui gère la création et la destruction de l'interface bridge, il s'agit du service lxc-net qui gère également les règles de routage avec iptables.

Sinon, on peut créer une interface bridge manuellement également:

sudo brctl addbr lxcbr0
# On assigne une ip a l'interface
sudo ip addr add 10.0.30.1 brd + dev lxcbr0

Rendre l'interface bridge persistente après un reboot

Systemd+netctl (Arch Linux)

Pour netctl, on crée un fichier de configuration pour l'interface dans /etc/netctl/lxcbr0:

Description="LXD Bridge"
Interface=lxc
Connection=bridge
BindsToInterfaces=()
IP=static
Address=10.0.30.1/24
SkipForwardingDelay=yes

On peut ensuite la contrôler avec systemd:

sudo netctl enable lxcbr0
sudo netctl start lxcbr0
Debian

Sur debian, c'est le script de networking qui gère les interfaces. Soit on ajoute la partie suivante dans /etc/network/interfaces, soit on s'assure que source-directory interfaces.d est présent dans ce fichier et on peut utiliser un autre fichier pour l'interface tel que /etc/network/interfaces.d/lxcbr0:

auto lxcbr0
iface lxcbr0 inet static
    bridge_ports none
    address 10.0.30.1
    netmask 255.255.255.0

On peut ensuite contrôler l'interface avec les scripts ifup et ifdown:

sudo ifup lxcbr0
sudo ifdown lxcbr0

Mon petit serveur dhcp et dns: dnsmasq

Comme on l'a vu, les conteneurs se connectent sur l'interface bridge, et le service réseau des conteneurs est configuré pour exécuter un client DHCP par défaut. Donc, il faut un serveur DHCP qui écoute sur cette interface et qui puisse attribuer des IPs aux conteneurs qui démarrent. La chronologie ressemblera à:

  • le PID1 du conteneur démarre dans le contexte du conteneur,
  • il démarre le service réseau,
  • le service réseau active l'interface virtuelle du bridge et lance un client dhcp dessus,
  • le serveur dnsmasq qui tourne sur l'hôte et écoute sur le bridge fournit une l'adresse IP, un serveur DNS (lui-même) et une route par défaut.

Puisque les conteneurs seront branchés sur l'interface bridge, lxcbr0, c'est également sur cette interface qu'il faut que le serveur dnsmasq qui servira aux conteneurs lxc écoute.

Donc la première étape est de s'assurer qu'on a bien un serveur dnsmasq qui tourne sur l'hôte. J'aime bien faire un coup de ps aux | grep dnsmasq pour cela, car c'est la pure vérité qui en sort, et là on voit tout de suite la config de beaugosse des alpages:

dnsmasq   1881  0.0  0.0  48060  4092 ?        Ss   Sep27   0:04 /usr/bin/dnsmasq -k -C /etc/dnsmasq-lxc.conf --enable-dbus=uk.org.thekelleys.dnsmasq.lxc --user=dnsmasq --pid-file
dnsmasq   1900  0.0  0.0  48060  4048 ?        Ss   Sep27   0:00 /usr/bin/dnsmasq -k -C /etc/dnsmasq-lxd.conf --user=dnsmasq --pid-file --enable-dbus=uk.org.thekelleys.dnsmasq.lxd
nobody    2515  0.0  0.0  48060  4004 ?        S    Sep27   0:01 /usr/bin/dnsmasq --no-resolv --keep-in-foreground --no-hosts --bind-interfaces --pid-file=/var/run/NetworkManager/dnsmasq.pid --listen-address=127.0.0.1 --cache-size=400 --conf-file=/dev/null --proxy-dnssec --enable-dbus=org.freedesktop.NetworkManager.dnsmasq --conf-dir=/etc/NetworkManager/dnsmasq.d
jpic     10538  0.0  0.0  10760  2268 pts/13   S+   11:29   0:00 grep --color=auto dnsmasq

Retenez bien que la vérité est dans l'output de cette commande. On voie quels dnsmasq tournent avec quelles options et quelles configs. C'est par là qu'on commence à inspecter pour voir si tout correspond bien à ce qu'on veut. Vous pouvez ignorer la config lxd pour l'instant, sachez juste que c'est la même que celle pour lxc sauf qu'on remplace lxc par lxd et qu'on a une plage d'ips différentes.

On a donc ici 3 dnsmasq qui tournent:

  • un pour les conteneurs lxc,
  • un pour les lxd,
  • un pour l'hôte.

Celui pour lxc permet donc aux conteneurs de se connecter, et d'avoir un nom resolvable en .lxc. Celui sur l'hôte permet d'"aiguiller" votre laptop:

  • transférer les demandes de résolutions de .lxc vers le dnsmasq de lxc,
  • transférer les demandes de résolutions de .lxd vers le dnsmasq de lxc,
  • transférer les demandes customs vers des serveurs dns custom, parfait si vous vous connectez parfois sur un vpn,
  • transférer le reste des demandes vers un serveur dns de votre choix, si vous n'avez pas peur que google mange vos petits enfants alors peut-être opterez vous pour 8.8.8.8.

A noter que dnsmasq n'a pas besoin de fichier de config, on peut setter toutes les options en ligne de commande. C'est ce que le dnsmasq de l'hôte qui peut être géré par la distribution peut faire. Dans ce cas on ira toucher un fichier plutôt dans /etc/default du style de /etc/default/dnsmasq pour bidouiller, c'est notamment le cas sur Ubuntu.

Nous étudierons en détail pourquoi cette config est si belle gosse au fur et à mesure et vous apprendrez vous aussi à être des belles gosses du dnsmasq au terme de ce chapitre. Pour commencer nous nous contenterons de démarrer dnsmasq au premier plan ("foreground"). Vérifiez qu'à ce stade, vous êtes bien capable de faire des lxc-create, lxc-start, lxc-stop et que vous avez un conteneur de test.

dnsmasq: enable-dbus

Si vous comptez intégrer vos démons dnsmasq avec systemd, de l'amour et du perfectionnisme, le premier truc à retenir c'est qu'on ne peut pas laisser l'option enable-dbus par défaut sur les dnsmasq qu'on gère nous même. L'option enable-dbus, comme le dit si bien man 8 dnsmasq:

-1, --enable-dbus[=<service-name>]
       Allow dnsmasq configuration to be updated via DBus method calls.
       The  configuration  which  can  be  changed  is  upstream  DNS
       servers (and corresponding domains) and cache clear. Requires
       that dnsmasq has been built with DBus support.  If  the
       service  name is given, dnsmasq provides service at that name,
       rather than the default which is uk.org.thekelleys.dnsmasq

D'ailleurs, vous ne devrez jamais avoir plusieurs dnsmasq qui tournent sur le même service-name dbus. Du coup, si vous avez un dnsmasq sur l'hôte pour votre interface physique, et cela sera le cas comme nous le verrons plus loin, vous devez soit:

  • désactiver l'option dbus sur les dnsmasq que vous gérer,
  • l'activer avec un service-name bien unique pour qu'il n'y ait pas de conflit

Pour l'instant on va pas s'en occuper, mais j'ai choisi de mettre ceci en avant au début car c'est source d'un bon nombre de WTF quand on a pas fait ça correctement.

démarrer dsnmasq

On crée un conteneur de test:

$ sudo lxc-create -t debian -n foobar -- -r jessie
debootstrap is /usr/bin/debootstrap
Checking cache download in /var/cache/lxc/debian/rootfs-jessie-amd64 ...
Copying rootfs to /var/lib/lxc/foobar/rootfs...Generating locales (this might take a while)...
  en_US.UTF-8... done
  en_US.UTF-8... done
Generation complete.
insserv: warning: current start runlevel(s) (empty) of script `checkroot.sh' overrides LSB defaults (S).
insserv: warning: current stop runlevel(s) (S) of script `checkroot.sh' overrides LSB defaults (empty).
insserv: warning: current start runlevel(s) (empty) of script `checkroot.sh' overrides LSB defaults (S).
update-rc.d: error: umountfs Default-Start contains no runlevels, aborting.
insserv: warning: current start runlevel(s) (empty) of script `hwclock.sh' overrides LSB defaults (S).
insserv: warning: current stop runlevel(s) (0 6 S) of script `hwclock.sh' overrides LSB defaults (0 6).
update-rc.d: error: cannot find a LSB script for hwclockfirst.sh
Creating SSH2 RSA key; this may take some time ...
2048 27:86:ec:14:5d:06:3d:85:3a:46:86:b8:2f:fa:e1:08 /etc/ssh/ssh_host_rsa_key.pub (RSA)
Creating SSH2 DSA key; this may take some time ...
1024 e4:17:74:ca:fc:e8:72:c1:13:1d:08:76:6c:61:23:3e /etc/ssh/ssh_host_dsa_key.pub (DSA)
Creating SSH2 ECDSA key; this may take some time ...
256 dd:c6:96:de:48:9a:de:4a:f0:f2:6e:ef:4e:5b:60:ff /etc/ssh/ssh_host_ecdsa_key.pub (ECDSA)
Creating SSH2 ED25519 key; this may take some time ...
256 24:0b:9d:0a:7b:00:db:f9:55:24:7e:74:32:8b:dc:c7 /etc/ssh/ssh_host_ed25519_key.pub (ED25519)
Failed to read /proc/cmdline. Ignoring: No such file or directory
invoke-rc.d: policy-rc.d denied exécution of start.
Timezone in container is not configured. Adjust it manually.
Root password is 'root', please change !

On démarre le conteneur et on attache un shell dessus pour l'inspecter:

$ sudo lxc-start -n foobar
$ sudo lxc-attach -n foobar --clear-env

On voie que l'interface veth n'a pas d'ipv4:

root@foobar:/# ip a
39: eth0@if40: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 00:16:3e:74:0f:e3 brd ff:ff:ff:ff:ff:ff
inet6 fe80::216:3eff:fe74:fe3/64 scope link
   valid_lft forever preferred_lft forever

root@foobar:/# ifconfig
eth0      Link encap:Ethernet  HWaddr 00:16:3e:74:0f:e3
          inet6 addr: fe80::216:3eff:fe74:fe3/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:40 errors:0 dropped:0 overruns:0 frame:0
          TX packets:15 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:4504 (4.3 KiB)  TX bytes:3042 (2.9 KiB)

Qu'on a aucune route:

root@foobar:/# ip route

Et qu'on arrive pas à résoudre internet:

root@foobar:/# getent hosts google.com

Alors démarrons un dnsmasq sur l'hôte:

$ sudo dnsmasq \
    --keep-in-foreground \
    --log-queries \
            --log-dhcp \
            --log-facility=/tmp/dnsmasq-lxc.log \
    --except-interface=lo \ # toi pas binder 127.0.0.1
    --bind-interfaces \     # toi binder que l'interface
    --interface=lxcbr0 \    # l'interface que toi binder port 53 et 68
    --dhcp-leasefile=/tmp/dnsmasq-lxc.leases \ # toi ranger leases
    --domain=lxc \          # toi donner suffixe .lxc aux hostnames
    --local=/lxc/ \         # toi résoudre que les .lxc par toi même
    --dhcp-range=10.0.30.2,10.0.30.254,12h # toi assigner ce range

On redémarre le réseau dans le conteneur et là, surprise, ça ne marche toujours pas:

root@foobar:/# systemctl restart networking

Pourtant, dans les logs du conteneur on voit bien qu'il essaye:

root@foobar:/# journalctl -f
-- Logs begin at Fri 2016-09-30 10:14:14 UTC. --
Sep 30 10:15:49 foobar dhclient[123]: Sending on   Socket/fallback
Sep 30 10:15:49 foobar dhclient[123]: DHCPDISCOVER on eth0 to 255.255.255.255 port 67 interval 4
Sep 30 10:15:49 foobar networking[111]: Listening on LPF/eth0/00:16:3e:74:0f:e3
Sep 30 10:15:49 foobar networking[111]: Sending on   LPF/eth0/00:16:3e:74:0f:e3
Sep 30 10:15:49 foobar networking[111]: Sending on   Socket/fallback
Sep 30 10:15:49 foobar networking[111]: DHCPDISCOVER on eth0 to 255.255.255.255 port 67 interval 4
Sep 30 10:15:53 foobar dhclient[123]: DHCPDISCOVER on eth0 to 255.255.255.255 port 67 interval 7
Sep 30 10:15:53 foobar networking[111]: DHCPDISCOVER on eth0 to 255.255.255.255 port 67 interval 7
Sep 30 10:16:00 foobar dhclient[123]: DHCPDISCOVER on eth0 to 255.255.255.255 port 67 interval 9
Sep 30 10:16:00 foobar networking[111]: DHCPDISCOVER on eth0 to 255.255.255.255 port 67 interval 9
Sep 30 10:16:09 foobar dhclient[123]: DHCPDISCOVER on eth0 to 255.255.255.255 port 67 interval 19
Sep 30 10:16:09 foobar networking[111]: DHCPDISCOVER on eth0 to 255.255.255.255 port 67 interval 19
^C

Et dans ceux du dnsmasq de l'hôte, qu'il répond:

$ sudo tail -f /tmp/out
Sep 30 12:16:41 dnsmasq-dhcp[14412]: 1302916205 DHCPOFFER(lxc) 10.0.30.239 00:16:3e:74:0f:e3
Sep 30 12:16:41 dnsmasq-dhcp[14412]: 1302916205 requested options: 1:netmask, 28:broadcast, 2:time-offset, 3:router,
Sep 30 12:16:41 dnsmasq-dhcp[14412]: 1302916205 requested options: 15:domain-name, 6:dns-server, 119:domain-search,
Sep 30 12:16:41 dnsmasq-dhcp[14412]: 1302916205 requested options: 12:hostname, 44:netbios-ns, 47:netbios-scope,
Sep 30 12:16:41 dnsmasq-dhcp[14412]: 1302916205 requested options: 26:mtu, 121:classless-static-route, 42:ntp-server
Sep 30 12:16:41 dnsmasq-dhcp[14412]: 1302916205 next server: 10.0.30.1
Sep 30 12:16:41 dnsmasq-dhcp[14412]: 1302916205 sent size:  1 option: 53 message-type  2
Sep 30 12:16:41 dnsmasq-dhcp[14412]: 1302916205 sent size:  4 option: 54 server-identifier  10.0.30.1
Sep 30 12:16:41 dnsmasq-dhcp[14412]: 1302916205 sent size:  4 option: 51 lease-time  12h
Sep 30 12:16:41 dnsmasq-dhcp[14412]: 1302916205 sent size:  4 option: 58 T1  6h
Sep 30 12:16:41 dnsmasq-dhcp[14412]: 1302916205 sent size:  4 option: 59 T2  10h30m
Sep 30 12:16:41 dnsmasq-dhcp[14412]: 1302916205 sent size:  4 option:  1 netmask  255.255.255.0
Sep 30 12:16:41 dnsmasq-dhcp[14412]: 1302916205 sent size:  4 option: 28 broadcast  10.0.30.255
Sep 30 12:16:41 dnsmasq-dhcp[14412]: 1302916205 sent size:  4 option:  3 router  10.0.30.1
Sep 30 12:16:41 dnsmasq-dhcp[14412]: 1302916205 sent size:  4 option:  6 dns-server  10.0.30.1
Sep 30 12:16:41 dnsmasq-dhcp[14412]: 1302916205 sent size:  3 option: 15 domain-name  lxc
^C

Si les paquets partent bien du conteneur vers le dnsmasq de lxc sur l'hôte et que celui-ci répond correctement, la première hypothèse à considérer est simple: le client dhcp du conteneur ne reçoit pas les réponses.

Ok, alors on va écouter ce qui se passe sur l'interface bridge par laquelle tout le réseau entre le conteneur et l'hôte se passe:

$ sudo tcpdump -i lxcbr0 -v

On voie la requête DHCP qui part du conteneur vers le monde:

IP (tos 0x10, ttl 128, id 0, offset 0, flags [none], proto UDP (17), length 328)
0.0.0.0.bootpc > 255.255.255.255.bootps: [udp sum ok] BOOTP/DHCP, Request from 00:16:3e:74:0f:e3 (oui Unknown), length 300, xid 0x8cbae537, Flags [none] (0x0000)

Super, dnsmasq reçoit la requête, et on voit vite sa réponse partir:

12:30:38.628059 IP (tos 0xc0, ttl 64, id 60890, offset 0, flags [none], proto UDP (17), length 328)
    lue.bootps > 10.0.30.239.bootpc: [bad udp cksum 0x5235 -> 0x3bc3!] BOOTP/DHCP, Reply, length 300, xid 0x8cbae537, Flags [none] (0x0000)

Et là, surprise: ça ne fonctionne pas, le client dhcp tourne en boucle dans le conteneur malgré les réponses répétées par dnsmasq. Nous allons d'abord réparer cela, puis nous reviendrons sur la persistance de la configuration du dnsmasq de lxc.

Le drame du DHCP sur bridge

Avez-vous eu l'oeil ?

12:30:38.628059 IP (tos 0xc0, ttl 64, id 60890, offset 0, flags [none], proto UDP (17), length 328)
    lue.bootps > 10.0.30.239.bootpc: [bad udp cksum 0x5235 -> 0x3bc3!] BOOTP/DHCP, Reply, length 300, xid 0x8cbae537, Flags [none] (0x0000)
                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                                      et là c'est le drame

Le protocole UDP fournit des checksums pour vérifier l'intégrité des paquets reçus. Le checksum qui permet au receveur de vérifier qu'il a bien reçu le paquet correctement: on ne sait jamais, peut-être qu'une souris grignote le câble réseau et avale des bits sur le chemin. Comme on dit une mouche qui pête peut corrompre un paquet qui passe en wifi. Du coup, le paquet n'est pas pris en compte ou quelque chose comme ça, je ne suis pas sûr. Mais je suis sûr d'un truc: il n'y a pas de petite souris avaleuse de bit dans mon laptop. Bref, résultat des courses, le client DHCP ne considère pas sa requête comme satisfaite et réessaye:

12:30:41.091262 IP (tos 0x10, ttl 128, id 0, offset 0, flags [none], proto UDP (17), length 328)
    0.0.0.0.bootpc > 255.255.255.255.bootps: [udp sum ok] BOOTP/DHCP, Request from 00:16:3e:74:0f:e3 (oui Unknown), length 300, xid 0x8cbae537, secs 3, Flags [none] (0x0000)

Le dnsmasq qui écoute sur le bridge reçoit et répond à nouveau:

12:30:41.091692 IP (tos 0xc0, ttl 64, id 61447, offset 0, flags [none], proto UDP (17), length 328)
lue.bootps > 10.0.30.239.bootpc: [bad udp cksum 0x5235 -> 0x3bc0!] BOOTP/DHCP, Reply, length 300, xid 0x8cbae537, secs 3, Flags [none] (0x0000)

Et ainsi de suite, comme on dit "sa marche pas".

C'est tout simplement parce que les interfaces bridges, aussi cools et fun soient elles, se basent sur un bon gros hack sorti de par derrière les fagots par nos amis (nos idoles ? nos dieux ?) les kernel hackers. Pour que cette histoire de brancher plusieurs interfaces sur une seule fonctionne, le kernel doit réécrire une partie du paquet, et ensuite le checksum n'est plus compatible.

Heureusement, netfilter va nous sauver. Nombreux sont les moyens de gérer sa config netfilter. De nos jours, le plus stable et fonctionnel des moyens est iptables. On va donc essayer l'option netfilter de --checksum-fill:

sudo iptables -t mangle -A POSTROUTING -s 10.0.30.0/24 -p udp -m udp --dport 68 -m comment --comment "Needed for LXC containers to get an ip from the host" -j CHECKSUM --checksum-fill

Et là, c'est la fête, les paquets repartent bien avec un checksum qui passe:

12:31:41.824302 IP (tos 0x10, ttl 128, id 0, offset 0, flags [none], proto UDP (17), length 328)
12:31:41.824927 IP (tos 0xc0, ttl 64, id 4501, offset 0, flags [none], proto UDP (17), length 328)
    lue.bootps > 10.0.30.239.bootpc: [udp sum ok] BOOTP/DHCP, Reply, length 300, xid 0x299da841, Flags [none] (0x0000)

Du coup, le container reçoit l'ip 10.0.30.239 et la route par défaut via le bridge 10.0.30.1, le client dhcp est content, et on peut résoudre les domaines externes:

$ sudo lxc-attach -n foobar  --clear-env -- ip a
53: eth0@if54: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether 00:16:3e:74:0f:e3 brd ff:ff:ff:ff:ff:ff
    inet 10.0.30.239/24 brd 10.0.30.255 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::216:3eff:fe74:fe3/64 scope link
       valid_lft forever preferred_lft forever

$ sudo lxc-attach -n foobar  --clear-env -- ip route
default via 10.0.30.1 dev eth0
10.0.30.0/24 dev eth0  proto kernel  scope link  src 10.0.30.239

root@foobar:/# getent hosts google.com
2a00:1450:400c:c06::64 google.com
Rendre les règles iptables persistentes après un reboot

Tout ça c'est bien gentil, mais alors Jamy est-ce que la règle va persister après un reboot ? Et bien non, il faut sauvegarder les règles dans un fichier et activer un service pour le charger au démarrage. Sur Arch Linux rien de plus facile, le paquet iptables installe un service iptables qu'on regarde comme ça:

$ sudo systemctl status iptables
● iptables.service - Packet Filtering Framework
   Loaded: loaded (/usr/lib/systemd/system/iptables.service; enabled; vendor preset: disabled)
   Active: active (exited) since Tue 2016-09-27 16:28:20 CEST; 2 days ago
  Process: 1711 ExecStart=/usr/bin/iptables-restore /etc/iptables/iptables.rules (code=exited,
 Main PID: 1711 (code=exited, status=0/SUCCESS)
    Tasks: 0 (limit: 4915)
   Memory: 0B
      CPU: 0
   CGroup: /system.slice/iptables.service

Sep 27 16:28:20 lue systemd[1]: Starting Packet Filtering Framework...
Sep 27 16:28:20 lue systemd[1]: Started Packet Filtering Framework.

L'important ici c'est de voir la ligne:

Process: 1711 ExecStart=/usr/bin/iptables-restore /etc/iptables/iptables.rules
                                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                                                  chemin du fichier de conf

Alors voici une manière de le mettre à jour:

sudo iptables-save |& sudo tee /etc/iptables/iptables.rules

On n'oublie pas d'activer le service quand même:

$ sudo systemctl enable iptables

Referez vous à la doc iptables de votre distro de belle gosse pour les détails qui vous concernent.

Debian networking

On peut également gérer ces règles avec le système de networking de Debian, avec quelque chose comme cela dans /etc/network/interfaces.d/lxcbr0:

auto lxcbr0
iface lxcbr0 inet static
    bridge_ports none
    address 10.0.30.1
    netmask 255.255.255.0
    post-up iptables -A FORWARD -i $IFACE -s 10.0.30.0/24 -j ACCEPT
    post-down iptables -D FORWARD -i $IFACE -s 10.0.30.0/24 -j ACCEPT
    post-up iptables -A POSTROUTING -t nat -s 10.0.30.0/24 ! -d 10.0.30.0/24 -j MASQUERADE
    post-down iptables -D POSTROUTING -t nat -s 10.0.30.0/24 ! -d 10.0.30.0/24 -j MASQUERADE
    post-up iptables -A POSTROUTING -t mangle -p udp --dport bootpc -s 10.0.30.0/24 -j CHECKSUM --checksum-fill
    post-down iptables -D POSTROUTING -t mangle -p udp --dport bootpc -s 10.0.30.0/24 -j CHECKSUM --checksum-fill

Personnellement je ne suis pas fan de cette méthode que j'ai beaucoup utilisé pour des serveurs de CI en debian, parce qu'on se retrouve souvent à nettoyer le système à la main. Par exemple, si l'une des commandes post-up échoue, alors ifup échouera aussi. Dans ce cas il y a de grandes chances que ifdown échoue aussi parce que quelque chose n'a pas été créé. La solution dans ce cas est de nettoyer et tout remettre en ordre à la main. Cela dit, certains de mes collègues adorés en sont satisfaits et c'est peut-être un peu plus simple que de gérer chaque pièce du puzzle séparément.

Accès à internet depuis le conteneur

Les conteneurs sont sur le réseau du bridge, mais ont-ils accès à internet ? Un simple test de apt-get update bloquera ainsi:

root@foobar:/# apt-get update
0% [Connecting to httpredir.debian.org (5.153.231.35)] [Connecting to security.debian.org (212

Effectivement, les paquets réseau ne dépassent pas l'interface de bridge. On peut configurer netfilter ainsi:

sudo iptables -A FORWARD -s 10.0.30.0/24 -i lxc -j ACCEPT -m comment --comment "DNAT/SNAT everything going out of LXC subnet" -m comment "Forward any packet from lxc subnet and interface"
sudo iptables -t nat -A POSTROUTING -s 10.0.30.0/24 -j MASQUERADE -m comment --comment "DNAT/SNAT everything going out of LXC subnet"
Résolution des conteneurs à partir de l'hôte

Á ce stade, vous avez une configuration fonctionnelle. Cependant, il reste une fonctionnalité très pratique et primordiale, c'est la possibilité de résoudre les conteneurs à partir de leur nom de domaine en .lxc assignés par le dnsmasq de lxc.

Pour cela, nous configurons le dnsmasq principal de votre machine, soit le service dnsmasq du paquetage dnsmasq de votre distro, soit celui qui est géré par NetworkManager, ce qui sera probablement le cas sur les laptops:

  • /etc/dnsmasq.d/lxcbr0 ou /etc/dnsmasq.conf et systemctl restart dnsmasq dans le premier cas,
  • /etc/NetworkManager/dnsmasq.d/lxc.conf et systemctl restart NetworkManager dans le second.

Il suffit d'ajouter une ligne à sa configuration, pour lui dire de transférer les requêtes de résolution de nom en .lxc au dnsmasq de lxc:

server=/lxc/10.0.30.1
Persistance de la configuration de dnsmasq de lxc

Nous allons commencer par simplifier la ligne de commande qui démarre le processus dnsmasq pour lxc en utilisant un fichier de configuration, par exemple /etc/dnsmasq-lxc.conf:

log-queries
log-dhcp
except-interface=lo
bind-interfaces
interface=lxcbr0
dhcp-leasefile=/tmp/dnsmasq-lxc.leases
domain=lxc
local=/lxc/
dhcp-range=10.0.30.2,10.0.30.254,12h

Puis, on ajoute un fichier de service, par exemple avec systemd dans /etc/systemd/system/dnsmasq@lxc.service:

[Unit]
Description=Dnsmasq for lxc
After=network.target
# Pour les utilisateurs de netctl
Requires=netctl@lxc.service
Documentation=man:dnsmasq(8)

[Service]
Type=dbus
BusName=uk.org.thekelleys.dnsmasq.lxc
ExecStartPre=/usr/bin/dnsmasq --test -C /etc/dnsmasq-lxc.conf
ExecStart=/usr/bin/dnsmasq -k -C /etc/dnsmasq-lxc.conf --enable-dbus=uk.org.thekelleys.dnsmasq.lxc --user=dnsmasq --pid-file
ExecReload=/bin/kill -HUP $MAINPID

[Install]
WantedBy=multi-user.target
Debian networking

Encore une fois, on peut également confier la gestion de l'instance de dnsmasq pour lxc au script de networking de Debian:

auto lxcbr0
iface lxcbr0 inet static
    bridge_ports none
    address 10.0.30.1
    netmask 255.255.255.0
    post-up iptables -A FORWARD -i $IFACE -s 10.0.30.0/24 -j ACCEPT
    post-down iptables -D FORWARD -i $IFACE -s 10.0.30.0/24 -j ACCEPT
    post-up iptables -A POSTROUTING -t nat -s 10.0.30.0/24 ! -d 10.0.30.0/24 -j MASQUERADE
    post-down iptables -D POSTROUTING -t nat -s 10.0.30.0/24 ! -d 10.0.30.0/24 -j MASQUERADE
    post-up iptables -A POSTROUTING -t mangle -p udp --dport bootpc -s 10.0.30.0/24 -j CHECKSUM --checksum-fill
    post-down iptables -D POSTROUTING -t mangle -p udp --dport bootpc -s 10.0.30.0/24 -j CHECKSUM --checksum-fill
    post-up dnsmasq --interface=$IFACE --conf-file=/etc/dnsmasq-$IFACE.conf --pid-file=/var/run/lxc-dnsmasq.$IFACE.pid
    post-down kill $(cat /var/run/lxc-dnsmasq.$IFACE.pid)

Merci

N'hésitez pas à m'envoyer vos commentaires car il est possible que des erreurs se soient glissées dans cet article, moi-même j'apprends le réseau et je ne suis pas à l'abri d'avoir écrit une bêtise !

Merci d'avoir lu cet article, je serai heureux d'apprendre qu'il vous a permis de progresser et mieux comprendre lxc ansi que ses implications réseau voire un peu de système.