Une longue durée de fonctionnement prouve seulement que le démarrage a réussi avec les privilèges que le démon possédait à cet instant. Les défaillances qui comptent ici arrivent plus tard : un nouveau client se connecte, une liste de révocation est remplacée, un script auxiliaire doit écrire un fichier. Chacune est une ouverture nouvelle effectuée par le compte non privilégié, avec sa propre question de permissions et son propre message d’erreur. Trois décisions séparent une configuration qui continue de fonctionner d’une qui échoue au prochain événement.

Séparez ce qui a été chargé tôt de ce qui est ouvert plus tard

Un descripteur est une poignée sur une description de fichier ouverte, et le mode d’accès a été fixé lors de la création de cette description. Changer l’utilisateur du processus ensuite ne ferme pas la poignée, ce qui explique qu’un fichier de clé lu en root continue de fonctionner après la baisse. Le mode ne change pas non plus : une poignée ouverte en lecture ne devient pas inscriptible, et une poignée inscriptible n’est pas révoquée simplement parce que le compte a perdu ses droits sur le chemin. Ces deux faits expliquent l’essentiel du comportement surprenant de ces rapports.

L’autre moitié est la mémoire. Un démon peut analyser un certificat, conserver la matière analysée et fermer le fichier ; ce qui reste utilisé est une donnée, pas un fichier ouvert. Le trafic continu ne prouve donc ni qu’un descripteur est ouvert, ni que le nom de chemin est atteignable. Il prouve que la matière nécessaire à la session courante a été obtenue plus tôt.

Une ouverture ultérieure est une autre transaction. Elle s’exécute avec les identifiants courants, le contexte de sécurité courant et la vue de montage courante, si bien qu’un chemin lisible au démarrage peut être refusé à la connexion suivante. La rotation ajoute un piège : écrire un nouveau fichier sur le même nom de chemin laisse un descripteur existant attaché à l’ancien objet, si bien qu’un rechargement peut sembler réussir alors que le démon utilise encore le contenu précédent. Comparez l’inode détenu par le processus avec celui du disque avant de croire à une rotation.

Nommez les trois lectures d’exécution et le moment de chacune

Trois ressources sont normalement ouvertes bien après l’initialisation, et elles échouent à des moments différents. Un fichier de configuration propre à un client est lu lorsque ce client se connecte, il peut donc casser pour un utilisateur tandis que les autres continuent. Les données de révocation sont consultées pendant l’authentification du pair et de nouveau lorsque le fichier est remplacé. Un répertoire temporaire n’est touché que lorsqu’un script ou un greffon écrit quelque chose.

Aucune des trois n’appartient au chemin de démarrage, ce qui explique qu’un serveur sain pendant des semaines puisse échouer à la connexion suivante. La catégorie nomme aussi le propriétaire du correctif : les deux premières sont des droits de lecture sur la configuration sous le compte du démon, et la troisième est un emplacement inscriptible à l’usage étroit.

Path reopened laterWhat triggers the openFirst sign when it fails
Client configuration fileA connection for that client identityOne client rejected while others connect
Revocation dataPeer authentication, including after replacementRevoked and valid peers both refused
Script or plugin temporary directoryA helper needs to create a fileThe helper exits with a path error

persist-key répond à une question plus étroite qu’on ne le suppose. Il garde le fichier de clé ouvert pendant les réinitialisations documentées SIGUSR1 et ping-restart, et il ne fait rien pour les fichiers CCD, les données de révocation, les exécutables auxiliaires ou un redémarrage complet du processus. Ceux-ci suivent toujours leurs propres chemins d’ouverture, sous le compte qui tourne à ce moment-là.

Confirmez le compte et la vue qu’il a réellement

Demandez au démon en cours quel compte il utilise au lieu de faire confiance au fichier d’unité, puis comparez la réponse avec ce que l’unité définit. Un écart est la première chose à résoudre, car chaque vérification de permissions ultérieure s’exécute comme celui que le processus est réellement. Parcourez ensuite le chemin composant par composant ; la traversée est évaluée à chaque niveau, donc un fichier lisible dans un répertoire non traversable reste illisible. Le mode final du fichier est la dernière question, pas la première.

Les espaces de noms et les bacs à sable de service ajoutent des frontières que la propriété et les bits de mode ne décrivent pas. Un chemin visible dans votre shell peut être absent ou en lecture seule dans un chroot, un espace de noms de montage ou une unité qui masque les répertoires personnels. Les modules de sécurité ajoutent une couche de plus, si bien qu’un fichier qui semble lisible peut encore être refusé à l’ouverture.

ps -o user:12,pid,args -C openvpn
systemctl show -p User -p Group -p ProtectHome -p RootDirectory [email protected]
sudo -u openvpn namei -l /etc/openvpn/ccd/alice

Une lecture réussie dans le shell de l’administrateur ne teste aucune de ces conditions combinées. Exécutez la vérification avec le compte du démon, avec le même espace de noms et le même bac à sable, ou acceptez que le résultat décrive votre shell et non le serveur. La troisième commande est la version bon marché de ce test : elle affiche le mode de chaque composant et échoue au premier que le compte du démon ne peut pas traverser.

Accordez l’écriture d’exécution dans un répertoire dédié

La lecture n’est que la moitié de l’inventaire. Un script auxiliaire peut avoir besoin de créer un fichier d’état, un greffon peut avoir besoin d’écrire un état à côté de sa configuration, et ces écritures ne sont pas couvertes par le fait de rendre l’arbre de configuration inscriptible. Donnez au répertoire concerné le droit d’écriture que sa fonction exige, avec un propriétaire et un mode que vous pouvez expliquer. Préférez l’accès en lecture seule pour tout ce qui n’est que lu.

Gardez les permissions de la clé privée séparées des fichiers de politique. Une clé qui doit être relue après une réinitialisation a une exigence différente d’un fichier client lu à chaque connexion, et un seul mode permissif pour les deux accorde plus d’accès que le problème ne le demande. Un compte de démon dédié à propriété étroite est plus facile à auditer qu’un groupe large. Lorsque la clé doit survivre aux réinitialisations, persist-key supprime la relecture au lieu d’élargir le répertoire.

Décidez à partir de l’événement que vous pouvez reproduire

Un tunnel actif prouve un seul chemin dans le code. L’événement qui a besoin du fichier est le seul test qui couvre l’ouverture qui vous intéresse, qu’il s’agisse d’une nouvelle connexion client, d’un fichier de révocation remplacé, d’une réinitialisation douce autorisée ou d’un redémarrage planifié. Choisissez l’événement, exécutez-le et gardez la première erreur plutôt que la dernière.

Pour une réinitialisation douce, un signal et le journal de l’unité montrent si la clé a été relue. Envoyez le signal, puis lisez les dernières lignes écrites par l’unité :

systemctl kill -s SIGUSR1 [email protected]
journalctl -u [email protected] -n 40 --no-pager

Ne concluez pas qu’un démon compromis ne peut plus modifier de fichiers simplement parce que les privilèges ont été abaissés. Ce qu’il peut encore dépend de ses identifiants restants, de ses capacités, des répertoires où il peut écrire et de tout descripteur inscriptible qu’il détient encore. La réduction des privilèges resserre l’autorité, et l’inventaire des ouvertures d’exécution montre jusqu’où. Écrivez cette frontière à côté de la configuration pour que la personne suivante n’ait pas à la redécouvrir.

Articles similaires