umask en Linux: de dónde salen los permisos iniciales
Comprueba el error exacto de los permisos: muestra umask, crea un archivo y un directorio, y compara sus modos para entender la diferencia entre root y tu usuario.
Qué hace umask en Linux
umask es un número que lleva cada proceso en Linux. Determina el modo de cada archivo y directorio que crea ese proceso. Al crear un archivo o directorio, un programa solicita al kernel un conjunto de permisos. El kernel borra todos los bits que indica la máscara y aplica los que quedan. umask nunca concede acceso. Sólo elimina bits de los permisos solicitados por el programa que crea el recurso.
El valor no es una propiedad de la distribución. Depende de la cuenta utilizada y de cómo se inició el shell. Esas dos respuestas pueden ser diferentes en una misma máquina y en el mismo momento, incluso en una imagen estándar. Por eso, el primer paso no es buscar el valor en un manual. Es medirlo en el sistema que tiene delante.
Muestre el umask en la shell actual
umask
umask -SLa primera forma muestra la máscara en octal. La segunda muestra la misma máscara como los permisos que permite, en la forma simbólica que acepta chmod. Mantenga ambas líneas en pantalla. Todo lo que sigue compara los resultados con lo que acaba de mostrar la shell.
umask es un builtin de la shell, no un programa del sistema de archivos. Confírmelo con type umask. Esto es importante porque un builtin modifica el proceso de la shell. Un programa independiente sólo podría modificar su propio proceso. Después terminaría y se llevaría consigo ese cambio.
Cree un archivo y un directorio y vuelva a leer sus modos
cd "$(mktemp -d)"
touch probe.file
mkdir probe.dir
stat -c '%a %A %n' probe.file probe.dir%a muestra el modo en octal y %A muestra el mismo modo en el formato drwxr-xr-x que usa ls -l. Compare ambas líneas con la máscara que mostró hace un momento. Cada bit establecido en la máscara falta en los modos, porque una máscara sólo sirve para borrar esos bits. Si la columna %A todavía no resulta evidente, la cadena de permisos drwxr-xr-x es lo primero que debe entender.
El archivo y el directorio son diferentes entre sí, y la máscara no es la causa de esa diferencia. touch solicita permisos de lectura y escritura para el propietario, el grupo y los demás usuarios. mkdir solicita permisos de lectura, escritura y ejecución para los tres. La misma máscara se resta de dos solicitudes diferentes. Por eso, un archivo creado por touch nunca es ejecutable, independientemente del contenido de la máscara: el bit de ejecución nunca se solicitó y una máscara no puede volver a añadir un bit.
( umask a=rwx; touch open.file; stat -c '%a %A %n' open.file )Los paréntesis ejecutan los comandos en un subshell, por lo que el cambio desaparece al salir de él. La máscara ahora no solicita borrar nada, y stat sigue mostrando que el archivo no tiene el bit de ejecución. Ejecute umask de nuevo después y recuperará el valor original. Esto demuestra que la configuración vive dentro de un proceso y se hereda a los procesos hijos, en lugar de almacenarse en el disco.
Los directorios son donde se nota la ausencia del bit de ejecución.
( umask a=rw; mkdir noexec.dir; cd noexec.dir )Como usuario normal, cd falla con bash: cd: noexec.dir: Permission denied porque la máscara borró el bit de ejecución que solicitó mkdir y un directorio sin el bit de ejecución no se puede abrir. root omite esa comprobación, por lo que este efecto sólo se observa con una cuenta normal.
Por qué root y tu usuario tienen un umask diferente
Ejecuta la misma medición con otra cuenta, iniciada de otra forma, y lee las dos salidas una junto a la otra.
umask
sudo -i umasksudo -i inicia el shell de inicio de sesión de root y ejecuta el builtin dentro de él, por lo que se trata de una cuenta diferente que llega mediante otra ruta de inicio. En las imágenes estándar de servidores Ubuntu y Debian, las dos líneas pueden mostrar valores diferentes. Las dos líneas son correctas. Cada una muestra el resultado de su propia ruta de inicio, y el resto de este artículo explica qué parte del sistema lo produjo.
Qué archivo de la imagen decidió el valor
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 el primer grep no muestra nada, ejecútelo de nuevo sin el anclaje ^: es posible que la línea esté comentada, y una línea comentada es documentación, no configuración. El tercer grep es el que suele sorprender. En Debian y Ubuntu, el /etc/profile incluido normalmente apunta a PAM en lugar de establecer una máscara por sí mismo, por lo que el archivo que suponía responsable a menudo no lo es. grep devuelve un código distinto de cero cuando no encuentra coincidencias. Por eso esa línea termina en || echo: en una imagen cuyos archivos de inicio no mencionan ninguna máscara, se muestra el mensaje en lugar de no mostrar nada, y ese mensaje es el hallazgo.
/etc/login.defs anuncia un valor y PAM aplica otro
La línea UMASK de /etc/login.defs contiene el valor que citan la mayoría de las guías. Ni el kernel ni el shell leen ese archivo. Lo lee pam_umask, un módulo PAM (módulos de autenticación conectables) que se ejecuta cuando se crea una sesión. pam_umask toma el primer valor que encuentra: una entrada umask= en el campo GECOS del usuario, después un argumento umask= escrito en la propia línea pam_umask.so y, por último, UMASK de /etc/login.defs. Las distribuciones modifican este módulo, por lo que debe ejecutar man pam_umask en su propia imagen y leer el orden que muestra.
Por eso /etc/login.defs puede anunciar un valor mientras la sesión termina usando otro, sin mostrar ningún aviso en ninguno de los dos lados. Los comandos grep indican en qué caso se encuentra. Si la línea pam_umask.so incluye su propio argumento umask=, el valor de login.defs no se aplica.
USERGROUPS_ENAB y la excepción de root
id -un
id -gnSi ambos muestran el mismo nombre, tiene un grupo privado de usuario: la cuenta se creó con un grupo propio cuyo nombre coincide con el de la cuenta. pam_umask tiene un comportamiento para grupos privados de usuario, controlado por USERGROUPS_ENAB en /etc/login.defs. Cuando está activado, la cuenta no es root y el nombre del grupo principal coincide con el nombre del usuario, el módulo copia el dígito del propietario de la máscara en el dígito del grupo. La sesión termina entonces con una máscara que deja abiertos los bits del grupo en todo lo que cree esa cuenta. El propio módulo excluye a root, y esa exclusión es la razón individual más frecuente por la que dos shells del mismo servidor muestran máscaras distintas.
La regla se basa en que un grupo privado tiene exactamente un miembro, por lo que tener permisos de escritura para el grupo equivale únicamente a tener permisos de escritura para el propietario. Esto se cumple hasta que alguien añade un segundo miembro al grupo. Desde ese momento, el nuevo miembro puede escribir en todos los archivos que la cuenta haya creado, y no se ejecutó ningún comando sobre esos archivos para habilitarlo. Asigne a cada servicio su propia cuenta de usuario con privilegios mínimos y mantenga deliberadamente ese grupo con un solo miembro.
Shell de inicio de sesión, shell sin inicio de sesión y shell no interactivo
umask
bash -lc 'umask'
bash -c 'umask'PAM se ejecuta cuando se crea una sesión: login en la consola, sshd, su, sudo -i. No se ejecuta cuando un shell inicia otro shell. bash -l es un shell de inicio de sesión, por lo que lee /etc/profile y ~/.profile, pero nunca llama a pam_umask porque no se creó ninguna sesión. bash -c no lee ninguno de los dos archivos y hereda la máscara del proceso que lo inició. Un trabajo de cron, un hook de git y un programa iniciado por un gestor de servicios pertenecen a este último caso, por lo que su máscara es la que tenía el proceso padre.
Por eso es tan habitual recibir el informe «He configurado el valor en /etc/profile, pero el servicio sigue escribiendo con el modo incorrecto». El servicio nunca leyó ese archivo.
Dónde configurarlo para que se mantenga
Configure la máscara donde se inicia realmente la carga de trabajo, porque cada ruta de inicio lee un archivo diferente.
- Para las cuentas que inician sesión:
UMASKen/etc/login.defs, aplicado por pam_umask a todas las sesiones de la máquina. Es un ajuste para toda la máquina, por lo que cambia todas las cuentas a la vez. - Para una sola cuenta: el argumento
umask=de la líneapam_umask.sotambién se aplica a toda la máquina. Por tanto, un valor por usuario debe estar en el campo GECOS de ese usuario, o en~/.profilepara los shells de inicio de sesión y en~/.bashrcpara los interactivos. - Para un daemon gestionado por systemd:
UMask=en la sección[Service]de la unidad. El gestor de servicios inicia la unidad, por lo que/etc/profilenunca se lee y pam_umask nunca se ejecuta. El archivo de unidad es el único de estos ajustes que llega al daemon. - Para un script iniciado por cron o por un hook: un
umaskexplícito en la primera línea, antes de que el script cree cualquier elemento.
[Service]
UMask=<the octal mask you chose>Después, compruébelo desde un inicio nuevo de esa misma ruta. No lo haga nunca desde el shell donde editó el archivo. El shell actual ya tiene su propia máscara, y editar un archivo de configuración no modifica un proceso que ya está en ejecución.
bash -lc 'umask'
sudo -i umaskPor qué ejecutar chmod después no soluciona el problema
chmod corrige los archivos que ya existen. La máscara determina el modo de los archivos que aún no existen. Ejecute chmod -R sobre un directorio y el siguiente archivo que escriba el servicio volverá a tener el modo anterior, porque ese modo procede del proceso que lo crea y nada del directorio lo modifica.
También existe una ventana de riesgo. Entre el momento en que se crea un archivo y el momento en que se ejecuta chmod, el archivo permanece en el disco con el modo más permisivo, y cualquier proceso que pueda leer el directorio puede abrirlo. En el caso de una clave privada o un archivo de backup, esa ventana introduce precisamente el riesgo que intentaba eliminar.
Establezca el modo durante la creación. install -m u=rw,go= newfile /etc/app/newfile escribe el destino con un modo explícito, y mkdir -m hace lo mismo con un directorio. Ambos usan el modo indicado e ignoran la máscara. ssh-keygen establece el modo de la clave privada que escribe, por lo que ese archivo suele tener el modo correcto en una máquina donde los demás no lo tienen.
La víctima habitual es SSH. Un ~/.ssh creado con un mkdir simple, o un authorized_keys al que se añade contenido con cat >>, adopta la máscara de su shell. Con StrictModes activado, sshd se niega a leer un archivo de clave situado en un directorio con permisos de escritura para el grupo. El cliente muestra Permission denied (publickey), mientras que el servidor registra la causa real en /var/log/auth.log:
Authentication refused: bad ownership or modes for directory /home/deploy/.sshEsta comprobación es intencionada, y reforzar la seguridad de SSH en un VPS depende de que se mantenga. Compruebe la máscara en un servidor nuevo antes de crear las cuentas que lo utilizarán, junto con los primeros diez minutos en un VPS nuevo, para establecer de antemano el modo de todos los archivos que escriban esas cuentas.
Las copias y los archivos ignoran la máscara
cp -p y rsync -a restauran el modo registrado en el archivo de origen, por lo que la máscara no interviene en el resultado. tar hace lo mismo cuando extrae como root o como un usuario normal con -p. Un archivo restaurado desde una copia de seguridad conserva el modo que tenía cuando se creó la copia. Compruébelo antes de concluir que se ignora una máscara correcta: para los datos restaurados, la máscara nunca se consultó.
FAQ
¿Por qué mi tarea de cron crea archivos con un modo distinto al de mi sesión de ssh?
Una tarea de cron no es una sesión de inicio de sesión, por lo que pam_umask no se ejecuta y no lee /etc/profile ni ~/.profile. Hereda la máscara del proceso que la inició. Añada un umask explícito en la primera línea del script, antes de que cree cualquier archivo, e imprima la máscara una vez desde la tarea para ver la configuración real de esa tarea y no la de su propia shell.
¿Por qué /etc/login.defs indica una cosa y mi shell muestra otra?
UMASK en /etc/login.defs es sólo el último valor alternativo de pam_umask. El módulo da prioridad a una entrada umask= en el campo GECOS del usuario y después a un argumento umask= en la línea pam_umask.so de /etc/pam.d/. El comportamiento usergroups, activado mediante USERGROUPS_ENAB, reescribe el dígito correspondiente al grupo para cualquier cuenta que no sea root cuyo grupo principal tenga el mismo nombre que la cuenta. Ejecute grep -rn pam_umask /etc/pam.d/ y id -un; id -gn para ver cuál de esas opciones se aplica a su cuenta.
¿Puede una umask hacer que un archivo sea ejecutable?
No. Una máscara sólo puede quitar bits de los permisos solicitados por el programa que crea el archivo. touch nunca solicita el bit de ejecución, por lo que ninguna máscara produce un archivo ejecutable. Confírmelo en un directorio desechable con ( umask a=rwx; touch f; stat -c '%a %A' f ). Para obtener un bit de ejecución necesita chmod o un programa como install -m que lo solicite al crear el archivo.
¿Dónde configuro la umask de un servicio de systemd?
En la unidad, mediante UMask= en la sección [Service]. El gestor de servicios inicia el servicio, no un inicio de sesión, por lo que nunca se leen los archivos de inicio de la shell y pam_umask no se ejecuta. Después de systemctl daemon-reload y de reiniciar la unidad, confírmelo desde fuera: deje que el servicio cree un archivo y lea el resultado con stat -c '%a %n'.
¿Es segura una máscara predeterminada que permite escribir al grupo?
Es segura mientras el grupo tenga exactamente un miembro, que es la premisa del esquema de grupos privados de usuario. Si añade una segunda cuenta a ese grupo, cada archivo creado por la primera cuenta pasa a ser modificable por el nuevo miembro de inmediato, sin ejecutar ningún comando sobre esos archivos. Ejecute id -un y id -gn: si muestran el mismo nombre, está usando un grupo privado. Si comparte un grupo entre cuentas, configure una máscara que quite el bit de escritura del grupo, cree un archivo y lea stat -c '%a %n' para confirmar que el cambio se aplicó.