Le conteneur LXD n'ouvre pas /dev/net/tun ? Vérifiez l'entrée unix-char
LXD doit transmettre le périphérique TUN de l'hôte au conteneur avant qu'un client VPN puisse l'utiliser. Voyez ce qu'ajoute unix-char, ce qu'il n'ajoute pas, et comment le vérifier.
LXD expose un périphérique à caractères de l'hôte à un conteneur en ajoutant un nœud de périphérique, non en apprenant à l'invité à en créer un. Si le conteneur n'a pas /dev/net/tun, l'hôte peut avoir le périphérique et l'invité ne pas pouvoir l'ouvrir pour autant. L'entrée unix-char est la moitié de ce chemin côté gestionnaire, et elle est délibérément plus étroite qu'un conteneur privilégié. C'est pourquoi le nœud de l'hôte et celui du conteneur méritent des vérifications distinctes.
Le mécanisme est assez court pour rester en vue : LXD résout un chemin de l'hôte, crée un nœud correspondant dans l'instance et ajoute le périphérique au cgroup de périphériques du conteneur. Le processus invité a ensuite besoin de sa propre autorisation et de sa propre capacité pour utiliser le nœud à quoi que ce soit.
Ce que l'entrée unix-char change réellement
Un périphérique unix-char demande à LXD d'exposer un périphérique à caractères de l'hôte sur un chemin choisi dans le conteneur. L'entrée comporte trois parties qui comptent : l'étiquette de périphérique utilisée par le gestionnaire, le chemin source sur l'hôte et le path que voit l'invité. Seules les deux dernières décrivent le périphérique lui-même. Une faute de frappe dans l'une ou l'autre produit un nœud à un emplacement que le client n'ouvre jamais.
Sur l'hôte, le périphérique est normalement /dev/net/tun. Dans le conteneur, vous pouvez garder le même chemin ou en choisir un autre, mais le chemin doit correspondre à ce qu'attend le client VPN. Conserver /dev/net/tun évite un second problème de configuration, car la plupart des clients ouvrent ce chemin sans demander.
Le gestionnaire applique l'entrée au démarrage de l'instance ou lors d'un branchement à chaud du périphérique. Il ne copie pas de pilote dans l'invité et ne crée pas d'interface de tunnel. Il rend le nœud visible et autorise l'accès via le groupe de contrôle des périphériques du conteneur.
Déclarez le périphérique sur l'instance, pas dans l'invité
Ajoutez l'entrée depuis le contexte d'administration de LXD, en nommant l'instance et une étiquette que vous reconnaîtrez plus tard. Pour un conteneur nommé vpnbox, la commande est :
lxc config device add vpnbox tun unix-char source=/dev/net/tun path=/dev/net/tun
lxc config device show vpnbox
L'étiquette tun est locale à la configuration de l'instance. Ce n'est pas un nom d'interface et elle n'a à correspondre à rien dans l'invité. Si un profil fournit déjà le même périphérique, ajouter une seconde entrée crée un conflit plutôt qu'une redondance, alors inspectez d'abord la configuration effective.
Modifier des fichiers dans le conteneur est la mauvaise couche. Un mknod dans un script de démarrage peut sembler fonctionner sur un conteneur privilégié, puis disparaître à la recréation ou échouer sur un conteneur moins privilégié. Le gestionnaire possède le jeu de périphériques de l'instance ; laissez-lui aussi posséder le nœud.
Alignez propriété et mode sur le processus invité
L'entrée unix-char peut définir uid, gid et mode pour le nœud dans le conteneur. Servez-vous-en pour correspondre au compte qui exécute le client VPN, au lieu d'ouvrir le périphérique à tous les utilisateurs ou de basculer le conteneur en mode privilégié.
La visibilité n'est pas la même chose que l'autorisation. Un nœud appartenant à root en mode 600 est visible par le service exécuté sous un utilisateur non privilégié et reste impossible à ouvrir par lui. Vérifiez d'abord le compte invité, puis attribuez la propriété à ce compte, et gardez un mode pas plus large que nécessaire.
Le processus a aussi besoin de la capacité de configurer une interface de tunnel, généralement CAP_NET_ADMIN dans l'espace de noms réseau du conteneur. Transmettre le périphérique n'accorde pas cette capacité, et accorder la capacité ne fournit pas le périphérique ; ce sont des exigences indépendantes.
Sachez ce que l'entrée n'accorde pas
Un nœud visible n'est pas un VPN qui fonctionne. Le paquet client doit encore être installé, le profil doit encore être valide et le service doit encore démarrer. L'authentification, les routes et le DNS restent le travail du client, et un échec à ce niveau ne prouve pas que l'entrée de périphérique est erronée.
systemd peut ajouter une autre restriction. Une unité avec DeviceAllow ou une politique de périphériques durcie peut refuser l'accès à un nœud fourni par le gestionnaire de conteneurs. Vérifiez la politique de périphériques de l'unité lorsque le nœud existe mais que le service signale une erreur d'autorisation, et changez cette politique au lieu d'élargir le conteneur.
Le branchement à chaud est facultatif et explicite. Une entrée avec required=false exige un chemin source et peut apparaître ou disparaître à mesure que le périphérique de l'hôte change. Ce cycle de vie convient au matériel qui va et vient ; pour un périphérique TUN dont le VPN a besoin au démarrage, une entrée facultative peut laisser le service démarrer avant que son prérequis existe.
Vérifiez visibilité, ouverture et interface dans cet ordre
Confirmez d'abord le nœud dans le conteneur visé. Le chemin, les numéros majeur et mineur et la propriété doivent correspondre au périphérique de l'hôte et à l'entrée que vous avez ajoutée. Un nœud absent signifie que le gestionnaire n'a pas appliqué l'entrée, ou l'a appliquée à une autre instance.
lxc exec vpnbox -- ls -l /dev/net/tun
lxc exec vpnbox -- sh -c 'test -c /dev/net/tun && echo char-device-ok'
Confirmez ensuite que le compte de service peut l'ouvrir. Un test court sous ce compte sépare un problème de propriété d'un problème de service. Enfin, démarrez le client et confirmez qu'une interface de tunnel apparaît ; seule cette troisième étape prouve tout le chemin, car les deux premières peuvent réussir alors que le profil ou la capacité du client est encore erroné.
Notez l'instance, l'étiquette du périphérique, la source sur l'hôte, le chemin dans l'invité et le compte propriétaire lorsque le changement est approuvé. Ces cinq valeurs sont ce qu'une recréation, une migration ou un changement de profil ultérieur doit préserver, et elles sont bien plus faciles à comparer qu'une erreur d'autorisation sans contexte.