chroot échoue sur OpenVPN : règle AppArmor ou capacité ?
Ajouter un chemin à un profil AppArmor n’accorde jamais CAP_SYS_CHROOT, et un chroot refusé peut venir de trois couches distinctes. Déterminez laquelle changer.
Le démon journalise un échec de chroot et le profil paraît déjà généreux, aussi le correctif tentant consiste à élargir une règle de chemin et à redémarrer. Cela ne change généralement rien, car chroot est gardé par un privilège que le profil n’a jamais pu transmettre. Décider quelle couche toucher demande trois vérifications, et elles sont peu coûteuses : ce que le processus a le droit de détenir, ce que la politique autorise, et ce que le lanceur a retiré avant que l’une ou l’autre n’ait d’importance.
Deux portes distinctes, et une seule appartient à AppArmor
chroot exige CAP_SYS_CHROOT dans l’espace de noms utilisateur où le processus s’exécute. C’est un privilège Linux classique, et le noyau le vérifie avant que l’appel ne fasse quoi que ce soit avec des chemins. Séparément, un profil AppArmor en mode application peut refuser la capacité même lorsque le processus la détient, car la politique obligatoire est consultée en plus des identifiants, et non à leur place. La distinction compte en pratique : les deux couches vivent dans des fichiers différents et sont généralement maintenues par des personnes différentes.
Ces deux portes échouent avec le même errno. Les deux produisent EPERM, et les deux apparaissent comme une plainte de permission générique dans le journal du démon, ce qui explique pourquoi deviner le contenu du profil fait perdre tant de temps. La règle de chemin que vous alliez ajouter contrôle l’accès aux fichiers une fois chroot réussi ; elle n’a aucune incidence sur le fait que l’opération soit autorisée ou non.
Une troisième porte se situe en amont des deux. Si le gestionnaire de services ou un wrapper a lancé le processus sans la capacité dans son ensemble de limites, aucune modification d’AppArmor ne peut la rétablir, et ajouter des capacités au profil ne fait que documenter une intention que le noyau n’honorera pas.
Lisez l’état des privilèges avant de modifier la politique
Partez du processus en cours d’exécution, et non de la configuration que vous croyez active. Le fichier d’état expose cinq masques de capacités et l’indicateur no-new-privileges, et ils répondent à des questions différentes :
grep -E '^(CapInh|CapPrm|CapEff|CapBnd|CapAmb|NoNewPrivs):' /proc/$(pidof openvpn)/status
sudo aa-status | grep -i openvpnCapEff est l’ensemble qui compte pour l’appel : une capacité présente dans CapBnd mais absente de CapEff est autorisée par le plafond et n’est pas détenue à cet instant. CapAmb compte lorsque le binaire n’est pas privilégié au sens habituel de setuid, car les capacités ambiantes sont la façon dont un service conserve un privilège à travers exec sans capacités de fichier. CapInh contient des bits héritables à travers exec, mais seulement si l’ensemble héritable du fichier exécuté le permet, donc un bit à cet endroit n’accorde rien à lui seul.
Confirmez ensuite quel profil est réellement attaché. Un processus peut être confiné par un nom de profil que vous n’attendiez pas, surtout lorsqu’une distribution livre son unité et son profil ensemble, et la lecture de /proc/PID/attr/current tranche en une ligne. Comparer votre fichier modifié au profil que le noyau rapporte, c’est la différence entre corriger la cause et modifier un fichier que personne ne charge.
N’ajoutez la règle de capacité que si la trace d’audit le dit
Les enregistrements du noyau et de l’audit nomment la couche qui refuse. Cherchez un refus portant l’opération capable et le nom sys_chroot, avec le profil qui l’a produit. Si cet enregistrement existe, le processus détenait déjà la capacité et AppArmor l’a refusée, et la modification ciblée est une règle de capacité à l’intérieur du vrai profil :
capability sys_chroot,Gardez-la comme fragment dans le profil réellement attaché, et vérifiez le mot-clé dans la référence des profils apparmor.d plutôt que de copier une règle depuis un tutoriel sans rapport. La placer dans un include partagé l’accorde à chaque programme confiné qui tire ce fichier, et basculer les profils en mode plainte masque le refus suivant au lieu de résoudre celui-ci. La règle de profil retire un refus de politique ; elle ne fabrique pas de privilège, donc un processus qui n’a jamais eu CAP_SYS_CHROOT dans son ensemble effectif échouera encore avec la règle en place.
Si l’enregistrement d’audit ne montre aucun refus AppArmor, cessez de modifier le profil. Vous êtes devant un problème d’identifiants, et le travail restant se trouve dans l’unité ou dans le processus parent, pas dans le langage de politique.
Le gestionnaire de services garde son propre plafond
systemd fixe un ensemble de limites de capacités pour chaque service, et par défaut cet ensemble est étroit. Tout ce qui y est exclu est indisponible pour le processus quoi que dise le profil, ce qui explique pourquoi des unités durcies peuvent échouer sur une opération qui fonctionnait lorsque le démon était lancé à la main. Les directives concernées sont faciles à lire dans l’unité et faciles à négliger lorsque l’application signale l’échec. Les drop-in fournis par un paquet de distribution sont la source habituelle d’un plafond que personne ne se souvient avoir configuré.
Relever le plafond et accorder la capacité sont deux changements, pas un. Une entrée dans l’ensemble de limites dit que le processus peut détenir la capacité ; elle ne l’ajoute pas à l’ensemble effectif, et NoNewPrivs peut empêcher une élévation ultérieure de réussir même quand tout le reste concorde. N’ajoutez que ce dont l’opération a besoin, gardez le reste du durcissement intact et résistez au raccourci d’accorder toutes les capacités pour faire passer un appel.
L’ordre compte aussi ici. Modifiez d’abord le plafond de l’unité, vérifiez que CapEff contient désormais le bit, et seulement ensuite décidez si AppArmor refuse encore. Faire les deux changements à la fois vous laisse de nouveau deviner si l’échec persiste.
Une fois le refus levé, vérifiez la prison elle-même
Un chroot réussi déplace la racine, donc chaque chemin que le démon touche ensuite est résolu à l’intérieur du nouvel arbre. Une configuration manquante, un nœud de périphérique absent ou un répertoire de certificats non copié apparaîtront maintenant comme une erreur de fichier, et cette erreur est un problème d’agencement, non de capacité. Lisez l’événement suivant au lieu d’ajouter une permission large de plus. Le déplacement change aussi ce que signifie un chemin relatif, ce qui explique pourquoi les guides de durcissement insistent sur des chemins absolus dans la configuration du démon.
Il aide aussi de se rappeler ce que chroot ne fait pas. C’est une frontière de chemins, pas un bac à sable : un processus qui conserve le privilège peut s’en échapper, ce qui explique pourquoi la capacité est normalement abandonnée après l’appel et pourquoi le confinement doit rester autour du démon. Conserver ensemble le profil AppArmor, l’ensemble de limites de capacités et les restrictions du système de fichiers est le but de l’exercice, pas un reste à supprimer dès que le service démarre.
La règle de décision est courte. Si l’enregistrement d’audit nomme AppArmor, ajoutez une règle de capacité au profil attaché. Si l’ensemble effectif est vide, corrigez le plafond de l’unité et l’ordre d’abandon du privilège. Si le profil n’est pas celui que vous avez modifié, cessez de modifier et trouvez le vrai.