umask Linux: come funzionano i permessi predefiniti
Scopri come umask modifica i permessi di file e directory: stampa il valore, crea esempi e verifica perché root e il tuo utente possono differire.
Cosa fa umask su Linux
umask è un numero associato a ogni processo Linux e determina la modalità di ogni file e directory creati da quel processo. Un programma richiede al kernel un insieme di permessi al momento della creazione. Il kernel azzera ogni bit indicato dalla maschera e applica i bit rimanenti. umask non concede mai l'accesso. Rimuove soltanto alcuni bit dai permessi richiesti dal programma che crea l'oggetto.
Il valore non è una proprietà della distribuzione. Dipende dall'account utilizzato e da come è stata avviata la shell. Su una stessa macchina, nello stesso momento e con un'immagine standard, queste due condizioni possono produrre valori diversi. Il primo passo, quindi, non consiste nel cercare una risposta in un manuale. Consiste nel rilevare il valore direttamente sul sistema in uso.
Stampa umask nella shell corrente
umask
umask -SLa prima forma stampa la maschera in ottale. La seconda stampa la stessa maschera come permessi consentiti, nel formato simbolico accettato da chmod. Mantieni entrambe le righe visualizzate. Tutto ciò che segue confronta i valori con quelli appena stampati dalla shell.
umask è un builtin della shell, non un programma presente su disco. Verificalo con type umask. Questo è importante perché un builtin modifica direttamente il processo della shell. Un programma separato potrebbe modificare soltanto il proprio processo, quindi terminare e perdere la modifica.
Creare un file e una directory, quindi leggere nuovamente i relativi mode
cd "$(mktemp -d)"
touch probe.file
mkdir probe.dir
stat -c '%a %A %n' probe.file probe.dir%a stampa il mode in formato ottale e %A stampa lo stesso mode nel formato drwxr-xr-x usato da ls -l. Confrontate entrambe le righe con la mask stampata poco prima. Ogni bit impostato nella mask manca dai mode, perché l'unica operazione eseguita dalla mask consiste nel cancellare quei bit. Se la colonna %A non è ancora immediata da leggere, la stringa di permessi drwxr-xr-x è il primo elemento da comprendere.
Il file e la directory differiscono tra loro, ma la mask non è la causa di questa differenza. touch chiede al kernel i permessi di lettura e scrittura per owner, group e other. mkdir chiede i permessi di lettura, scrittura ed esecuzione per tutti e tre. La stessa mask viene sottratta a due richieste diverse. Di conseguenza, un file creato da touch non è mai eseguibile, indipendentemente dal contenuto della mask: il bit di esecuzione non è mai stato richiesto e una mask non può aggiungere nuovamente un bit.
( umask a=rwx; touch open.file; stat -c '%a %A %n' open.file )Le parentesi eseguono i comandi in una subshell, quindi la modifica termina insieme a essa. La mask ora non richiede la cancellazione di alcun bit e stat continua a non segnalare il bit di esecuzione sul file. Eseguite nuovamente umask in seguito: il valore originale è tornato. Questo dimostra che l'impostazione vive all'interno di un processo e viene ereditata dai processi figli, invece di essere memorizzata su disco.
Le directory sono il contesto in cui l'assenza del bit di esecuzione è evidente.
( umask a=rw; mkdir noexec.dir; cd noexec.dir )Come utente normale, cd fallisce con bash: cd: noexec.dir: Permission denied, perché la mask ha cancellato il bit di esecuzione richiesto da mkdir e una directory priva del bit di esecuzione non può essere attraversata. root ignora questo controllo, quindi il problema è visibile soltanto con un account normale.
Perché root e il proprio utente vedono un umask diverso
Eseguire la stessa misurazione con un altro account, avviato in un altro modo, quindi leggere i due output affiancati.
umask
sudo -i umasksudo -i avvia la shell di login di root ed esegue al suo interno il comando builtin. Si tratta quindi di un account diverso, che segue un percorso di avvio diverso. Nelle immagini server standard di Ubuntu e Debian, le due righe possono restituire valori diversi. Entrambe le righe sono corrette. Ciascuna mostra il risultato prodotto dal proprio percorso di avvio. Il resto di questo articolo analizza quale componente del sistema lo ha prodotto.
Quale file nell’immagine determinava il valore
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'Se il primo comando grep non restituisce nulla, eseguilo di nuovo senza l’ancora ^: la riga potrebbe essere commentata, e una riga commentata è documentazione, non configurazione. Il terzo comando grep è quello che sorprende. Su Debian e Ubuntu, il file distribuito /etc/profile rimanda principalmente a PAM invece di impostare direttamente una mask, quindi spesso il file che ritenevi responsabile non lo è. grep restituisce un codice diverso da zero quando non trova corrispondenze, per questo la riga termina con || echo: in un’immagine in cui nessun file di avvio menziona una mask, viene visualizzato il messaggio invece del silenzio, e il messaggio è il risultato dell’analisi.
/etc/login.defs pubblicizza un valore e PAM ne applica un altro
La riga UMASK in /etc/login.defs contiene il valore citato dalla maggior parte delle guide. Né il kernel né la shell leggono quel file. Lo legge pam_umask, un modulo PAM (pluggable authentication modules) eseguito durante la creazione di una sessione. pam_umask usa il primo valore disponibile: una voce umask= nel campo GECOS dell'utente, quindi un argomento umask= scritto direttamente nella riga pam_umask.so, infine UMASK da /etc/login.defs. Le distribuzioni applicano patch a questo modulo, quindi esegui man pam_umask sulla tua immagine e verifica l'ordine stampato.
Per questo /etc/login.defs può pubblicizzare un valore mentre la sessione ne utilizza un altro, senza visualizzare avvisi in nessuno dei due casi. I comandi grep indicano quale situazione si applica. Se la riga pam_umask.so contiene un proprio argomento umask=, la riga di login.defs non viene applicata.
USERGROUPS_ENAB e l'eccezione per root
id -un
id -gnSe i due comandi stampano lo stesso nome, hai un gruppo privato dell'utente: l'account è stato creato con un gruppo dedicato, denominato come l'account. pam_umask applica un comportamento basato sui gruppi dell'utente, controllato da USERGROUPS_ENAB in /etc/login.defs. Quando questa impostazione è attiva, l'account non è root e il nome del gruppo primario corrisponde al nome dell'utente, il modulo copia la cifra relativa al proprietario nella cifra relativa al gruppo. La sessione termina quindi con una maschera che lascia aperti i bit del gruppo per tutto ciò che l'account crea. root è escluso direttamente dal modulo; questa esclusione è il motivo più comune per cui due shell sullo stesso server stampano maschere diverse.
La logica di questa regola è che un gruppo privato contiene esattamente un membro. Pertanto, la scrittura per il gruppo equivale alla scrittura per il proprietario e non concede altro. Questo vale finché non viene aggiunto un secondo membro al gruppo. Da quel momento, il nuovo membro può scrivere ogni file creato in precedenza dall'account, senza che sia stato eseguito alcun comando per modificare i permessi di quei file. Assegna a ogni servizio un proprio account utente con privilegi minimi e il gruppo resterà intenzionalmente composto da un solo membro.
Shell di login, shell non di login e shell non interattiva
umask
bash -lc 'umask'
bash -c 'umask'PAM viene eseguito quando viene creata una sessione: login sulla console, sshd, su, sudo -i. Non viene eseguito quando una shell avvia un'altra shell. bash -l è una shell di login, quindi legge /etc/profile e ~/.profile, ma non richiama mai pam_umask, perché non è stata creata alcuna sessione. bash -c non legge nessuno dei due file ed eredita la maschera del processo che l'ha avviata. Un processo cron, un hook Git e un programma avviato da un service manager rientrano tutti nell'ultimo caso, quindi la loro maschera è quella del processo padre.
Per questo è così comune ricevere segnalazioni del tipo: «Ho impostato il valore in /etc/profile, ma il servizio continua a creare file con modalità errate». Il servizio non ha mai letto quel file.
Dove impostarlo perché resti applicato
Impostare la maschera nel punto in cui il workload viene effettivamente avviato, perché ogni percorso di avvio legge un file diverso.
- Per gli account che eseguono l'accesso:
UMASKin/etc/login.defs, applicato da pam_umask a ogni sessione del sistema. È un'impostazione valida per l'intera macchina, quindi modifica contemporaneamente tutti gli account. - Per un singolo account: l'argomento
umask=nella rigapam_umask.soè anch'esso valido per l'intera macchina, quindi un valore specifico per l'utente deve trovarsi nel campo GECOS dell'utente oppure in~/.profileper le shell di login e in~/.bashrcper quelle interattive. - Per un daemon gestito da systemd:
UMask=nella sezione[Service]dell'unità. Un'unità viene avviata dal service manager, quindi/etc/profilenon viene mai letto e pam_umask non viene eseguito. Tra queste opzioni, il file dell'unità è l'unico che viene applicato al daemon. - Per uno script avviato da cron o da un hook: un
umaskesplicito nella prima riga, prima che lo script crei qualsiasi elemento.
[Service]
UMask=<the octal mask you chose>Verificare quindi il risultato avviando nuovamente lo stesso percorso e non dalla shell in cui è stato modificato il file. La shell corrente ha già memorizzato la propria maschera e la modifica di un file di configurazione non modifica retroattivamente un processo in esecuzione.
bash -lc 'umask'
sudo -i umaskPerché eseguire chmod dopo non risolve il problema allo stesso modo
chmod corregge i file già esistenti. La mask determina i permessi dei file che non esistono ancora. Esegui chmod -R su una directory e il file successivo scritto dal servizio verrà nuovamente creato con i vecchi permessi, perché questi dipendono dal processo che lo crea e la directory non li modifica.
Esiste anche una finestra temporale. Tra la creazione del file e l'esecuzione di chmod, il file resta sul disco con permessi più permissivi e qualsiasi processo in grado di leggere la directory può aprirlo. Per una chiave privata o un archivio di backup, questa finestra crea proprio il rischio che si voleva eliminare.
Imposta invece i permessi al momento della creazione. install -m u=rw,go= newfile /etc/app/newfile scrive la destinazione con permessi espliciti e mkdir -m fa lo stesso per una directory. Entrambi applicano i permessi specificati ignorando la mask. ssh-keygen imposta i permessi sulla chiave privata che scrive. Per questo quel file è spesso corretto anche su un sistema in cui tutto il resto non lo è.
La vittima più comune è SSH. Una ~/.ssh creata con un semplice mkdir, oppure una authorized_keys a cui viene aggiunto contenuto con cat >>, eredita la mask della shell. Con StrictModes attivo, sshd rifiuta di leggere un file di chiavi da una directory scrivibile dal gruppo. Il client visualizza Permission denied (publickey), mentre il /var/log/auth.log del server registra la causa effettiva:
Authentication refused: bad ownership or modes for directory /home/deploy/.sshIl controllo è intenzionale e l'hardening di SSH su un VPS dipende dal fatto che venga mantenuto. Misura la mask su un nuovo server prima di creare gli account che la utilizzeranno, insieme a i primi dieci minuti su un nuovo VPS, così i permessi di ogni file scritto da tali account sono definiti in anticipo.
Copie e archivi ignorano la maschera
cp -p e rsync -a ripristinano la modalità registrata nel file sorgente, quindi la maschera non influisce sul risultato. tar si comporta allo stesso modo quando estrae come root o come utente normale con -p. Un file ripristinato da un backup mantiene la modalità che aveva al momento della creazione del backup. Verificalo prima di concludere che una maschera corretta venga ignorata: per i dati ripristinati non è mai stata consultata.
FAQ
Perché il mio cron job crea file con una modalità diversa da quella della sessione SSH?
Un cron job non è una sessione di login, quindi pam_umask non viene mai eseguito e non legge né /etc/profile né ~/.profile. Eredita la maschera del processo che lo ha avviato. Inserisci un umask esplicito nella prima riga dello script, prima che crei qualsiasi elemento, e stampa una volta la maschera dall'interno del job. In questo modo puoi verificare la maschera effettiva del job, non quella della tua shell.
Perché /etc/login.defs indica un valore diverso da quello stampato dalla shell?
UMASK in /etc/login.defs è soltanto l'ultimo valore di fallback per pam_umask. Il modulo preferisce una voce umask= nel campo GECOS dell'utente, quindi un argomento umask= nella riga pam_umask.so di /etc/pam.d/. Il comportamento usergroups, attivato da USERGROUPS_ENAB, modifica quindi la cifra relativa al gruppo per qualsiasi account non-root il cui gruppo primario abbia lo stesso nome dell'account. Esegui grep -rn pam_umask /etc/pam.d/ e id -un; id -gn per verificare quale di queste impostazioni si applica al tuo account.
Una umask può rendere eseguibile un file?
No. Una maschera può soltanto rimuovere bit da quelli richiesti dal programma che crea il file. touch non richiede mai il bit di esecuzione, quindi nessuna maschera può produrre un file eseguibile. Verificalo in una directory temporanea con ( umask a=rwx; touch f; stat -c '%a %A' f ). Per impostare un bit di esecuzione servono chmod oppure un programma come install -m che lo richieda al momento della creazione.
Dove imposto la umask per un servizio systemd?
Nell'unità, con UMask= nella sezione [Service]. Un servizio viene avviato dal service manager e non da una sessione di login, quindi i file di avvio della shell non vengono mai letti e pam_umask non viene mai eseguito. Dopo systemctl daemon-reload e il riavvio dell'unità, verifica dall'esterno: fai creare un file al servizio, quindi leggi il risultato con stat -c '%a %n'.
Una maschera predefinita che consente la scrittura al gruppo è sicura?
È sicura finché il gruppo ha esattamente un membro, secondo l'ipotesi alla base dello schema user private group. Se aggiungi un secondo account al gruppo, ogni file creato dal primo account diventa immediatamente scrivibile dal nuovo membro, senza eseguire comandi su quei file. Esegui id -un e id -gn: se stampano lo stesso nome, stai usando un gruppo privato. Se condividi un gruppo tra più account, imposta una maschera che rimuova il bit di scrittura del gruppo, quindi crea un file e leggi stat -c '%a %n' per verificare che la modifica sia effettiva.