SSD Nodes Learn 🎉 VPS dès $5.50/mois
Guides Matt ConnorPar Matt Connor

umask Linux : comprendre les permissions par défaut

Découvrez pourquoi root et votre utilisateur diffèrent : affichez l’umask, créez un fichier et un répertoire, puis vérifiez leurs modes avec ls.

Ce que fait umask sous Linux

umask est un nombre associé à chaque processus Linux. Il détermine le mode de chaque fichier et répertoire créé par ce processus. Lors de la création, un programme demande au kernel un ensemble de permissions. Le kernel efface chaque bit indiqué par le masque, puis applique les bits restants. umask n’accorde jamais d’accès. Il retire uniquement des bits de ce que le programme créateur a demandé.

Cette valeur n’est pas une propriété de votre distribution. Elle dépend du compte utilisé et de la manière dont le shell a été démarré. Ces deux éléments peuvent différer sur une même machine, au même moment, avec une image standard. La première étape ne consiste donc pas à chercher la valeur dans un manuel. Il faut la mesurer sur la machine concernée.

Afficher l’umask dans le shell courant

umask
umask -S

La première commande affiche le masque en octal. La seconde affiche le même masque sous forme des permissions qu’il autorise, dans la notation symbolique acceptée par chmod. Laissez les deux lignes affichées. Tout ce qui suit compare le résultat que votre shell vient d’afficher.

umask est un builtin du shell, et non un programme présent sur le disque. Vérifiez-le avec type umask. Cette distinction est importante, car un builtin modifie directement le processus du shell. Un programme distinct ne pourrait modifier que son propre processus, puis se terminer en emportant cette modification avec lui.

Créer un fichier et un répertoire, puis relire leurs modes

cd "$(mktemp -d)"
touch probe.file
mkdir probe.dir
stat -c '%a %A %n' probe.file probe.dir

%a affiche le mode en octal et %A affiche le même mode au format drwxr-xr-x utilisé par ls -l. Comparez les deux lignes avec le masque affiché juste avant. Chaque bit activé dans le masque est absent des modes, car un masque ne fait que désactiver ces bits. Si la colonne %A n’est pas encore évidente à lire, commencez par bien comprendre la chaîne d’autorisation drwxr-xr-x.

Le fichier et le répertoire diffèrent, et le masque n’est pas à l’origine de cette différence. touch demande les droits de lecture et d’écriture pour le propriétaire, le groupe et les autres utilisateurs. mkdir demande les droits de lecture, d’écriture et d’exécution pour les trois catégories. Le même masque est appliqué à deux demandes différentes. Ainsi, un fichier créé par touch n’est jamais exécutable, quelle que soit la valeur du masque : le bit d’exécution n’a jamais été demandé, et un masque ne peut pas réactiver un bit.

( umask a=rwx; touch open.file; stat -c '%a %A %n' open.file )

Les parenthèses exécutent les commandes dans un sous-shell, donc la modification disparaît avec lui. Le masque ne demande maintenant la désactivation d’aucun bit, et stat n’affiche toujours aucun bit d’exécution sur le fichier. Exécutez de nouveau umask ensuite : votre valeur d’origine est rétablie. Cela montre que ce paramètre existe dans un processus et est hérité par ses processus enfants, au lieu d’être enregistré sur le disque.

Ce sont les répertoires qui révèlent l’absence du bit d’exécution.

( umask a=rw; mkdir noexec.dir; cd noexec.dir )

Avec un compte utilisateur normal, cd échoue avec bash: cd: noexec.dir: Permission denied, car le masque a désactivé le bit d’exécution demandé par mkdir, et un répertoire dépourvu du bit d’exécution ne peut pas être parcouru. root ne passe pas par ce contrôle. Ce comportement est donc visible uniquement avec un compte normal.

Pourquoi root et votre propre utilisateur affichent un umask différent

Effectuez la même mesure avec un autre compte, en le démarrant d’une autre manière, puis lisez les deux sorties côte à côte.

umask
sudo -i umask

sudo -i démarre le login shell de root et exécute le builtin à l’intérieur de celui-ci. Il s’agit donc d’un autre compte, qui passe par un autre chemin d’initialisation. Sur les images serveur Ubuntu et Debian standard, les deux lignes peuvent afficher des valeurs différentes. Les deux lignes sont correctes. Chacune indique le résultat produit par son propre chemin d’initialisation. La suite de cet article explique quelle partie du système l’a produit.

Quel fichier de votre image a fixé la valeur

grep -nE '^(UMASK|USERGROUPS_ENAB)' /etc/login.defs
grep -rn pam_umask /etc/pam.d/
grep -rn umask /etc/profile /etc/profile.d/ /etc/bash.bashrc ~/.profile ~/.bashrc 2>/dev/null || echo 'no umask line in the shell startup files'

Si le premier grep n’affiche rien, exécutez-le de nouveau sans l’ancre ^ : la ligne est peut-être commentée. Une ligne commentée sert de documentation, pas de configuration. Le troisième grep est celui qui surprend le plus souvent. Sur Debian et Ubuntu, le fichier /etc/profile fourni avec le système renvoie généralement vers PAM au lieu de définir lui-même un masque. Le fichier que vous pensiez responsable ne l’est donc souvent pas. grep renvoie un code différent de zéro lorsqu’il ne trouve aucune correspondance. C’est pourquoi cette ligne se termine par || echo : sur une image où aucun fichier de démarrage ne mentionne de masque, vous obtenez le message au lieu du silence, et ce message constitue le résultat.

/etc/login.defs annonce une valeur et PAM en applique une autre

La ligne UMASK de /etc/login.defs contient la valeur citée par la plupart des guides. Ni le kernel ni le shell ne lisent ce fichier. Il est lu par pam_umask, un module PAM (pluggable authentication modules) exécuté lors de la création d’une session. pam_umask utilise la première valeur trouvée : une entrée umask= dans le champ GECOS de l’utilisateur, puis un argument umask= écrit directement sur la ligne pam_umask.so, puis UMASK provenant de /etc/login.defs. Les distributions modifient ce module. Exécutez donc man pam_umask sur votre propre image et consultez l’ordre affiché.

C’est ainsi que /etc/login.defs peut annoncer une valeur alors que votre session en utilise une autre, sans afficher d’avertissement dans l’un ou l’autre cas. Les commandes grep indiquent dans quel cas vous vous trouvez. Si la ligne pam_umask.so contient son propre argument umask=, la valeur de la ligne login.defs est ignorée.

USERGROUPS_ENAB et l’exception root

id -un
id -gn

Si ces deux commandes affichent le même nom, vous utilisez un user private group : le compte a été créé avec son propre groupe, nommé d’après le compte. pam_umask applique un comportement usergroups, contrôlé par USERGROUPS_ENAB dans /etc/login.defs. Lorsqu’il est activé, que le compte n’est pas root et que le nom du groupe primaire correspond au nom de l’utilisateur, le module copie le chiffre du propriétaire du masque dans le chiffre du groupe. La session se termine alors avec un masque qui laisse les bits du groupe ouverts pour tout ce que ce compte crée. root est exclu par le module lui-même. Cette exclusion est la raison la plus fréquente pour laquelle deux shells sur un même serveur affichent des masques différents.

Cette règle repose sur le principe qu’un groupe privé ne contient qu’un seul membre. Les droits d’écriture du groupe sont donc équivalents à ceux du propriétaire, et rien de plus. Cela reste vrai jusqu’à ce qu’un second membre soit ajouté au groupe. À partir de ce moment, ce nouveau membre peut écrire dans tous les fichiers que le compte a créés auparavant, sans qu’aucune commande n’ait été exécutée sur ces fichiers pour lui accorder ce droit. Attribuez à chaque service son propre compte utilisateur avec le principe du moindre privilège. Le groupe reste ainsi volontairement limité à un seul membre.

Shell de connexion, shell sans connexion et shell non interactif

umask
bash -lc 'umask'
bash -c 'umask'

PAM s’exécute lorsqu’une session est créée : login sur la console, sshd, su, sudo -i. Il ne s’exécute pas lorsqu’un shell en lance un autre. bash -l est un shell de connexion ; il lit donc /etc/profile et ~/.profile, mais n’appelle jamais pam_umask, car aucune session n’a été créée. bash -c ne lit aucun de ces fichiers et hérite du masque du processus qui l’a lancé. Une tâche cron, un hook Git et un programme lancé par un service manager relèvent tous de ce dernier cas : leur masque correspond à celui de leur processus parent.

C’est pourquoi « Je l’ai défini dans /etc/profile, mais le service crée toujours des fichiers avec les mauvais droits » est un signalement si fréquent. Le service n’a jamais lu ce fichier.

Où le définir pour qu’il soit conservé

Définissez le masque à l’endroit où la charge de travail démarre réellement, car chaque chemin de démarrage lit un fichier différent.

  1. Pour les comptes qui se connectent : UMASK dans /etc/login.defs, appliqué par pam_umask à chaque session de la machine. Ce réglage s’applique à toute la machine et modifie donc tous les comptes en une seule fois.
  2. Pour un seul compte : l’argument umask= sur la ligne pam_umask.so s’applique également à toute la machine. Une valeur propre à l’utilisateur doit donc être placée dans le champ GECOS de cet utilisateur, ou dans ~/.profile pour les shells de connexion et dans ~/.bashrc pour les shells interactifs.
  3. Pour un daemon sous systemd : UMask= dans la section [Service] de l’unité. Une unité est démarrée par le service manager. /etc/profile n’est donc jamais lu et pam_umask ne s’exécute jamais. Le fichier d’unité est le seul de ces fichiers qui atteint le daemon.
  4. Pour un script démarré par cron ou par un hook : un umask explicite sur la première ligne, avant que le script ne crée quoi que ce soit.
[Service]
UMask=<the octal mask you chose>

Vérifiez ensuite le résultat depuis un nouveau démarrage du même chemin, et jamais depuis le shell dans lequel vous avez modifié le fichier. Votre shell actuel possède déjà son masque, et la modification d’un fichier de configuration ne se répercute pas sur un processus en cours d’exécution.

bash -lc 'umask'
sudo -i umask

Pourquoi chmod ensuite ne corrige pas le problème

chmod corrige les fichiers existants. Le masque détermine le mode des fichiers qui n’existent pas encore. Exécutez chmod -R sur un répertoire : le fichier suivant créé par le service reprendra l’ancien mode, car ce mode vient du processus qui crée le fichier et le répertoire ne le modifie pas.

Il existe également une fenêtre de risque. Entre la création du fichier et l’exécution de chmod, le fichier reste sur le disque avec un mode trop permissif. Tout processus pouvant lire le répertoire peut alors l’ouvrir. Pour une clé privée ou une archive de sauvegarde, cette fenêtre crée précisément le risque que vous cherchiez à supprimer.

Définissez plutôt le mode lors de la création. install -m u=rw,go= newfile /etc/app/newfile écrit la destination avec un mode explicite, et mkdir -m fait de même pour un répertoire. Tous deux utilisent le mode indiqué et ignorent le masque. ssh-keygen définit le mode de la clé privée qu’il écrit. C’est pourquoi ce fichier est souvent correct sur une machine où les autres ne le sont pas.

SSH est le cas le plus fréquent. Un ~/.ssh créé avec un simple mkdir, ou un authorized_keys auquel cat >> ajoute du contenu, utilise le masque de votre shell. Lorsque StrictModes est activé, sshd refuse de lire un fichier de clé situé dans un répertoire accessible en écriture par le groupe. Le client affiche Permission denied (publickey), tandis que le serveur enregistre la cause réelle dans /var/log/auth.log :

Authentication refused: bad ownership or modes for directory /home/deploy/.ssh

Ce contrôle est volontaire. Le renforcement de la sécurité de SSH sur un VPS en dépend. Mesurez le masque sur un nouveau serveur avant de créer les comptes qui l’utiliseront, en même temps que les dix premières minutes sur un nouveau VPS. Le mode de chaque fichier créé par ces comptes sera ainsi défini à l’avance.

Les copies et les archives ignorent le masque

cp -p et rsync -a restaurent le mode enregistré sur le fichier source. Le masque n’intervient donc pas dans le résultat. tar fait de même lors de l’extraction en tant que root ou en tant qu’utilisateur ordinaire avec -p. Un fichier restauré depuis une sauvegarde conserve le mode qu’il avait au moment de la sauvegarde. Vérifiez ce point avant de conclure qu’un masque correct est ignoré : pour les données restaurées, il n’a jamais été consulté.

FAQ

Pourquoi mon cron crée-t-il des fichiers avec un mode différent de celui de ma session SSH ?

Un cron n’est pas une session de connexion. pam_umask ne s’y exécute donc pas et ne lit ni /etc/profile ni ~/.profile. Il hérite du masque du processus qui l’a lancé. Ajoutez un umask explicite à la première ligne du script, avant toute création, puis affichez une fois le masque depuis le job. Vous verrez ainsi le masque réellement appliqué à ce job, et non celui de votre propre shell.

Pourquoi /etc/login.defs indique-t-il une valeur différente de celle affichée par mon shell ?

UMASK dans /etc/login.defs n’est que la dernière valeur de repli utilisée par pam_umask. Le module privilégie une entrée umask= dans le champ GECOS de l’utilisateur, puis un argument umask= sur la ligne pam_umask.so dans /etc/pam.d/. Le comportement usergroups, activé par USERGROUPS_ENAB, réécrit ensuite le chiffre correspondant au groupe pour tout compte autre que root dont le groupe principal porte le même nom. Exécutez grep -rn pam_umask /etc/pam.d/ et id -un; id -gn pour déterminer lequel de ces paramètres s’applique à votre compte.

Un umask peut-il rendre un fichier exécutable ?

Non. Un masque peut seulement supprimer des bits parmi ceux demandés par le programme qui crée le fichier. touch ne demande jamais le bit d’exécution. Aucun masque ne peut donc produire un fichier exécutable. Vérifiez-le dans un répertoire temporaire avec ( umask a=rwx; touch f; stat -c '%a %A' f ). Pour obtenir un bit d’exécution, vous devez utiliser chmod ou un programme tel que install -m, qui le demande lors de la création.

Où définir l’umask d’un service systemd ?

Dans l’unité, avec UMask= dans la section [Service]. Un service est lancé par le service manager, et non par une session de connexion. Les fichiers de démarrage du shell ne sont donc jamais lus et pam_umask ne s’exécute pas. Après systemctl daemon-reload et le redémarrage de l’unité, vérifiez le résultat depuis l’extérieur : faites créer un fichier par le service, puis lisez le résultat avec stat -c '%a %n'.

Un masque par défaut autorisant l’écriture au groupe est-il sûr ?

Il est sûr tant que le groupe ne compte qu’un seul membre. C’est l’hypothèse du modèle user private group. Ajoutez un second compte à ce groupe : tous les fichiers créés par le premier compte deviennent immédiatement accessibles en écriture au nouveau membre, sans qu’aucune commande ne soit exécutée sur ces fichiers. Exécutez id -un et id -gn : si le même nom s’affiche, vous utilisez un groupe privé. Si vous partagez un groupe entre plusieurs comptes, définissez un masque qui supprime le bit d’écriture du groupe. Créez ensuite un fichier et lisez stat -c '%a %n' pour vérifier que la modification est effective.

#umask#permissions#pam#login-defs#linux