Permissions Linux : chmod, chown et umask

1. Présentation

L’erreur Permission denied est l’une des erreurs les plus courantes lors du déploiement d’une application sur Linux. L’application ne peut pas écrire les logs, ne peut pas lire le fichier de configuration, ou le fichier secret est lisible par tous les utilisateurs.

Cet article explique le modèle de permissions des fichiers Linux et comment utiliser trois outils principaux : chmod pour modifier les permissions, chown pour changer le propriétaire, umask pour contrôler les permissions par défaut des nouveaux fichiers. À la fin de l’article, vous saurez lire les permissions de n’importe quel fichier, accorder les permissions au strict nécessaire, et manipuler les permissions POSIX depuis Java.

2. Lire les permissions avec ls -l

Commençons par un répertoire d’exemple :

mkdir -p shop && cd shop
echo "id,total" > order.csv
mkdir reports
ls -l
-rw-r--r-- 1 an dev 9    Oct 13 09:00 order.csv
drwxr-xr-x 2 an dev 4096 Oct 13 09:00 reports

La première colonne a 10 caractères :

  • Le caractère 1 est le type : - pour un fichier ordinaire, d pour un répertoire, l pour un lien symbolique.
  • Les 9 caractères restants se divisent en trois groupes, chacun de 3 caractères : user (propriétaire), group (groupe), other (tous les autres).
  • Chaque groupe comprend r (lecture), w (écriture), x (exécution). Un tiret - signifie qu’il n’y a pas cette permission.

Les troisième et quatrième colonnes sont le propriétaire (an) et le groupe (dev). Ainsi, pour order.csv, an peut lire et écrire, les membres du groupe dev peuvent seulement lire, et les autres utilisateurs peuvent aussi seulement lire.

Pour voir les permissions au format numérique, utilisez stat :

stat -c '%a %A %U:%G %n' order.csv reports
644 -rw-r--r-- an:dev order.csv
755 drwxr-xr-x an:dev reports

Le noyau vérifie les permissions dans cet ordre. Si vous êtes le propriétaire, seul le groupe de permissions du propriétaire s’applique. Si vous n’êtes pas le propriétaire mais vous êtes membre du groupe, seul le groupe de permissions du groupe s’applique. Sinon, les permissions des autres s’appliquent. Les groupes ne s’ajoutent pas. Ainsi, un fichier avec les permissions ---r--r-- empêchera le propriétaire de le lire, même si tous les autres peuvent le faire.

3. Modifier les permissions avec chmod

3.1. Format symbolique

Le format symbolique a la syntaxe [ugoa][+-=][rwxX]. Où u est le propriétaire, g est le groupe, o sont les autres, a signifie tous.

chmod g+w order.csv      # ajouter la permission d'écriture au groupe
chmod o-r order.csv      # retirer la permission de lecture des autres
chmod u=rw,go= order.csv # propriétaire : rw, groupe et autres : rien
stat -c '%a %A' order.csv
600 -rw-------

Les opérateurs + et - n’ajoutent ou ne retirent que les permissions spécifiées, les autres permissions restent inchangées. L’opérateur = réassigne tout le groupe. Le format symbolique est sûr quand vous ne voulez modifier qu’une partie sans connaître les permissions actuelles.

3.2. Format octal

Chaque permission a une valeur : r = 4, w = 2, x = 1. Additionner les valeurs donne un chiffre pour chaque groupe.

Chiffre Permissions Utilisé pour
7 rwx propriétaire d’un répertoire ou script
6 rw- propriétaire d’un fichier de données
5 r-x lire et accéder au répertoire, exécuter le script
4 r-- lecture seulement
0 --- aucune permission

Par exemple, créons un script et un fichier contenant un mot de passe :

printf '#!/bin/bash\necho "Backup orders..."\n' > backup.sh
echo "db.password=s3cret" > secret.properties

chmod 755 backup.sh
chmod 600 secret.properties
./backup.sh
stat -c '%a %n' backup.sh secret.properties
Backup orders...
755 backup.sh
600 secret.properties

755 permet à tout le monde d’exécuter le script mais seul le propriétaire peut le modifier. 600 fait que seul le propriétaire peut lire et modifier le fichier secret. Le format octal assigne toutes les permissions à la fois, c’est approprié quand vous savez exactement l’état souhaité.

3.3. Modifier les permissions récursivement et le X majuscule

Une erreur classique est d’exécuter la commande suivante :

chmod -R 755 shop/

Cette commande assigne la permission d’exécution à tous les fichiers, même order.csv. La bonne façon est d’utiliser X majuscule. Ce symbole n’ajoute x que pour les répertoires, ou pour les fichiers qui ont déjà x dans un groupe :

chmod -R u=rwX,go=rX shop/

Le résultat est que les répertoires ont 755, les fichiers ordinaires 644, et backup.sh conserve 755. Si vous avez besoin d’un meilleur contrôle, vous pouvez séparer avec find :

find shop -type d -exec chmod 750 {} +
find shop -type f -exec chmod 640 {} +

4. Les permissions sur les répertoires diffèrent des fichiers

Pour les répertoires, rwx a une signification différente :

  • r : lister les éléments à l’intérieur (ls).
  • w : créer, supprimer, renommer les éléments à l’intérieur. Cette permission doit être accompagnée de x pour être effective.
  • x : accéder au répertoire (cd) et accéder aux éléments si vous en connaissez le nom.

Une conséquence surprenante : la suppression d’un fichier dépend de la permission du répertoire, pas de celle du fichier.

mkdir inbox && echo "data" > inbox/invoice.txt
chmod 444 inbox/invoice.txt   # fichier en lecture seule
rm -f inbox/invoice.txt       # vous pouvez toujours le supprimer car inbox a la permission w
ls inbox                      # vide

Inversement, si vous retirez la permission x du répertoire, vous ne pouvez pas lire les fichiers dedans même si le fichier a les permissions 644 :

echo "data" > inbox/invoice.txt
chmod 644 inbox/invoice.txt
chmod u-x inbox
cat inbox/invoice.txt
cat: inbox/invoice.txt: Permission denied

Quand l’application signale Permission denied mais que la permission du fichier semble correcte, vérifiez tous les répertoires parents dans le chemin. La commande namei -l /path/to/file liste les permissions de chaque niveau. Avant de continuer, restaurez la permission du répertoire avec chmod u+x inbox.

5. Changer le propriétaire avec chown et chgrp

chown change le propriétaire, le groupe ou les deux :

sudo chown appuser order.csv          # change seulement le propriétaire
sudo chown appuser:dev order.csv      # change le propriétaire et le groupe
sudo chown :dev order.csv             # change seulement le groupe
sudo chown -R appuser:appuser /opt/shop

Quelques points à retenir :

  • Seul root peut changer le propriétaire. Un utilisateur ordinaire ne peut pas “donner” ses fichiers à quelqu’un d’autre.
  • Un utilisateur ordinaire peut changer le groupe d’un fichier qu’il possède, mais seulement vers un groupe dont il est membre. La commande à utiliser est chgrp dev order.csv.
  • chown -R parcourt l’arborescence du répertoire. Quand il rencontre un lien symbolique, par défaut il change le propriétaire du fichier auquel le lien pointe. Ajoutez -h si vous voulez changer le propriétaire du lien lui-même.

Un modèle courant lors du déploiement est d’avoir le service s’exécuter avec un utilisateur dédié, tandis que l’équipe d’exploitation partage un groupe :

sudo chown -R appuser:ops /var/log/shop
sudo chmod -R u=rwX,g=rX,o= /var/log/shop

Avec cette configuration, le service peut écrire les logs, le groupe ops peut les lire, et les autres utilisateurs ne peuvent pas y accéder.

6. Permissions par défaut avec umask

Lors de la création d’un nouveau fichier, les programmes demandent généralement les permissions 666 pour les fichiers et 777 pour les répertoires. Ensuite, le noyau désactive les bits présents dans umask. Le résultat est :

permission effective = permission demandée AND (NOT umask)
umask            # voir la valeur actuelle
umask 027        # changer pour le shell actuel
touch customer.csv && mkdir archive
stat -c '%a %n' customer.csv archive
0022
640 customer.csv
750 archive

Avec umask 027 : les fichiers reçoivent 666 moins les bits 027 donc 640, les répertoires reçoivent 777 moins 027 donc 750. Les autres perdent toutes les permissions, le groupe perd la permission d’écriture.

Note : umask est un masquage de bits, pas une soustraction arithmétique. Avec umask 033, les nouveaux fichiers ont les permissions 644, pas 633. La raison est que le bit x n’est pas dans 666 à l’origine, donc il n’y a rien à désactiver.

Quelques valeurs communes :

umask Fichier Répertoire Quand utiliser
022 644 755 valeur par défaut sur la plupart des distros
027 640 750 serveur, partage uniquement au sein du groupe
077 600 700 données sensibles

La commande umask n’a effet que dans le processus actuel et ses processus enfants. Pour le définir de manière permanente pour un service systemd, déclarez-le dans le fichier de unit :

[Service]
User=appuser
UMask=0027

7. Trois bits spéciaux : setuid, setgid, sticky

En plus des 9 bits de base, Linux a trois bits spéciaux. Ils sont écrits comme un quatrième chiffre au début du format octal :

  • setuid (4) : le programme s’exécute avec les permissions du propriétaire. Par exemple /usr/bin/passwd s’affiche comme -rwsr-xr-x.
  • setgid (2) : quand défini sur un répertoire, les nouveaux fichiers créés à l’intérieur héritent du groupe du répertoire. Ce bit est très utile pour un répertoire de travail partagé.
  • sticky (1) : quand défini sur un répertoire, seul le propriétaire du fichier peut le supprimer. /tmp utilise ce bit et s’affiche comme drwxrwxrwt.

Par exemple, créons un répertoire partagé pour le groupe dev :

sudo mkdir /srv/shared && sudo chown :dev /srv/shared
sudo chmod 2770 /srv/shared
stat -c '%a %A' /srv/shared
2770 drwxrws---

Note : Ne définissez pas setuid sur des scripts ou fichiers que vous avez écrits à moins de vraiment comprendre les risques. Un programme setuid root bugué est une escalade de privilèges classique.

8. Manipuler les permissions POSIX depuis Java

Java NIO supporte les permissions POSIX via PosixFilePermissions. La classe suivante écrit un fichier secret et s’assure que seul le propriétaire peut le lire :

import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.PosixFilePermissions;

public class SecretWriter {

    public Path writeSecret(Path dir, String content) throws IOException {
        Path file = dir.resolve("secret.properties");
        Files.writeString(file, content);
        Files.setPosixFilePermissions(file, PosixFilePermissions.fromString("rw-------"));
        return file;
    }
}

fromString("rw-------") convertit la chaîne symbolique en Set<PosixFilePermission>. Ensuite setPosixFilePermissions fonctionne comme la commande chmod 600.

Vous pourriez être tenté de passer PosixFilePermissions.asFileAttribute(...) à Files.createFile. Cette approche est toujours soumise à umask, tout comme l’appel système open(). Avec les fichiers sensibles, appelez setPosixFilePermissions après la création pour garantir le résultat. Une approche encore plus sûre est de créer le fichier avec des permissions strictes dès le départ, afin qu’il n’y ait pas de fenêtre de temps où d’autres pourraient le lire.

Test avec JUnit 5 et AssertJ :

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import java.nio.file.*;
import java.nio.file.attribute.PosixFilePermissions;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assumptions.assumeTrue;

class SecretWriterTest {

    @TempDir
    Path tempDir;

    @Test
    void givenSecret_whenWritten_thenOnlyOwnerCanReadAndWrite() throws Exception {
        assumeTrue(FileSystems.getDefault().supportedFileAttributeViews().contains("posix"));

        Path file = new SecretWriter().writeSecret(tempDir, "db.password=s3cret");

        String perms = PosixFilePermissions.toString(Files.getPosixFilePermissions(file));
        assertThat(perms).isEqualTo("rw-------");
        assertThat(Files.readString(file)).contains("db.password");
    }
}

assumeTrue ignorera le test sur Windows, où la vue d’attributs posix n’existe pas. Si vous omettez cette ligne, le test lèvera UnsupportedOperationException.

9. Erreurs courantes

  • Utiliser chmod 777 pour une “correction rapide”. Cette approche ouvre la permission d’écriture à tous les utilisateurs de la machine. Identifiez quel utilisateur exécute le processus (ps -o user= -p <pid>), puis n’accordez les permissions qu’à cet utilisateur ou à ce groupe.
  • Exécuter chmod -R 755 sur l’arborescence. Cette commande rend tous les fichiers exécutables. Utilisez X majuscule ou find comme dans la section 3.3.
  • Vérifier uniquement la permission du fichier. Vous devez aussi vérifier les répertoires parents avec namei -l.
  • Exécuter sudo puis oublier de changer le propriétaire. Les fichiers créés par sudo appartiennent à root, donc une application exécutée par un utilisateur ordinaire ne pourra pas les modifier. Exécutez chown immédiatement après.
  • Monter un volume dans Docker. L’UID à l’intérieur du conteneur peut être différent de l’UID sur l’hôte. Les permissions sont vérifiées par le noyau selon le numéro UID, pas selon le nom d’utilisateur.
  • SSH refuse la clé. SSH ignore ~/.ssh/authorized_keys ou la clé privée si les permissions sont trop larges. Le répertoire ~/.ssh a besoin des permissions 700, la clé privée des permissions 600.

10. Conclusion

Le modèle de permissions Linux comprend trois groupes (user, group, other) et trois permissions (r, w, x). Ces permissions ont des significations différentes entre les fichiers et les répertoires.

Vous utilisez chmod pour modifier les permissions : le format symbolique quand vous ne devez modifier qu’une partie, le format numérique quand vous voulez tout assigner. Vous utilisez chown et chgrp pour assigner les fichiers au bon utilisateur et groupe. Vous utilisez umask pour que les nouveaux fichiers soient créés avec des permissions sûres dès le départ.

Le principe qui sous-tend tout cela est d’accorder le moins de permissions possible. Exécutez les services avec un utilisateur dédié, partagez via le groupe, et laissez les autres au niveau 0 pour tout ce qui est sensible.

linuxchmodchownumasksécurité