NET_ADMIN est la capacité Linux qui permet à un processus d’effectuer de l’administration réseau, et dans un conteneur elle fait exactement cela dans l’espace de noms réseau où le processus s’exécute. Ce n’est pas une licence pour ouvrir une socket, et ce n’est pas un substitut à un périphérique ou à une route. La question intéressante n’est jamais de savoir si la capacité est accordée, mais quel espace de noms reçoit le changement.

Ce que couvre la capacité

Dans son espace de noms, un processus détenant NET_ADMIN peut monter et descendre des interfaces, attribuer des adresses, ajouter et supprimer des routes et écrire des règles de pare-feu, ce qui est le groupe d’opérations que capabilities(7) rassemble sous cette capacité. Il peut aussi ajuster des réglages au niveau de l’interface qui contrôlent la façon dont le noyau met en file et filtre le trafic. Un client VPN a généralement besoin de quelques-uns d’entre eux au démarrage et de rien d’autre ensuite. C’est pourquoi la plupart des images demandent plus de portée qu’elles n’en utilisent.

Les opérations hors de cet espace de noms ne sont pas couvertes, et les privilèges voisins sont distincts. La lecture et l’écriture de paquets bruts relèvent de NET_RAW, et la création d’un périphérique tun exige à la fois la capacité et l’accès au nœud du périphérique. Un processus privé du périphérique obtient une erreur de permission qui ressemble à un problème réseau et n’en est pas un.

L’espace de noms décide du rayon d’action

Un conteneur sur son propre réseau pont atteint son propre loopback, ses propres interfaces et sa propre table de routage. Y accorder NET_ADMIN permet au client VPN de construire un tunnel sans toucher aux décisions de routage de l’hôte, ce qui est la disposition que supposent la plupart des images de conteneur.

Basculez ce conteneur sur le réseau de l’hôte et la même autorisation modifie désormais les routes et le pare-feu de l’hôte, car il ne reste plus d’espace de noms séparé. Le client censé gérer un seul tunnel peut alors rediriger le trafic de toute la machine, et une erreur dans son script de démontage laisse à l’hôte des règles que personne n’a écrites. Rien dans la capacité elle-même n’a changé ; le contexte, si. C’est pourquoi le même fichier compose se comporte différemment sur deux hôtes.

Trois prérequis fusionnés en un

Les échecs de démarrage des conteneurs VPN impliquent généralement l’une de trois choses distinctes : la capacité, le périphérique de tunnel et la prise en charge du protocole par le noyau. Accorder les trois à la fois fonctionne, et cela rend aussi l’échec suivant impossible à attribuer. Un fragment compose minimal accorde la capacité et transmet explicitement le nœud du périphérique :

services:
  vpn:
    image: example/vpn-client:latest
    cap_add:
      - NET_ADMIN
    devices:
      - /dev/net/tun
    sysctls:
      - net.ipv4.conf.all.src_valid_mark=1

Lisez la documentation de l’image elle-même pour savoir de quels sysctls elle a besoin, car un sysctl dans un conteneur peut exiger un privilège supplémentaire ou un réglage au niveau de l’hôte. Là où l’image ne prend en charge que le mode espace utilisateur, aucun périphérique n’est nécessaire et la liste des capacités se réduit d’autant.

Accordez l’opération étroite, pas le mode

Le mode privilégié remet toutes les capacités et élargit l’accès aux périphériques en même temps, il masque donc lequel des trois prérequis manquait. Il rend aussi les modes de défaillance du conteneur indiscernables des changements de l’hôte. Préférez une liste explicite même lorsqu’elle est plus longue à écrire.

Les déploiements rootless et en espaces de noms utilisateur changent ce que signifie une capacité en pratique, car le processus ne peut la détenir qu’à l’intérieur d’un espace de noms mappé. Copier une liste de capacités d’un exemple rootful vers un environnement d’exécution rootless et attendre la même portée est une façon courante de perdre un après-midi.

Les secrets relèvent d’une décision distincte. Détenir NET_ADMIN ne dit rien sur l’identité de pair que le client présente, sur les identifiants qu’il charge ni sur leur rotation. L’administration réseau n’apporte pas la confiance.

Vérifiez le changement et consignez pourquoi l’autorisation existe

Confirmez ce que le conteneur a réellement reçu avant de déboguer le tunnel. L’inspection ci-dessous indique les capacités ajoutées et le mode réseau, puis montre à quoi ressemble la liste des interfaces depuis l’intérieur :

docker inspect --format '{{.HostConfig.CapAdd}} {{.HostConfig.NetworkMode}} {{.HostConfig.Privileged}}' vpn
docker exec vpn sh -c 'grep CapEff /proc/self/status'
docker exec vpn ip -br addr
docker exec vpn ip route

Si la capacité est présente et que la liste des interfaces est toujours vide, le client a échoué avant de pouvoir créer quoi que ce soit, et les journaux du conteneur sont le prochain endroit à consulter. Si le tunnel existe mais que le trafic ne circule pas, la table de routage à l’intérieur du conteneur répond à la question suivante.

Testez aussi le chemin de démontage, car un client qui écrit des règles de pare-feu peut les laisser derrière lui. Arrêtez le conteneur et vérifiez à nouveau le jeu de règles de l’hôte si le réseau de l’hôte est utilisé. Une autorisation nécessaire seulement deux secondes au démarrage ne devrait pas être décrite dans le fichier compose comme une exigence permanente sans une note qui l’explique.

Écrivez la raison à côté de chaque capacité : quelle opération en avait besoin et ce qui échoue sans elle. Cette note survit aux mises à jour d’image, et c’est la différence entre une liste de privilèges revue et une qui a grossi par copier-coller.

Articles similaires