J'écris un démon de serveur HTTP en C (il y a des raisons), je le gère avec le fichier d'unité systemd.
Je réécris une application conçue il y a 20 ans, vers 1995. Et le système qu'ils utilisent est qu'ils chrootent puis setuid, et la procédure standard.
Maintenant, dans mon travail précédent, la politique habituelle était de ne jamais exécuter de processus en tant que root. Vous créez un utilisateur / groupe pour lui et exécutez à partir de là. Bien sûr, le système exécutait certaines choses en tant que root, mais nous pouvions réaliser tous les traitements de logique métier sans être root.
Maintenant, pour le démon HTTP, je peux l'exécuter sans root si je ne chroote pas dans l'application. N'est-il donc pas plus sûr que l'application ne s'exécute jamais en tant que root?
N'est-il pas plus sûr de l'exécuter en tant qu'utilisateur mydaemon depuis le début? Au lieu de le démarrer avec root, chrooter, puis setuid à mydaemon-user?
capabilities(7)
.Réponses:
Il semble que d'autres aient manqué votre argument, ce qui n'était pas une raison pour utiliser des racines modifiées, que vous connaissez bien sûr déjà clairement, ni ce que vous pouvez faire d'autre pour limiter les dæmons, alors que vous savez également clairement comment courir sous l'égide de comptes d'utilisateurs non privilégiés; mais pourquoi faire ce genre de choses dans l'application . Il y a en fait un exemple assez juste de pourquoi.
Considérez la conception du
httpd
programme dæmon dans le dossier public de Daniel J. Bernstein. La première chose qu'il fait est de remplacer root par le répertoire racine qu'il a été invité à utiliser avec un argument de commande, puis de supprimer les privilèges de l'ID utilisateur et de l'ID de groupe non privilégiés transmis dans deux variables d'environnement.Les jeux d'outils de gestion Dæmon ont des outils dédiés pour des choses telles que la modification du répertoire racine et le passage aux ID utilisateur et groupe non privilégiés. Le runit de Gerrit Pape l'a fait
chpst
. Mon ensemble d'outils Nash achroot
etsetuidgid-fromenv
. Le s6 de Laurent Bercot as6-chroot
ets6-setuidgid
. Perp de Wayne Marshall aruntool
etrunuid
. Et ainsi de suite. En effet, ils ont tous la propre boîte à outils daemontools de M. Bernstein avecsetuidgid
comme antécédent.On pourrait penser que l'on pourrait extraire la fonctionnalité
httpd
et utiliser de tels outils dédiés. Ensuite, comme vous l'imaginez, aucune partie du programme serveur ne s'exécute avec les privilèges de superutilisateur.Le problème est que l'une des conséquences directes doit faire beaucoup plus de travail pour configurer la racine modifiée, ce qui expose de nouveaux problèmes.
Avec Bernstein
httpd
tel qu'il est, les seuls fichiers et répertoires qui se trouvent dans l'arborescence des répertoires racine sont ceux qui doivent être publiés dans le monde. Il n'y a rien d'autre dans l'arbre. De plus, il n'y a aucune raison pour qu'un fichier image de programme exécutable existe dans cette arborescence.Mais déplacer le changement de répertoire racine sur un programme chargement en chaîne (ou systemd), et tout à coup le fichier image du programme
httpd
, toutes les bibliothèques partagées qu'il charge, et tous les fichiers spéciaux/etc
,/run
et/dev
que le chargeur de programme ou runtime C accès à la bibliothèque lors de l'initialisation du programme (ce qui peut vous surprendre si voustruss
/strace
un programme C ou C ++), devez également être présent dans la racine modifiée. Sinon,httpd
ne peut pas être enchaîné à et ne se chargera / ne fonctionnera pas.N'oubliez pas qu'il s'agit d'un serveur de contenu HTTP (S). Il peut potentiellement servir n'importe quel fichier (lisible par le monde) dans la racine modifiée. Cela inclut maintenant des éléments tels que vos bibliothèques partagées, votre chargeur de programme et des copies de divers fichiers de configuration du chargeur / CRTL pour votre système d'exploitation. Et si, par certains moyens (accidentels), le serveur de contenu a accès à des éléments d' écriture , un serveur compromis peut éventuellement obtenir un accès en écriture à l'image du programme pour
httpd
lui-même, ou même au chargeur de programme de votre système. (Rappelez - vous que vous avez maintenant deux séries parallèles/usr
,/lib
,/etc
,/run
et/dev
répertoires pour garder en sécurité.)Rien de tout cela n'est le cas où la
httpd
racine change et supprime elle-même les privilèges.Vous avez donc échangé avec une petite quantité de code privilégié, qui est assez facile à auditer et qui s'exécute dès le début du
httpd
programme, avec des privilèges de superutilisateur; pour avoir une surface d'attaque considérablement étendue de fichiers et de répertoires dans la racine modifiée.C'est pourquoi ce n'est pas aussi simple que de tout faire à l'extérieur du programme de service.
Notez qu'il s'agit néanmoins d'un strict minimum de fonctionnalités en
httpd
soi. Tout le code qui fait des choses comme regarder dans la base de données des comptes du système d'exploitation pour l'ID utilisateur et l'ID de groupe à mettre dans ces variables d'environnement en premier lieu est externe auhttpd
programme, dans de simples commandes auditables autonomes telles queenvuidgid
. (Et bien sûr , il est un outil de ucspi, il ne contient aucune du code à écouter sur le port TCP concerné (s) ou d'accepter les connexions, ceux -ci étant le domaine des commandes telles quetcpserver
,tcp-socket-listen
,tcp-socket-accept
,s6-tcpserver4-socketbinder
,s6-tcpserver4d
, et ainsi de suite.)Lectures complémentaires
httpd
. fichier public . cr.yp.to.httpd
. Les logiciels de Daniel J. Bernstein tout en un . Logiciels. Jonathan de Boyne Pollard. 2016.gopherd
. Les logiciels de Daniel J. Bernstein tout en un . Logiciels. Jonathan de Boyne Pollard. 2017.la source
Je pense que de nombreux détails de votre question pourraient s'appliquer également
avahi-daemon
, que j'ai examinés récemment. (J'ai peut-être manqué un autre détail qui diffère cependant). L'exécution d'avahi-daemon dans un chroot présente de nombreux avantages, au cas où avahi-daemon serait compromis. Ceux-ci inclus:Le point 3 pourrait être particulièrement agréable lorsque vous n'utilisez pas dbus ou similaire ... Je pense qu'avahi-daemon utilise dbus, donc il s'assure de garder l'accès au système dbus même depuis l'intérieur du chroot. Si vous n'avez pas besoin d'envoyer des messages sur le système dbus, le fait de refuser cette capacité pourrait être une fonctionnalité de sécurité assez intéressante.
Notez que si avahi-daemon a été réécrit, il pourrait potentiellement choisir de s'appuyer sur systemd pour la sécurité, et utiliser par exemple
ProtectHome
. J'ai proposé un changement à avahi-daemon pour ajouter ces protections en tant que couche supplémentaire, ainsi que des protections supplémentaires qui ne sont pas garanties par chroot. Vous pouvez voir la liste complète des options que j'ai proposées ici:https://github.com/lathiat/avahi/pull/181/commits/67a7b10049c58d6afeebdc64ffd2023c5a93d49a
Il semble qu'il y ait plus de restrictions que j'aurais pu utiliser si avahi-daemon n'a pas utilisé chroot lui-même, dont certaines sont mentionnées dans le message de validation. Je ne sais pas combien cela s'applique cependant.
Remarque, les protections que j'ai utilisées n'auraient pas empêché le démon d'ouvrir des fichiers socket Unix (point 3 ci-dessus).
Une autre approche serait d'utiliser SELinux. Cependant, vous seriez en quelque sorte en train de lier votre application à ce sous-ensemble de distributions Linux. La raison pour laquelle j'ai pensé positivement à SELinux ici, c'est que SELinux restreint l'accès aux processus sur dbus, de manière fine. Par exemple, je pense que vous pourriez souvent vous attendre à ce que
systemd
ce ne soit pas dans la liste des noms de bus dont vous avez besoin pour pouvoir envoyer des messages à :-)."Je me demandais si l'utilisation du sandboxing systemd était plus sûre que chroot / setuid / umask / ..."
Résumé: pourquoi pas les deux? Décodons un peu ce qui précède :-).
Si vous pensez au point 3, l'utilisation de chroot offre plus de confinement. ProtectHome = et ses amis n'essaient même pas d'être aussi restrictifs que chroot. (Par exemple, aucune des listes noires d'options systemd nommées
/run
, où nous avons tendance à placer des fichiers socket Unix).chroot montre que restreindre l'accès au système de fichiers peut être très puissant, mais tout n'est pas sous Linux un fichier :-). Il existe des options systemd qui peuvent restreindre d'autres choses, qui ne sont pas des fichiers. Ceci est utile si le programme est compromis, vous pouvez réduire les fonctionnalités du noyau qui lui sont disponibles, dans lesquelles il pourrait essayer d'exploiter une vulnérabilité. Par exemple, avahi-daemon n'a pas besoin de prises bluetooth et je suppose que votre serveur web non plus :-). Ne lui donnez donc pas accès à la famille d'adresses AF_BLUETOOTH. Ajoutez simplement la liste blanche AF_INET, AF_INET6, et peut-être AF_UNIX, en utilisant l'
RestrictAddressFamilies=
option.Veuillez lire la documentation de chaque option que vous utilisez. Certaines options sont plus efficaces en combinaison avec d'autres, et certaines ne sont pas disponibles sur toutes les architectures CPU. (Pas parce que le CPU est mauvais, mais parce que le port Linux pour ce CPU n'était pas aussi bien conçu. Je pense).
(Il y a un principe général ici. C'est plus sûr si vous pouvez écrire des listes de ce que vous voulez autoriser, pas de ce que vous voulez refuser. Comme définir un chroot vous donne une liste de fichiers auxquels vous êtes autorisé à accéder, et ce plus robuste que de dire que vous voulez bloquer
/home
).En principe, vous pouvez appliquer vous-même les mêmes restrictions avant setuid (). C'est tout simplement du code que vous pouvez copier depuis systemd. Cependant, les options d'unité systemd devraient être beaucoup plus faciles à écrire, et comme elles sont dans un format standard, elles devraient être plus faciles à lire et à examiner.
Je peux donc fortement recommander de lire la section sandboxing de
man systemd.exec
votre plate-forme cible. Mais si vous voulez la conception la plus sécurisée possible, je n'aurais pas peur d'essayerchroot
(puis de supprimer lesroot
privilèges) dans votre programme également . Il y a un compromis ici. L'utilisationchroot
impose certaines contraintes à votre conception globale. Si vous avez déjà un design qui utilise chroot et qu'il semble faire ce dont vous avez besoin, cela sonne plutôt bien.la source
Si vous pouvez compter sur systemd, il est en effet plus sûr (et plus simple!) De laisser le sandboxing à systemd. (Bien sûr, l'application peut également détecter si elle a été lancée en sandbox par systemd ou non, et sandbox elle-même si elle est toujours root.) L'équivalent du service que vous décrivez serait:
Mais nous ne devons pas nous arrêter là. systemd peut également faire beaucoup d'autres sandboxing pour vous - voici quelques exemples:
Voir
man 5 systemd.exec
pour beaucoup plus de directives et des descriptions plus détaillées. Si vous rendez votre démon activable par socket (man 5 systemd.socket
), vous pouvez même utiliser les options liées au réseau: le seul lien du service vers le monde extérieur sera le socket réseau qu'il a reçu de systemd, il ne pourra pas se connecter à autre chose. S'il s'agit d'un simple serveur qui n'écoute que sur certains ports et n'a pas besoin de se connecter à d'autres serveurs, cela peut être utile. (Les options liées au système de fichiers peuvent également rendreRootDirectory
obsolètes, à mon avis, donc vous n'avez peut-être plus besoin de vous soucier de configurer un nouveau répertoire racine avec tous les binaires et bibliothèques requis.)Les versions plus récentes de systemd (depuis la v232) prennent également en charge
DynamicUser=yes
, où systemd allouera automatiquement l'utilisateur du service pour vous uniquement pour l'exécution du service. Cela signifie que vous ne devez pas vous enregistrer un utilisateur permanent pour le service, et fonctionne très bien tant que le service ne pas écrire à tous les emplacements du système de fichiers autres que sonStateDirectory
,LogsDirectory
etCacheDirectory
(que vous pouvez également déclarer dans le fichier de l' unité - voirman 5 systemd.exec
, encore une fois - et quel systemd va ensuite gérer, en prenant soin de les affecter correctement à l'utilisateur dynamique).la source