Quelles capacités Linux votre processus VPN nécessite-t-il ?
La configuration de l’interface, les routes, l’accès aux périphériques et les sockets ordinaires sont des privilèges distincts. Nommez l’opération qui a échoué avant d’accorder CAP_NET_ADMIN.
Un démon VPN a besoin d’opérations réseau privilégiées, alors même que les applications ordinaires ouvrent des sockets sortantes sans aucun privilège. C’est dans cet écart que commencent la plupart des erreurs de capacités : quelqu’un voit une erreur de permission, accorde tout l’ensemble, et le service fonctionne pour des raisons que personne ne saura expliquer ensuite. Le parcours ci-dessous part de l’opération qui a échoué, la rattache à une seule autorité, puis vérifie le contexte dans lequel le processus s’exécute réellement. Tourner en root sans restriction est une réponse valable pour certains déploiements, mais cela devrait être une décision et non un réglage par défaut.
Partez de l’opération qui a échoué
Lisez l’erreur avant de lire un tutoriel. « Opération non permise » lors d’un changement d’interface, un refus d’ouvrir un nœud de périphérique et un échec de bind sur un port bas sont trois problèmes différents avec trois autorisations différentes. Si le processus démarre et n’échoue qu’au premier changement de route, cela indique aussi que la création de l’interface a déjà réussi, ce qui réduit considérablement la réponse.
Reproduisez l’échec avec le service arrêté, puis lancez-le au premier plan sous le même utilisateur si l’implémentation le permet. Une erreur que vous voyez vaut mieux qu’une ligne de journal que vous devez deviner, et elle rend évidente la différence entre un problème de capacités et un refus de SELinux ou d’AppArmor.
Rattachez chaque opération à une autorité
Le tableau est une carte de départ, pas un substitut à la documentation de l’implémentation :
| Operation | Usual authority | Watch for |
|---|---|---|
| Create or configure tun/tap | CAP_NET_ADMIN in the relevant namespace | Creating the device and opening it are separate steps |
| Add routes, rules, firewall entries | CAP_NET_ADMIN | Same capability, different namespace than you think |
| Open /dev/net/tun | Device permissions, not a capability | Group membership or a udev rule may be enough |
| Raw sockets | CAP_NET_RAW | Needed by some user-space tunnel implementations |
| Bind a port below 1024 | CAP_NET_BIND_SERVICE | Irrelevant for most modern tunnel ports |
Les deux lignes de ce tableau sont celles que les gens confondent. Ouvrir /dev/net/tun est une question de permissions de fichier ; demander au noyau de configurer ce que vous avez ouvert est une question de capacités. Accorder CAP_NET_ADMIN ne corrige pas un nœud de périphérique que votre utilisateur de service ne peut pas lire, et corriger le nœud ne vous donne pas l’autorité sur les routes. Vérifiez les deux avant de modifier l’un ou l’autre.
Le WireGuard du noyau et les programmes de tunnel en espace utilisateur n’ont pas non plus les mêmes exigences. La voie du module noyau configure une interface existante via netlink, tandis qu’une implémentation en espace utilisateur peut aussi avoir besoin du périphérique et, pour certains transports, de sockets brutes.
Lisez le contexte : service, conteneur ou shell
Une capacité dans l’espace de noms réseau de l’hôte peut avoir bien plus d’effet que la même opération dans une pile délibérément isolée. Un environnement d’exécution de conteneurs démarre généralement avec un ensemble de limites réduit ; ajouter NET_ADMIN à un conteneur qui partage le réseau de l’hôte est une autorisation bien plus large que de l’ajouter à un conteneur doté de son propre espace de noms réseau et sans montages de l’hôte.
Pour les unités systemd, lisez ce qui est réellement appliqué et non ce que le fichier d’unité semble dire : systemctl show -p CapabilityBoundingSet -p AmbientCapabilities rapporte les valeurs effectives, qui peuvent être fixées par un drop-in que vous avez oublié. Demandez la valeur effective, jamais celle qui était prévue. Les capacités ambient comptent parce qu’un service non root perd des capacités au passage de exec sans elles.
Prenez la configuration prise en charge du paquet ou de l’image comme référence. N’ajoutez pas de permissions de sockets brutes ni de mode privilégié parce qu’un autre tutoriel les a listées pour un autre outil ; ce raisonnement ne transfère rien et vous coûte l’isolation que vous avez payée. En cas de doute, n’accordez rien et lisez les notes de l’implémentation pour l’opération exacte dont vous avez besoin.
Observez ce qui se passe après la perte des privilèges
Beaucoup de démons démarrent avec des privilèges, configurent l’interface, puis passent à un utilisateur non privilégié et conservent le tunnel. À partir de là, le processus ne peut plus ajouter de route, réécrire une règle de pare-feu ni ouvrir un nouveau périphérique privilégié, ce qui va bien jusqu’à ce qu’une reconnexion ait besoin exactement de cela. Les lectures de fichiers et l’exécution de hooks ultérieures peuvent échouer pour la même raison.
Testez tout le cycle de vie plutôt que la première seconde : le démarrage, un renouvellement ou une reconnexion, et le nettoyage à l’arrêt. Une première création d’interface réussie n’établit pas que toute opération privilégiée ultérieure fonctionnera encore, et l’échec apparaît généralement des heures plus tard en production.
Les implémentations qui gardent le tunnel ouvert lors d’un changement d’utilisateur l’associent généralement à des options pour conserver le tunnel et les clés. Côté systemd, la question équivalente est de savoir si l’ensemble de capacités de l’unité survit au changement d’utilisateur que vous avez configuré.
Consignez l’autorisation et sa raison
Notez l’autorisation exacte, l’opération qu’elle permet et l’erreur qu’elle a corrigée. Une ligne dans les notes de déploiement — « CAP_NET_ADMIN dans l’espace de noms réseau de l’unité, nécessaire aux changements de route après une reconnexion » — suffit à empêcher la personne suivante d’élargir à nouveau l’autorisation.
Relisez ensuite l’ensemble effectif après chaque mise à niveau. Les éditeurs ajoutent des fonctions, les images changent les valeurs par défaut, et une capacité inutile l’an dernier peut devenir porteuse sans que personne ne le remarque.