Aller au contenu

Les permissions

200 Pratiquer ⏱ 1 h 15 linuxubuntudebiansystemd

À la fin, vous saurez

  • Lire les permissions affichées par ls -l et stat, et prédire si un processus donné pourra lire, écrire ou exécuter un fichier
  • Expliquer le sens de r, w et x sur un répertoire, différent de leur sens sur un fichier
  • Régler propriétaire, groupe et droits avec chown, chgrp et chmod, en notation symbolique et octale
  • Calculer l'effet d'un umask et le régler pour une session ou un service
  • Utiliser le bit setgid pour un répertoire d'équipe et reconnaître setuid et sticky
  • Diagnostiquer méthodiquement un « Permission denied » avec namei, id et stat

Prérequis

Testé avec acl 2.3.2 coreutils 9.4 systemd 255 ubuntu 24.04 util-linux 2.39.3 , vérifié le 5 octobre 2026

Pourquoi

Lundi matin, le service Signalements ne redémarre plus sur sig-app-1. systemctl status signalements indique que le processus principal s'est terminé avec le code status=203/EXEC, que systemd.exec(5) décrit ainsi : l'appel système qui lance le programme a échoué, le plus souvent parce que l'exécutable est absent ou inaccessible. Une ligne du journal précise la cause : systemd n'a pas pu lancer /opt/signalements/venv/bin/gunicorn, Permission denied.

Vendredi, quelqu'un a « rangé » le serveur. Le fichier gunicorn est pourtant là, et ses droits n'ont pas changé. Deux personnes proposent deux corrections. La première : sudo chmod -R 777 /opt/signalements, « comme ça, plus de problème ». La seconde ne propose rien, mais demande : « sous quelle identité le service tourne-t-il, et qu'est-ce qui l'empêche d'atteindre ce fichier ? »

La première correction marche, au sens où le service redémarre. Elle rend aussi le code de l'application modifiable par n'importe quel compte de la machine, à commencer par le compte du service lui-même : la moindre faille de l'application permettrait à un attaquant de réécrire son code et de s'installer durablement. La seconde question est la bonne, et cette leçon vous apprend à y répondre en quelques minutes.

Les permissions Unix sont un modèle simple, vieux de cinquante ans, et pourtant mal compris : la plupart des « Permission denied » que l'on rencontre ne viennent pas du fichier lui-même, mais d'un répertoire sur le chemin, ou d'un groupe que le processus n'a pas. Les comprendre vous évitera à la fois les heures perdues et les chmod 777.

Les concepts

Trois classes, trois droits

Chaque fichier (et chaque répertoire, qui est un fichier d'un type particulier) a un propriétaire (un UID), un groupe (un GID) et un mode : douze bits, dont neuf forment les permissions au sens courant. Ces neuf bits se lisent en trois groupes de trois :

-rw-r-----  1  root  signalements  58  Oct  5 09:12  /etc/signalements/env
│└┬┘└┬┘└┬┘     └─┬┘  └─────┬────┘
│ │  │  │        │         └── groupe du fichier
│ │  │  │        └──────────── propriétaire
│ │  │  └── autres (o, others) : ---
│ │  └───── groupe (g, group)  : r--
│ └──────── propriétaire (u, user) : rw-
└────────── type : - fichier, d répertoire, l lien symbolique

Pour chaque classe, trois droits : r (lire), w (écrire), x (exécuter). Un tiret signifie que le droit est absent.

Une seule classe s'applique

Quand un processus accède à un fichier, le noyau choisit une seule des trois classes, selon une règle que path_resolution(7) énonce précisément :

  1. si l'UID (effectif) du processus est celui du propriétaire, on applique les droits du propriétaire ;
  2. sinon, si le groupe du fichier est le groupe principal du processus ou l'un de ses groupes supplémentaires, on applique les droits du groupe ;
  3. sinon, on applique les droits des autres.

Les droits ne se cumulent pas. Un fichier en ----rwxrwx est illisible par son propriétaire, alors que tous les autres comptes peuvent le lire : le noyau s'arrête à la première classe qui correspond. C'est contre-intuitif, et c'est démontré plus bas.

r, w, x sur un fichier et sur un répertoire

Sur un fichier, les trois droits sont ce que l'on attend : lire son contenu, le modifier, l'exécuter comme un programme. Un script a besoin de r et de x (l'interpréteur doit pouvoir le lire) ; un binaire compilé n'a besoin que de x.

Sur un répertoire, ils changent de sens, parce qu'un répertoire est une liste de noms pointant vers des fichiers :

DroitSur un fichierSur un répertoire
rlire le contenulister les noms qu'il contient (ls)
wmodifier le contenucréer, renommer, supprimer des entrées (avec x)
xexécutertraverser : accéder à un élément dont on connaît le nom, cd dans le répertoire

Trois conséquences surprennent toujours :

  • x sur chaque répertoire du chemin est obligatoire. Pour ouvrir /etc/signalements/env, le processus doit pouvoir traverser /, /etc et /etc/signalements. Un fichier en 0644 dans un répertoire en 0700 reste inaccessible à tous sauf au propriétaire du répertoire.
  • r sans x sur un répertoire permet de voir les noms, mais pas d'accéder aux fichiers ni à leurs informations. x sans r permet d'ouvrir un fichier dont on connaît le nom exact, sans pouvoir lister le répertoire.
  • Supprimer un fichier est un droit sur le répertoire, pas sur le fichier. Avec w et x sur le répertoire, on peut supprimer un fichier en lecture seule, et même un fichier qui appartient à quelqu'un d'autre. Inversement, sans w sur le répertoire, on ne peut pas supprimer son propre fichier.

Notation symbolique et octale

chmod accepte deux notations. La notation symbolique dit qui (u, g, o, ou a pour tous), quelle opération (+ ajouter, - retirer, = fixer exactement) et quels droits :

chmod u=rw,g=r,o= fichier     # fixe exactement : rw- r-- ---
chmod g+w fichier             # ajoute w au groupe, sans toucher au reste
chmod o-rwx fichier           # retire tout aux autres
chmod a+X dossier             # x seulement pour les répertoires (et fichiers déjà exécutables)

La notation octale écrit chaque classe comme un chiffre de 0 à 7, somme de r = 4, w = 2, x = 1 :

OctalDroitsUsage typique
644rw-r--r--fichier ordinaire, lisible par tous
640rw-r-----fichier de configuration lisible par un groupe
600rw-------fichier secret personnel (clé SSH privée)
755rwxr-xr-xprogramme, répertoire public
750rwxr-x---répertoire réservé au propriétaire et à un groupe
700rwx------répertoire privé

L'octal fixe tout d'un coup ; le symbolique modifie une partie. Le premier convient pour poser un état connu, le second pour ajuster sans écraser ce qu'on ne connaît pas. Le X majuscule de la notation symbolique est précieux avec chmod -R : il donne x aux répertoires sans rendre exécutables tous les fichiers.

Les trois bits spéciaux

Le mode compte trois bits de plus, écrits comme un quatrième chiffre octal en tête (4755, 2775, 1777) :

BitOctalSur un fichier exécutableSur un répertoireAffichage
setuid4000le programme s'exécute avec l'UID du propriétaire du fichiersans effet sous Linuxs à la place du x du propriétaire
setgid2000le programme s'exécute avec le GID du groupe du fichierles fichiers créés dedans héritent du groupe du répertoire, et les sous-répertoires du bit lui-mêmes à la place du x du groupe
sticky1000sans effet sous Linuxdans ce répertoire, on ne peut supprimer ou renommer que ses propres fichierst à la place du x des autres

Une majuscule (S, T) signifie que le bit spécial est posé sans le x correspondant, ce qui est presque toujours une erreur.

Vous avez rencontré le setuid à la leçon 8 : c'est lui qui permet à sudo et à passwd de démarrer avec les droits de root. Le setgid sur un répertoire est l'outil des dossiers d'équipe. Le sticky est celui de /tmp, où tout le monde peut écrire mais personne ne doit pouvoir effacer les fichiers des autres.

Le masque de création : umask

Quand un programme crée un fichier, il demande un mode (en général 666 pour un fichier, 777 pour un répertoire), et le noyau en retire les bits présents dans le umask du processus. Le umask est hérité du parent, comme l'environnement.

umaskFichier créé (666 sans le masque)Répertoire créé (777 sans le masque)Usage
022644 rw-r--r--755 rwxr-xr-xdéfaut traditionnel, et défaut des services systemd
002664 rw-rw-r--775 rwxrwxr-xdéfaut des sessions Debian et Ubuntu, avec les groupes privés
027640 rw-r-----750 rwxr-x---serveur : rien pour les autres
077600 rw-------700 rwx------données sensibles

Le calcul n'est pas une soustraction mais un « et non » bit à bit : un umask de 033 sur un fichier 666 donne 644, et non 633, puisqu'il n'y avait pas de bit x à retirer.

Le umask de 002 d'Ubuntu peut sembler laxiste : il rend vos fichiers modifiables par votre groupe. Il est sans risque parce que votre groupe principal ne contient que vous (le groupe privé vu à la leçon 8), et il devient utile dans un répertoire d'équipe setgid, où les fichiers créés appartiennent au groupe de l'équipe et lui sont donc modifiables.

Pour un service, systemd fixe le umask par la directive UMask=, qui vaut 0022 par défaut pour les unités système d'après systemd.exec(5).

Au-delà des neuf bits : les ACL

Le modèle propriétaire, groupe, autres ne permet de nommer qu'un groupe. Pour donner en plus la lecture à un second compte sans créer de groupe, Linux propose les ACL POSIX (Access Control Lists), des entrées supplémentaires (user:www-data:r--) attachées au fichier. Elles se lisent avec getfacl, se posent avec setfacl, et leur présence est signalée par un + à la fin des permissions dans ls -l. Les ACL résolvent de vrais problèmes, mais elles rendent les droits invisibles à qui ne regarde que ls -l : utilisez-les avec parcimonie, et préférez un groupe quand c'est possible.

Il existe enfin des attributs propres aux systèmes de fichiers ext4 et XFS, indépendants des permissions : chattr +i rend un fichier immuable, même pour root, jusqu'au chattr -i. On les lit avec lsattr. Ils servent rarement, et désorientent d'autant plus le jour où un fichier refuse d'être modifié par root avec Operation not permitted.

En pratique

Les sorties de cette section ont été produites sur Ubuntu 24.04, dans un répertoire d'essai, avec un système en anglais. Le compte s'y appelle camille, et signalements-dev désigne un groupe supplémentaire dont elle est membre. Les commandes qui touchent /etc/signalements demandent sudo et sont décrites sans sortie.

Lire les permissions

$ printf 'APP_VERSION=1.3.0\n' > env && mkdir conf
$ ls -l
total 8
drwxr-xr-x 2 camille camille 4096 Oct  5 11:20 conf
-rw-r--r-- 1 camille camille   18 Oct  5 11:20 env
$ stat -c '%A %a %U:%G %n' env conf
-rw-r--r-- 644 camille:camille env
drwxr-xr-x 755 camille:camille conf

stat -c (format) affiche les champs demandés : %A les permissions lisibles, %a l'octal, %U et %G le propriétaire et le groupe. C'est la commande à utiliser dans un script ; ls -l est faite pour les yeux. Ces fichiers ont été créés avec un umask de 022.

Le propriétaire n'a pas les droits du groupe

$ chmod 0077 env
$ ls -l env
----rwxrwx 1 camille camille 18 Oct  5 11:20 env
$ cat env
cat: env: Permission denied

Camille est propriétaire : le noyau applique les droits du propriétaire (---) et s'arrête là, même si le groupe et les autres ont tous les droits. Rétablissons un état raisonnable, de deux façons :

$ chmod u=rw,g=r,o= env
$ ls -l env
-rw-r----- 1 camille camille 18 Oct  5 11:20 env
$ chmod g+w,o+r env
$ ls -l env
-rw-rw-r-- 1 camille camille 18 Oct  5 11:20 env
$ chmod 640 env
$ ls -l env
-rw-r----- 1 camille camille 18 Oct  5 11:20 env

Le x des répertoires

Avec r mais sans x, on voit les noms mais rien d'autre :

$ echo x > conf/f
$ chmod 644 conf
$ ls -l conf
ls: cannot access 'conf/f': Permission denied
total 0
-????????? ? ? ? ?            ? f
$ cat conf/f
cat: conf/f: Permission denied

ls a pu lire la liste des noms (f), mais pas les informations du fichier, d'où les points d'interrogation. Avec x mais sans r, c'est l'inverse :

$ chmod 100 conf
$ ls -ld conf
d--x------ 2 camille camille 4096 Oct  5 11:20 conf
$ ls conf
ls: cannot open directory 'conf': Permission denied
$ cat conf/f
x

On ne peut pas lister, mais on peut ouvrir un fichier dont on connaît le nom. C'est exactement la situation d'un répertoire personnel en 0711 sur certains serveurs web : on traverse sans voir.

Supprimer dépend du répertoire

$ chmod 755 conf
$ echo y > conf/g && chmod 444 conf/g
$ chmod 555 conf
$ rm -f conf/g
rm: cannot remove 'conf/g': Permission denied
$ chmod 755 conf
$ rm -f conf/g && echo supprimé
supprimé

Le fichier est en lecture seule dans les deux cas. Ce qui change, c'est le w du répertoire : sans lui, Camille ne peut pas supprimer son propre fichier ; avec lui, elle supprime un fichier en lecture seule sans que rien ne s'y oppose (-f supprime même la question que rm pose d'habitude pour un fichier protégé en écriture).

Le umask en action

$ for m in 022 002 027 077; do umask $m; touch f$m; mkdir d$m; done
$ ls -ld f0* d0*
drwxrwxr-x 2 camille camille 4096 Oct  5 11:20 d002
drwxr-xr-x 2 camille camille 4096 Oct  5 11:20 d022
drwxr-x--- 2 camille camille 4096 Oct  5 11:20 d027
drwx------ 2 camille camille 4096 Oct  5 11:20 d077
-rw-rw-r-- 1 camille camille    0 Oct  5 11:20 f002
-rw-r--r-- 1 camille camille    0 Oct  5 11:20 f022
-rw-r----- 1 camille camille    0 Oct  5 11:20 f027
-rw------- 1 camille camille    0 Oct  5 11:20 f077

Le tableau des concepts, vérifié. umask sans argument affiche la valeur courante (0002 dans une session Ubuntu), umask -S la même en notation symbolique.

Un répertoire d'équipe avec setgid

L'équipe veut un répertoire où chacun dépose des rapports que tous peuvent modifier :

$ mkdir partage
$ chgrp signalements-dev partage
$ chmod 2770 partage
$ touch partage/a.txt && mkdir partage/sous
$ ls -ld partage partage/a.txt partage/sous
drwxrws--- 3 camille signalements-dev 4096 Oct  5 11:20 partage
-rw-rw-r-- 1 camille signalements-dev    0 Oct  5 11:20 partage/a.txt
drwxrwsr-x 2 camille signalements-dev 4096 Oct  5 11:20 partage/sous
  • chgrp donne le répertoire au groupe de l'équipe (on peut le faire vers un groupe dont on est membre ; seul root peut changer le propriétaire, avec chown).
  • 2770 : le 2 en tête pose le bit setgid, affiché s à la place du x du groupe ; 770 donne tout au propriétaire et au groupe, rien aux autres.
  • a.txt appartient automatiquement au groupe signalements-dev, et non au groupe principal de Camille ; le sous-répertoire hérite en plus du bit setgid.
  • Avec le umask 002 de la session, le fichier est inscriptible par le groupe : les collègues peuvent le modifier.

Sans le bit setgid, le même fichier aurait appartenu au groupe camille, et les autres membres de l'équipe n'auraient pu que le lire, s'ils pouvaient seulement entrer dans le répertoire.

Le sticky bit de /tmp

$ ls -ld /tmp
drwxrwxrwt 160 root root 57344 Oct  5 11:20 /tmp
$ stat -c '%A %a %n' /tmp
drwxrwxrwt 1777 /tmp

/tmp est inscriptible par tous (rwx pour les autres) : n'importe qui peut y créer des fichiers. Sans le t final, n'importe qui pourrait aussi y supprimer ou remplacer ceux des autres, puisque supprimer est un droit sur le répertoire. Le sticky bit restreint la suppression au propriétaire du fichier, au propriétaire du répertoire et à root.

Une ACL, pour voir

$ setfacl -m u:www-data:r env
$ ls -l env
-rw-r-----+ 1 camille camille 18 Oct  5 11:20 env
$ getfacl env
# file: env
# owner: camille
# group: camille
user::rw-
user:www-data:r--
group::r--
mask::r--
other::---

Le + signale l'ACL. user:www-data:r-- donne la lecture au compte www-data en plus des trois classes habituelles. La ligne mask:: est la limite maximale des droits accordés aux entrées nommées et au groupe : chmod g-w sur un fichier qui a une ACL modifie ce masque, et peut réduire silencieusement les droits d'une entrée, ce que getfacl signale par un commentaire #effective:. Retirez toutes les entrées avec setfacl -b env.

Réparer le service Signalements

Revenons à l'incident. La méthode, en quatre questions.

1. Quel processus, sous quelle identité ? Le service tourne sous le compte signalements : c'est sous cette identité que systemd lance gunicorn, après avoir abandonné les droits de root. Vérifiez-le, ainsi que ses groupes :

$ systemctl show signalements -p User -p Group
$ id signalements

2. Que dit le fichier ?

$ stat -c '%A %a %U:%G %n' /opt/signalements/venv/bin/gunicorn

Un programme de l'environnement virtuel, installé par root, est normalement en -rwxr-xr-x 755 root:root : exécutable par tous. Le fichier n'est pas en cause.

3. Et chaque répertoire du chemin ? C'est là que se cachent la plupart des refus. namei -l décompose un chemin et affiche les permissions de chaque élément. Sur un fichier du système, pour voir la forme de la sortie :

$ namei -l /etc/shadow
f: /etc/shadow
drwxr-xr-x root root   /
drwxr-xr-x root root   etc
-rw-r----- root shadow shadow

Sur sig-app-1, namei -l /opt/signalements/venv/bin/gunicorn doit montrer chaque répertoire traversable par le compte signalements. Le « rangement » de vendredi avait passé /opt/signalements en drwx------ root root : le fichier était correct, mais le compte du service ne pouvait plus traverser le répertoire qui le contient. Profitez-en pour contrôler aussi le fichier de configuration : namei -l /etc/signalements/env doit finir par -rw-r----- root signalements env, dans un /etc/signalements en drwxr-x--- root signalements. (Ce fichier est lu par systemd, en root, avant le changement d'identité, comme l'a montré la leçon 7 ; ses droits protègent le secret contre les autres comptes.)

4. Corriger au plus juste.

$ sudo chmod 755 /opt/signalements
$ sudo -u signalements test -x /opt/signalements/venv/bin/gunicorn && echo "exécutable par signalements"
$ sudo systemctl restart signalements

Le code reste la propriété de root, lisible et exécutable par tous mais modifiable par root seul. Si le code ne doit pas être lisible par les autres comptes, sudo chown root:signalements /opt/signalements et sudo chmod 750 /opt/signalements donnent la traversée au seul groupe du service.

sudo -u signalements exécute une commande sous l'identité du compte de service : c'est la façon la plus directe de tester un droit tel que le service le voit, plutôt que de raisonner. test -x (ou -r, -w) répond par son code de sortie. Pour un fichier que l'on crée de toutes pièces, install pose le propriétaire, le groupe et le mode en une commande, sans fenêtre de temps où le fichier existerait avec de mauvais droits :

$ sudo install -m 0640 -o root -g signalements /dev/null /etc/signalements/env

Sous le capot

Le mode vit dans l'inode

Un fichier, sur un système de fichiers Linux, est décrit par un inode : une structure qui contient son type, son mode (les douze bits de permissions et les quatre bits du type), son propriétaire, son groupe, sa taille, ses dates et l'emplacement de ses données. Le nom du fichier n'est pas dans l'inode, mais dans le répertoire qui le contient, sous forme d'une entrée « nom → numéro d'inode ». inode(7) détaille ces champs, et stat les affiche tous.

Ce découpage explique les règles des répertoires : lister, c'est lire les entrées du répertoire (r) ; supprimer un fichier, c'est retirer une entrée du répertoire (w), ce qui ne touche pas à l'inode du fichier ; traverser, c'est avoir le droit de chercher un nom dans la liste pour suivre le numéro d'inode (x).

La vérification à l'ouverture

Le contrôle a lieu au moment de l'appel système, open(2) pour un fichier. Le noyau résout le chemin composant par composant, vérifie x sur chaque répertoire traversé, puis les droits demandés (r pour une lecture, w pour une écriture) sur le fichier final, en choisissant la classe comme décrit plus haut. S'il refuse, l'appel renvoie l'erreur EACCES, que les programmes affichent comme Permission denied.

Deux conséquences pratiques :

  • Un programme qui a déjà ouvert un fichier garde son accès si l'on change ensuite les permissions : le contrôle a eu lieu à l'ouverture. Un service doit être redémarré pour qu'un retrait de droits le concerne, et un ajout de droits ne sert qu'à la prochaine ouverture.
  • Un autre message, Operation not permitted (EPERM), signale en général autre chose qu'un défaut de permission classique : une opération réservée au propriétaire ou à root (comme chown par un utilisateur ordinaire), ou un attribut chattr +i.

Ce que root contourne, et ce qu'il ne contourne pas

Sous Linux, les pouvoirs de root sont découpés en capabilities (glossaire), dont deux concernent les fichiers. D'après path_resolution(7) :

  • CAP_DAC_OVERRIDE contourne tous les contrôles de permissions, mais n'accorde l'exécution que si au moins un des trois bits x est posé ;
  • CAP_DAC_READ_SEARCH accorde la lecture des fichiers, et la lecture et la traversée des répertoires.

root lit donc /etc/shadow malgré ses rw-r----- qui ne le concernent pas en tant que groupe, mais ne peut pas exécuter un script en 644 tant qu'aucun x n'est posé. C'est aussi pourquoi un problème de permission ne se reproduit pas avec sudo : tester un droit en root ne prouve rien pour le compte du service. Testez avec sudo -u <compte>.

Les containers et les services durcis retirent souvent ces capabilities à root (cours Docker : les fondamentaux) : un root sans CAP_DAC_OVERRIDE se heurte aux permissions comme tout le monde.

Pièges courants

chmod 777 pour « réparer ». Le fichier ou le répertoire devient modifiable par tout compte de la machine. Ce n'est jamais la correction : trouvez quelle identité a besoin de quel droit, et donnez celui-là.

Oublier le x d'un répertoire du chemin. Le fichier a les bons droits, l'accès échoue quand même. namei -l chemin montre l'élément fautif en une commande.

chmod -R 644 sur un répertoire. Tous les sous-répertoires perdent leur x et deviennent intraversables. Utilisez chmod -R u=rwX,g=rX,o= dossier (avec le X majuscule), ou find dossier -type d -exec chmod 750 {} + puis find dossier -type f -exec chmod 640 {} +.

Tester en root. « Je peux lire le fichier » ne veut rien dire si vous êtes root. sudo -u signalements test -r /etc/signalements/env && echo ok teste réellement.

Changer de groupe et oublier la reconnexion. Ajouter le compte au bon groupe ne suffit pas pour une session ou un service déjà lancé : reconnexion, ou redémarrage du service (leçon 8).

Un umask qui crée des fichiers trop ouverts. Un script lancé avec le umask par défaut écrit un fichier de sauvegarde ou de secrets en 644. Fixez umask 077 en tête du script, ou créez le fichier avec install -m 600.

Le + qu'on ne voit pas. Un fichier en 640 qui reste lisible par un compte inattendu porte souvent une ACL. getfacl la montre.

S ou T majuscule. Un bit spécial posé sans le x correspondant n'a pas l'effet voulu. Ajoutez le x, ou retirez le bit.

Sécurité

Le moindre privilège, fichier par fichier. Un secret est lisible par le seul compte qui en a besoin (640 root:signalements), un code applicatif n'est pas modifiable par le compte qui l'exécute (/opt/signalements appartient à root, le service tourne sous signalements) : si l'application est compromise, l'attaquant ne peut pas modifier son propre code pour s'installer durablement.

Les fichiers inscriptibles par tous sont des portes. Un script ou un fichier de configuration modifiable par n'importe qui, et exécuté ou lu par root ou par un service, permet une élévation de privilèges. Recherchez-les :

$ sudo find / -xdev -type f -perm -o+w -ls
$ sudo find / -xdev -type d -perm -o+w ! -perm -1000 -ls

La première commande liste les fichiers inscriptibles par les autres ; la seconde, les répertoires inscriptibles par tous sans sticky bit. -xdev reste sur le système de fichiers de départ (pas de parcours de /proc ni des montages réseau). -perm -o+w signifie « au moins ce bit posé ».

Les programmes setuid sont à inventorier. Chacun est un programme qui démarre en root à la demande de n'importe quel utilisateur : une faille dans l'un d'eux suffit à obtenir root. Sur la machine Ubuntu 24.04 qui a servi à préparer cette leçon :

$ find /usr/bin /usr/sbin -xdev -perm -4000 -type f | sort
/usr/bin/chfn
/usr/bin/chsh
/usr/bin/fusermount3
/usr/bin/gpasswd
/usr/bin/mount
/usr/bin/newgrp
/usr/bin/passwd
/usr/bin/pkexec
/usr/bin/su
/usr/bin/sudo
/usr/bin/umount
/usr/sbin/pppd

Chacun a une raison d'être (changer son mot de passe, monter un support amovible...). Un programme setuid inattendu, surtout hors de /usr, dans /tmp ou dans un répertoire personnel, est un signe classique de compromission. Gardez l'inventaire d'un serveur sain pour pouvoir le comparer, et montez les partitions de données avec l'option nosuid (cours Durcissement Linux).

Un script setuid n'est pas une solution. Linux ignore le bit setuid sur les scripts interprétés, précisément parce qu'il était impossible à sécuriser. Pour donner un droit précis à un utilisateur, la réponse est une règle sudo limitée (leçon 8).

En production

  • Les permissions sont du code. Elles sont posées par l'outil qui installe le fichier (paquet, Ansible, image de conteneur), jamais corrigées à la main sans être reportées dans cet outil, sinon la prochaine installation défait la correction.
  • Les services fixent leur umask (UMask=0027 dans l'unité) quand ils créent des fichiers qui ne doivent pas être lisibles par tous, et systemd sait créer leurs répertoires avec les bons droits (StateDirectory=, RuntimeDirectory=, cours systemd en profondeur).
  • Les audits automatiques (Lynis, les profils CIS appliqués par des outils de conformité) vérifient ces points : fichiers inscriptibles par tous, setuid, droits de /etc/shadow, du répertoire ~/.ssh et des fichiers de /etc/sudoers.d. Le cours Durcissement Linux les présente.
  • Dans les conteneurs, les mêmes règles s'appliquent avec une difficulté de plus : l'UID du processus dans le conteneur doit correspondre aux propriétaires des volumes montés. Le cours Construire des images de conteneurs montre comment fixer un UID non privilégié et préparer les répertoires en conséquence.

Exercices

1. Lire et prédire (niveau 100). Pour chaque cas, dites si l'opération réussit, et pourquoi. Le compte signalements est membre du seul groupe signalements.

  • a) signalements lit /opt/signalements/app.py, en -rw-r--r-- root root, dans un répertoire drwxr-xr-x root root.
  • b) signalements modifie ce même fichier.
  • c) signalements lit /etc/signalements/env, en -rw-r----- root signalements, dans /etc/signalements en drwx------ root root.
  • d) camille, propriétaire de notes.txt en ----r--r--, le lit.
Solution

a) Oui : signalements n'est ni propriétaire ni dans le groupe root, donc la classe « autres » s'applique, avec r ; le répertoire est traversable par les autres. b) Non : les autres n'ont pas w sur le fichier (et c'est voulu, le code ne doit pas être modifiable par le compte qui l'exécute). c) Non : le fichier serait lisible par le groupe, mais le répertoire n'a aucun droit pour le groupe ni pour les autres, il est intraversable. d) Non : Camille est propriétaire, la classe propriétaire s'applique (---), et les droits du groupe et des autres ne s'ajoutent pas.

2. Calculer des umask (niveau 100). Quels droits obtiennent un fichier et un répertoire créés avec un umask de 007 ? De 026 ? Quel umask choisiriez-vous pour un script qui écrit des sauvegardes de la base de données ?

Solution

007 : fichier 660 (rw-rw----), répertoire 770 (rwxrwx---). 026 : fichier 640 (rw-r----- : le masque retire w au groupe, r et w aux autres), répertoire 751 (rwxr-x--x). Pour des sauvegardes de base, qui contiennent toutes les données : 077, soit des fichiers en 600 lisibles par le seul compte qui les écrit.

3. Le dossier d'équipe (niveau 200). Créez /srv/rapports pour le groupe signalements-dev : tous les membres doivent pouvoir créer, lire et modifier les fichiers de tous, mais aucun ne doit pouvoir supprimer les fichiers d'un autre, et les autres comptes ne doivent rien voir. Donnez les commandes et justifiez chaque bit.

Solution
$ sudo mkdir /srv/rapports
$ sudo chgrp signalements-dev /srv/rapports
$ sudo chmod 3770 /srv/rapports

3770 = setgid (2000) + sticky (1000) + rwxrwx---. Le setgid fait appartenir tous les nouveaux fichiers au groupe signalements-dev ; avec le umask 002 des sessions Ubuntu, ils sont créés en 664, donc modifiables par le groupe. Le sticky interdit de supprimer ou renommer les fichiers des autres. --- pour les autres : rien de visible. Limite : un membre peut toujours vider le contenu d'un fichier d'un collègue, puisqu'il a le droit d'écriture sur le fichier lui-même ; le sticky ne protège que contre la suppression et le renommage.

4. Le refus mystérieux (niveau 200). Après une mise à jour, Signalements écrit dans son journal applicatif PermissionError: [Errno 13] Permission denied: '/var/lib/signalements/cache/index.db'. Décrivez dans l'ordre les commandes que vous lancez pour trouver la cause, sans rien modifier, et les causes possibles.

Solution

Errno 13 est EACCES. Dans l'ordre : systemctl show signalements -p User -p Group -p UMask (sous quelle identité tourne le service) ; id signalements (ses groupes) ; namei -l /var/lib/signalements/cache/index.db (chaque élément du chemin, avec propriétaire, groupe et droits) ; getfacl sur le fichier et le répertoire si un + apparaît ; sudo -u signalements test -w /var/lib/signalements/cache/index.db; echo $? pour vérifier le droit tel que le service le voit. Causes possibles : la mise à jour a recréé le fichier ou le répertoire en root (cas le plus fréquent, après un script de migration lancé avec sudo) ; un répertoire du chemin a perdu son x ; le service tourne désormais sous une autre identité ; plus rarement un attribut immuable (lsattr). La correction se fait ensuite au plus juste, et dans l'outil qui a créé le fichier.

Récapitulatif

  • Chaque fichier a un propriétaire, un groupe et un mode ; le noyau applique une seule classe : propriétaire, sinon groupe (principal ou supplémentaire), sinon autres. Les droits ne se cumulent pas.
  • Sur un répertoire, r liste, w crée et supprime, x traverse. Chaque répertoire du chemin doit être traversable ; supprimer un fichier dépend du répertoire, pas du fichier.
  • chmod en octal (640, 750) fixe tout, en symbolique (g+w, o=, X) ajuste ; chown et chgrp changent propriétaire et groupe.
  • Le umask retire des bits aux fichiers créés : 022 par défaut pour les services, 002 pour les sessions Ubuntu, 027 ou 077 pour les données sensibles.
  • setuid : exécuter avec l'UID du propriétaire (sudo, passwd) ; setgid sur un répertoire : héritage du groupe ; sticky : on ne supprime que ses propres fichiers (/tmp).
  • Les ACL (+, getfacl, setfacl) ajoutent des entrées nommées ; à utiliser avec parcimonie.
  • Diagnostiquer un refus : l'identité du processus, le fichier (stat), chaque répertoire (namei -l), puis tester avec sudo -u <compte>. Jamais chmod 777.

Pour aller plus loin

  • man 7 path_resolution, la référence exacte de ce que le noyau vérifie, et man 7 inode pour la structure d'un fichier.
  • Le chapitre File permissions du manuel de GNU Coreutils, qui détaille la notation symbolique, y compris ses cas limites.
  • Le chapitre 9 de The Linux Command Line de William Shotts, pour une présentation pas à pas complémentaire.
  • La leçon suivante, qui montre comment observer les processus et leur identité, et comment les arrêter.
Voir ma constellation →

Sources