...

Capacités Linux : répartir les droits root de manière sécurisée et granulaire

Grâce aux capacités Linux, je divise les droits root en petits privilèges clairement définis, ce qui réduit considérablement le risque. Je contrôle ainsi de manière ciblée les processus autorisés à effectuer des actions spécifiques et je limite la surface d'attaque de chaque application.

Points centraux

  • À granulométrie fine Au lieu d'être tout-puissant : diviser les droits root en privilèges plus restreints.
  • Capacités des fichiers Au lieu d'utiliser Set-UID : associer directement les droits requis aux fichiers binaires.
  • Ensembles de capacités Configurer de manière ciblée les paramètres « steuern » : Permitted, Effective, Inheritable, Bounding.
  • Séparation des privilèges: Séparer strictement les services, les outils et les tâches.
  • Défense en profondeur: Compléter les capacités avec sudo, les rôles et les journaux.

Pourquoi désactiver les droits root ?

Un compte root permet Accès complet au niveau des fichiers et des processus, mais c'est précisément ce qui peut entraîner des erreurs aux conséquences graves. Une commande erronée ou un exploit suffit à faire planter toute une installation. Je limite donc les actions de grande envergure au strict nécessaire, ce qui permet de réduire l'ampleur des dégâts et le temps de restauration. Le principe des droits minimaux permet de maintenir les services à une taille réduite et contrôlable. Je désactive la connexion directe en tant qu'utilisateur root, je mise sur les rôles et je génère des journaux complets.

Les capacités Linux en bref

Les capacités Linux décomposent les pouvoirs classiques de l'utilisateur root en éléments clairement définis Privilèges. Chaque processus ne reçoit que les modules dont il a réellement besoin pour accomplir sa tâche, par exemple la liaison à des ports inférieurs à 1024 ou l'envoi de signaux spécifiques. Cela me permet de contourner l'ancienne approche « tout ou rien ». Le noyau gère ces modules pour chaque processus et les applique de manière stricte. Le contrôle reste ainsi précis et traçable.

Sur le plan technique, j'associe mes compétences soit à Processus (via leurs ensembles de capacités) ou à Fichiers (sous forme d'attributs étendus security.capability aux binaires ELF). Dans le cas du execve()-Lors du démarrage, le noyau fusionne les capacités des fichiers avec les ensembles de processus : pour simplifier, les capacités autorisées issues des attributs du fichier, associées aux droits héritables du processus appelant, sont combinées pour former le nouvel ensemble « Permitted » et, si cela est indiqué, activées simultanément dans l'ensemble « Effective ». Cela évite les détours par le Set-UID et permet de garder les privilèges visibles et vérifiables.

Comprendre les ensembles de capacités dans le contexte des processus

Chaque processus dispose de plusieurs ensembles de droits, que je peux attribuer de manière ciblée contrôle. Le « Permitted-Set » définit ce qu'un processus est en principe autorisé à posséder. Le « Effective-Set » détermine ce qui est actuellement actif. Le « Inheritable-Set » régit les privilèges pouvant être transmis aux processus enfants. Le « Bounding-Set » fixe une limite maximale stricte et empêche les processus de la dépasser.

Ambient Capabilities et Securebits

Outre les coffrets bien connus, il y a le Ensemble « Ambient », qui, lors du execve() n'expire pas automatiquement. Je l'utilise lorsqu'un processus non privilégié a besoin de droits minimaux sur plusieurs exec- doit conserver ces sauts (par exemple lors de l'appel de programmes utilitaires externes). Les droits « ambient » ne sont pris en compte dans les droits effectifs que si le fichier appelé ne définit pas lui-même de capacités de fichier – cela me permet d'éviter toute escalade indésirable.

Avec les Securebits je gère les détails des transitions, par exemple si un processus peut conserver les capacités qu'il avait précédemment définies après le changement d'UID (keepcaps) ou s'il ne peut, d'une manière générale, obtenir aucun nouveau privilège (no_new_privs). Dans la pratique, je configure Securebits de manière très stricte et je sacrifie le confort d'utilisation pour briser les chaînes d'exploits.

« File Capabilities » au lieu de « Set-UID »

Je remplace les binaires Set-UID par des capacités de fichier afin de réduire le risque de abaisser. Au lieu d'accorder les droits de root à un programme, je me contente d'attribuer le droit nécessaire. Voici à quoi ressemble généralement cette modification : setcap 'cap_net_bind_service=+ep' /usr/bin/meinserver. Avec getcap -r / Je vérifie quels fichiers contiennent des compétences. Cela réduit sensiblement les voies d'escalade.

Il est important de noter que les capacités de fichier ne s'appliquent qu'à Fichiers binaires ELF fonctionnent. Les scripts d'interpréteur (par exemple Python, Bash) ne les héritent pas de manière fiable. Dans de tels cas, j'encapsule l'action privilégiée dans un petit utilitaire vérifié de manière statique ou j'utilise l'activation par socket afin que mon service n'ait pas à s'attacher lui-même. De plus, je surveille de près les droits d’accès aux fichiers : les capacités confèrent des droits spéciaux vis-à-vis du noyau, mais ne remplacent pas pas de les ACL habituelles ou les droits d'accès POSIX.

Lorsqu'on copie ou qu'on regroupe des éléments, on perd rapidement ses compétences : cp sans prise en charge de XATTR, mal défini umask ou supprimer un artefact de compilation dans un système de fichiers ne disposant pas d'attributs étendus security.capability sans le dire. C'est pourquoi je travaille de manière reproductible et j'utilise : cp --preserve=xattr ..., tar --xattrs, rsync -X. Dans les builds de paquets, je définis explicitement les capacités des fichiers dans le script d'installation, je teste l'installation dans une machine virtuelle vierge et je vérifie getcap dans le CI.

Séparation des privilèges à l'aide de scénarios réalistes

Un serveur web a besoin d'accéder aux ports 80/443, mais pas aux modules du noyau ni aux redémarrages du système ; c'est pourquoi je configure CAP_NET_BIND_SERVICE et rien d'autre. Un agent de sauvegarde est autorisé à lire et à écrire des fichiers, mais ne peut pas modifier la configuration réseau. Un outil de surveillance dispose d’un accès en lecture aux indicateurs clés de performance, mais ne dispose d’aucun droit de modification. Ces restrictions limitent les attaques au niveau local, au lieu de les laisser se propager à l’ensemble du système. C’est précisément cette séparation qui permet de garder les services gérables et de limiter les erreurs de configuration.

Combiner avec sudo et les rôles

Les « capabilities » ne remplacent pas une structure des rôles, elles les complètent. J'accorde les droits sudo avec parcimonie, j'utilise des chemins d'accès complets aux commandes et j'évite les règles générales telles que „ ALL=(ALL) ALL “. Je consigne chaque autorisation dans un journal. Les groupes regroupent les responsabilités, tandis que les capacités fixent des limites techniques aux processus. Cela permet d’établir des responsabilités claires sans droits excessifs.

Pièges courants et bonnes pratiques

  • Pas d'abréviation pour CAP_SYS_ADMIN : Ce droit est un fourre-tout. Je le remplace par des alternatives plus ciblées (par exemple,. CAP_SYS_CHROOT, CAP_SYS_TIME, CAP_SYS_NICE) ou renonce-y complètement.
  • Les droits d'accès aux fichiers restent strictement définis : Les « Capabilities » ne désactivent pas systématiquement le DAC. Sans CAP_DAC_OVERRIDE Le noyau continue de respecter les bits de propriétaire et de mode. Je continue donc à n'accorder que le minimum de droits de lecture.
  • Durcissement par chemin : Lorsque j'attribue des capacités de fichier à un binaire, j'empêche l'usurpation de PATH (chemins absolus dans sudoers, droits d'écriture bloqués sur les répertoires du chemin de recherche).
  • Commencez tôt, faites-le souvent : Les processus démarrent parfois avec plus de droits que nécessaire. Je supprime les droits superflus immédiatement après l'étape délicate (prctl()/libcap) et définis no_new_privs, dans la mesure du possible.
  • Limiter la transmission héréditaire : Je limite la taille des ensembles « Inheritable » et « Ambient ». Les processus enfants ne doivent pas ouvrir de nouvelles portes.
  • Vérifier le pipeline de compilation et de déploiement : Je confirme que security.capability soit conservé et qu'aucune étape de préparation (couches de conteneurs, NFS, analyseur d'artefacts) ne supprime les XATTR.

Aperçu des principales capacités et des principaux risques

Avant d'attribuer des privilèges, je définis clairement ceux qui sont nécessaires et j'évalue leur niveau de risque. Le tableau suivant présente des exemples typiques avec leurs effets et leur classification. Je prends toujours en compte des alternatives afin d'éviter d'accorder des droits trop étendus. Notamment CAP_SYS_ADMIN Je les accorde avec une extrême parcimonie. Dans la mesure du possible, je remplace les privilèges trop généraux par des variantes ciblées et restreintes.

Capacité Objectif Risque Exemple
CAP_NET_BIND_SERVICE Se lier aux ports inférieurs à 1024 Faible à moyen Serveur web sur les ports 80/443
CAP_SYS_BOOT Redémarrer le système Haute Redémarrage prévu
CAP_SYS_MODULE Charger/désinstaller des modules du noyau Très élevé Gestion des pilotes
CAP_SYS_ADMIN Opérations d'administration variées Très élevé Diverses tâches de maintenance
CAP_SETUID / CAP_SETGID Changer l'UID/GID Moyen à élevé Changement de fonction au sein du service

Au-delà du classement, je suis en train d'évaluer CAP_SYS_PTRACE (débogage des processus), CAP_NET_ADMIN (paramétrage du réseau) et CAP_DAC_OVERRIDE (Contourner les restrictions d'accès aux fichiers) est une pratique extrêmement critiquable. Il existe souvent des modèles permettant d'éviter ces droits : des points de terminaison métriques dédiés plutôt que l'espionnage des processus, l'activation de sockets ou les redirection de ports plutôt que les droits « bind », et des droits d'accès aux fichiers bien définis plutôt qu'un contournement généralisé du DAC.

Renforcement dans les conteneurs et l'hébergement

Dans les environnements multi-locataires, je considère que les compétences sont radicalement petit et j'empêche la transmission aux processus enfants. Les conteneurs en tirent un avantage considérable dès que l'ensemble de délimitation est bien défini. Je combine cela avec des espaces de système de fichiers et de processus isolés. Pour avoir une vue d'ensemble des approches d'isolation, cette introduction à Isolation des processus. Ainsi, les services restent isolés les uns des autres, même si une application présente des dysfonctionnements.

Concrètement, je configure par défaut les conteneurs sur „ tout supprimer, ajouter de manière ciblée “ : --cap-drop=ALL --cap-add=NET_BIND_SERVICE pour les services Web, pas de droits de montage, pas de SYS_ADMIN. Dans les environnements orchestrés, je gère le profil de manière centralisée et je le vérifie au niveau des politiques. Important : je ne me fie pas aux capacités des fichiers dans l'image, mais j'attribue des droits d'exécution dans l'orchestrateur – de manière reproductible et vérifiable.

Interaction avec SELinux et AppArmor

Les capacités déterminent ce qu'un processus est autorisé à faire, tandis que les profils MAC définissent ce à quoi il a accès, et les deux s'harmonisent bien. Je définis les capacités de manière restrictive et laisse SELinux ou AppArmor limiter l'accès aux fichiers et aux sockets. Cela crée une protection par couches qui impose plusieurs obstacles aux exploits. Je trouve ici un comparatif rapide : SELinux vs AppArmor. Ainsi, un service compromis reste isolé et peut causer moins de dégâts.

Mise en pratique : procéder étape par étape

Je commence par dresser un état des lieux de tous les services et de leurs Exigences. Ensuite, je supprime les binaires Set-UID superflus ou je les remplace par des capacités de fichier ciblées. Je configure sudo de manière restrictive et je documente chaque entrée. J'attribue des tâches aux rôles et aux groupes et je limite les droits au strict minimum. Je procède ensuite à des tests sous charge et vérifie les entrées des journaux pour détecter d’éventuels refus inattendus.

Une petite liste de contrôle m'aide à faire la transition :

  • Consigner par écrit les exigences pour chaque service (uniquement ce qui est réellement nécessaire).
  • Recenser les droits spéciaux existants (find / -perm -4000, getcap -r /).
  • Remplacer de manière ciblée : supprimer le Set-UID, définir les capacités de fichier, supprimer les droits dès que possible.
  • Fermer les héritages : réduire l'ensemble de délimitation, minimiser les éléments héritables/ambiants.
  • Sécuriser les profils Systemd/conteneurs (CapabilityBoundingSet= Ensemble de compétences, NoNewPrivileges=yes).
  • Tester en conditions de charge, vérifier les journaux et les entrées d'audit, documenter les exceptions.

Surveillance, espaces de noms et audits continus

Je surveille les fichiers journaux, les alertes et les appels système afin que toute action indésirable soit immédiatement se faire remarquer. Je vérifie régulièrement les modifications apportées aux capacités, aux règles sudo et aux rôles. Lorsque cela s'avère pertinent, je sépare également les charges de travail à l'aide des mécanismes d'isolation du noyau. Cet aperçu constitue un bon point de départ pour Espaces de noms et Cgroups. Cela me permet de détecter rapidement les anomalies et de maintenir les lieux propres.

Au quotidien, j'utilise des tests simples : capsh --print affiche l'ensemble actuel des compétences, getpcaps répertorie les droits de processus et dans /proc//status je lis CapEff, CapPrm, CapBnd. Avec auditd je suis les modifications apportées au statut de la capacité (par exemple, règle sur capset), je mets en corrélation les événements avec les déploiements et je déclenche des alertes lorsque des droits étendus apparaissent soudainement. Pour les cas complexes, je m'aide de strace -e capget,capset, afin de mettre en évidence les manipulations des droits.

Exemples pratiques concernant Systemd et les conteneurs

Je gère de nombreux services sous forme d'unités systemd et j'y encapsule les droits :

  • CapabilityBoundingSet=CAP_NET_BIND_SERVICE limite la fenêtre des droits accessibles au strict nécessaire.
  • AmbientCapabilities=CAP_NET_BIND_SERVICE permet au service de se connecter aux ports 80/443 sans capacités de fichiers.
  • NoNewPrivileges=yes empêche toute extension ultérieure des droits.
  • Utilisateur=, Groupe=, ProtectSystem=strict, PrivateTmp=yes complètent l'isolation.

Dans les conteneurs, je lance les processus de la manière la plus minimaliste possible : docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE --read-only. Pour les tâches de courte durée, j'utilise les capacités d'exécution plutôt que les capacités de fichiers dans l'image, afin que les builds restent reproductibles et que les droits soient liés à l'environnement.

Exemples concrets de migrations tirés de la pratique

  • ping sans Set-UID : Au lieu de setuid root je mets setcap 'cap_net_raw=+ep' /bin/ping. Cela permet à tout utilisateur d'ouvrir des sockets ICMP sans disposer de tous les droits root. Je vérifie régulièrement avec getcap /bin/ping, si l'attribut a été conservé.
  • Service Web sur les ports 80/443 : Je laisse mon service tourner en tant qu'utilisateur non privilégié et je ne donne que cap_net_bind_service. Si le service est de toute façon placé derrière un proxy inverse, je peux également le configurer sur les ports 80/443 et utiliser un port élevé en interne – sans avoir besoin de compétences supplémentaires.
  • Changement de partie au procès : Pour les outils qui ont besoin de droits étendus pendant un court instant (par exemple, pour définir les niveaux de priorité), je configure cap_sys_nice, effectue l'action rapidement, puis désactive la compétence. J'évite de conserver des droits accrus de manière permanente.

Limites et alternatives

Toutes les situations ne nécessitent pas le recours aux « Capabilities ». Il existe souvent des alternatives sûres présentant moins de risques :

  • Activation du socket : Le service d'initialisation (par exemple, systemd) ouvre des sockets privilégiées et les transmet au processus. Mon service n'a alors pas besoin de droits « bind ».
  • Redirection de port : À l'aide de règles de pare-feu, je redirige les ports 80/443 vers un port élevé. Le service reste sans privilèges, le comportement du système ne change pas.
  • Ports « low » non privilégiés : Lorsque cela s'avère approprié, je peux relever le seuil pour les ports non privilégiés. Cela élargit toutefois la marge de manœuvre pour tous les processus ; je pèse donc soigneusement le rapport entre risque et confort.
  • De petits outils plutôt que des appareils polyvalents : Mieux vaut un petit binaire audité ne disposant que d'une seule fonctionnalité qu'un gros monolithe offrant un large éventail de droits.

En bref

Avec Capacités de Linux Je divise les pouvoirs root en petits privilèges faciles à contrôler. Les capacités de fichiers remplacent les binaires Set-UID risqués et limitent les conséquences d'une attaque. Associées à des règles sudo strictes, à des rôles et à des profils MAC, elles créent une protection par couches aux limites clairement définies. Les ensembles « Bounding » et « Inheritable » limitent l’héritage et maintiennent les processus sur la bonne voie. En procédant ainsi, on réduit sensiblement les surfaces d’attaque et on maintient la charge administrative à un niveau raisonnable.

Derniers articles