/dev/net/tun absent alors que tun est chargé : réparer le nœud de périphérique
Un module tun chargé ne prouve pas que le nœud de périphérique existe. Séparez le pilote du nœud, reconstruisez-le dans l'ordre et vérifiez-le avec une interface.
OpenVPN s'arrête sur une plainte errno 2 à propos de /dev/net/tun alors que lsmod liste allègrement le module tun. Les deux faits sont vrais en même temps. Le module fournit un pilote, et le chemin qu'ouvre le client est un nœud de périphérique que quelque chose d'autre doit créer. Les traiter comme un seul objet est la raison pour laquelle la réparation évidente consistant à recharger le module ne change absolument rien.
Deux objets, deux propriétaires différents
Un module du noyau et un nœud de périphérique sont des objets indépendants avec des cycles de vie indépendants. Le chargement du module enregistre un pilote, et sur un système doté de devtmpfs le noyau demande la création du nœud correspondant. Sur un système sans devtmpfs, rien ne crée quoi que ce soit, et le pilote reste là sans aucun chemin qui pointe vers lui. Le module peut être chargé, sain et totalement inaccessible via le système de fichiers au même moment.
C'est pourquoi le texte de l'erreur et la liste des modules ne se contredisent pas. Le client ouvre un chemin, le chemin n'existe pas, et le noyau renvoie ENOENT avant même que le pilote tun soit consulté. Recharger le module ne peut pas réparer un chemin manquant, et créer le chemin ne peut pas réparer un pilote manquant.
lsmod | grep -w tun
stat -c "%F %t:%T" /dev/net/tun
grep " /dev " /proc/mounts
devtmpfs face à un mknod explicite
Deux mécanismes créent normalement ce nœud, et savoir lequel est censé en être responsable vous indique quoi réparer. devtmpfs fait créer par le noyau les nœuds de périphérique dans une instance montée, et udev applique la propriété et les permissions par-dessus. Un mknod explicite est la voie manuelle pour les images et les espaces de noms où aucun des deux ne s'exécute au démarrage.
Comparez les résultats plutôt que les intentions. Si devtmpfs est monté sur /dev et que le nœud est toujours absent, le pilote n'est pas celui que vous supposez, ou quelque chose a supprimé le nœud après le démarrage. Si /dev est un simple répertoire dans un rootfs décompressé, aucun mécanisme automatique n'existe et celui qui construit l'image doit créer le nœud.
stat -c "%F major=%t minor=%T" /dev/net/tun
# expect: character special file major=a minor=c8
Là où aucun des deux mécanismes ne s'exécute, le nœud doit être créé à la main, et l'ordre compte. Créez d'abord le répertoire parent, car mknod ne peut pas l'inventer, puis le périphérique caractère, puis un mode qui le laisse inscriptible par root seul. Rechargez le module avant de créer le nœud, et ne supprimez un chemin existant qu'après que ls a montré qu'il s'agit d'un répertoire et non d'un périphérique.
sudo modprobe tun
sudo mkdir -p /dev/net
sudo mknod /dev/net/tun c 10 200
sudo chmod 600 /dev/net/tun
ls -l /dev/net/tun
Le type rapporté par stat est le discriminant le plus rapide, et un répertoire à cet emplacement est la surprise la plus fréquente dans les images minimales. Un mkdir bien intentionné dans un Dockerfile laisse exactement cela, et aucun chargement de module ne remplace un répertoire par un périphérique caractère. Major 10 avec minor 200 est la paire enregistrée pour tun, exprimée en hexadécimal par a et c8.
Quatre traces, quatre causes
Quatre causes expliquent presque tous les cas, et chacune laisse une trace différente dans les journaux. Commencez par l'environnement de démarrage. Les images sans disque, les cartes embarquées et les étapes initramfs ne montent souvent ni devtmpfs ni un /devgéré par udev, si bien qu'aucun processus n'écrit jamais le nœud. Lisez ensuite les options de montage de ce qui héberge /dev, car nodev permet au chemin d'exister tout en refusant de l'ouvrir, et un montage bind pris depuis un hôte durci porte le drapeau sans se signaler. Un montage en lecture seule échoue plus tôt et plus fort : mknod y renvoie EROFS, tandis que EPERM signifie que le processus manque de la capacité et non que le système de fichiers se protège. Enfin, un runtime de conteneurs doté de son propre /dev tmpfs a besoin que le nœud soit injecté dans son propre espace de noms, et son cgroup de périphériques doit autoriser la paire major et minor.
Vérifiez la couche de politique avant de changer à nouveau les permissions. SELinux et AppArmor peuvent refuser une ouverture que tous les attributs de fichier autorisent, et ce refus apparaît dans le journal d'audit plutôt que dans la sortie du client. Un changement de permission qui n'apparaît jamais dans un refus est du temps passé sur la mauvaise couche.
sudo journalctl -k -n 20 --no-pager
sudo ausearch -m AVC -ts recent | tail -5
Prouvez-le avec une interface, pas avec une liste
Une liste réussie prouve qu'un chemin existe et rien de plus. Ouvrez un nœud tun sain en lecture et l'appel revient avec une erreur, car le périphérique n'a pas de sémantique de lecture et n'est pas un fichier ordinaire. Cette erreur est ici le signal de succès, et la reconnaître vous évite de la poursuivre comme une panne.
head -c 1 /dev/net/tun
head: error reading '/dev/net/tun': File descriptor in bad state
Le test décisif crée une interface, car il exerce le pilote, le nœud et vos privilèges en une seule étape. Si l'ajout réussit et que le lien apparaît, les trois fonctionnent. Quand il échoue, errno sépare les causes : ENOENT signifie qu'il n'y a aucun nœud utilisable, EPERM pointe vers la capacité, les drapeaux de montage ou la politique, et EBUSY signifie qu'un autre processus possède déjà l'interface.
sudo ip tuntap add dev tun7 mode tun
ip -brief link show tun7
sudo ip tuntap del dev tun7 mode tun
Exécutez ce test dans le contexte qui échoue réellement, car une unité de service, un conteneur ou une tâche planifiée peuvent porter un ensemble de capacités différent de celui de votre shell de connexion. Un test qui passe dans le mauvais contexte ne prouve rien sur celui qui échoue.
Quand le périphérique n'est pas le problème
Si le test d'interface réussit et que le client échoue encore, le problème restant est le profil, le contexte du service ou la route, et le périphérique est correct. C'est le moment d'arrêter de modifier les nœuds de périphérique, car d'autres changements ne font qu'ajouter de la distance par rapport à un système qui fonctionne.
Exécutez les cinq mêmes vérifications à chaque fois au lieu d'en inventer une nouvelle : le module est-il chargé sans module concurrent, le chemin est-il un périphérique caractère avec le bon major et le bon minor, les options de montage sont-elles propres, le contexte du processus porte-t-il la capacité, et existe-t-il un refus de politique à l'horodatage de la panne. Une panne qui survit aux cinq n'est pas un problème de périphérique, quoi que mentionne le message.