Permisos de archivos en Linux: chmod, chown y umask
1. Descripción general
El error Permission denied es uno de los errores más comunes al desplegar aplicaciones en Linux. La aplicación no puede escribir logs, no puede leer archivos de configuración, o los archivos secretos pueden ser leídos por cualquier usuario.
Este artículo explica el modelo de permisos de archivos de Linux y cómo usar tres herramientas principales: chmod para cambiar permisos, chown para cambiar el propietario, umask para controlar los permisos predeterminados de archivos nuevos. Al final, podrás leer los permisos de cualquier archivo, asignar los permisos necesarios correctamente y manipular permisos POSIX desde Java.
2. Lectura de permisos de archivos con ls -l
Comienza con un directorio de ejemplo:
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 primera columna tiene 10 caracteres:
- El primer carácter es el tipo:
-es un archivo normal,des un directorio,les un enlace simbólico. - Los 9 caracteres restantes se dividen en tres grupos, cada uno de 3 caracteres: user (propietario), group (grupo), other (otros).
- Cada grupo contiene
r(lectura),w(escritura),x(ejecución). Un guion-significa que no hay ese permiso.
La tercera y cuarta columnas son el propietario (an) y el grupo (dev). Así, con order.csv, an puede leer y escribir, los miembros del grupo dev solo pueden leer, y otros usuarios también solo pueden leer.
Para ver los permisos en forma numérica, usa 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
El kernel verifica los permisos en orden. Si eres el propietario, solo se aplica el grupo de permisos del usuario. Si no eres el propietario pero perteneces al grupo, solo se aplica el grupo de permisos del grupo. De lo contrario, se usa otros. Los grupos no se acumulan. Por lo tanto, un archivo con permisos ---r--r-- hará que ni siquiera el propietario pueda leerlo, aunque otros puedan.
3. Cambiar permisos con chmod
3.1. Formato simbólico
El formato simbólico tiene la sintaxis [ugoa][+-=][rwxX]. Donde u es usuario, g es grupo, o es otros, a es todos.
chmod g+w order.csv # añadir permiso de escritura al grupo
chmod o-r order.csv # quitar permiso de lectura de otros
chmod u=rw,go= order.csv # usuario: rw, grupo y otros: sin permisos
stat -c '%a %A' order.csv
600 -rw-------
Los operadores + y - solo agregan o quitan los permisos especificados; otros permisos permanecen sin cambios. El operador = reasigna completamente el grupo. El formato simbólico es seguro cuando solo deseas modificar una parte sin necesidad de conocer los permisos actuales.
3.2. Formato octal
Cada permiso tiene un valor: r = 4, w = 2, x = 1. Sumar los valores da un dígito para cada grupo.
| Número | Permisos | Usado para |
|---|---|---|
| 7 | rwx |
propietario de un directorio o script |
| 6 | rw- |
propietario de un archivo de datos |
| 5 | r-x |
leer e ingresar al directorio, ejecutar scripts |
| 4 | r-- |
solo lectura |
| 0 | --- |
sin permisos |
Por ejemplo, crea un script y un archivo que contiene una contraseña:
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 permite a todos ejecutar el script pero solo el propietario puede modificarlo. 600 permite que solo el propietario lea y escriba el archivo secreto. El formato octal asigna todos los permisos a la vez, así que es apropiado cuando conoces exactamente el estado deseado.
3.3. Cambiar permisos recursivamente y la letra X mayúscula
El error clásico es ejecutar el siguiente comando:
chmod -R 755 shop/
Este comando asigna permiso de ejecución a todos los archivos, incluso order.csv. La forma correcta es usar X mayúscula. Este símbolo solo agrega x a directorios, o a archivos que ya tenían x en algún grupo:
chmod -R u=rwX,go=rX shop/
El resultado es que los directorios tienen permisos 755, los archivos normales tienen permisos 644, y backup.sh mantiene permisos 755. Si necesitas un control más estricto, puedes separar usando find:
find shop -type d -exec chmod 750 {} +
find shop -type f -exec chmod 640 {} +
4. Los permisos en directorios son diferentes a los permisos en archivos
En los directorios, rwx tiene significados diferentes:
r: listar los nombres de los elementos dentro (ls).w: crear, eliminar, renombrar elementos dentro. Este permiso requierexpara tener efecto.x: ingresar al directorio (cd) y acceder a elementos dentro si ya conoces el nombre.
Una consecuencia sorprendente: eliminar un archivo depende de los permisos del directorio, no de los permisos del archivo.
mkdir inbox && echo "data" > inbox/invoice.txt
chmod 444 inbox/invoice.txt # archivo solo lectura
rm -f inbox/invoice.txt # se puede eliminar de todos modos porque inbox tiene permiso w
ls inbox # vacío
Por el contrario, si quitas el permiso x del directorio, no puedes leer archivos dentro aunque el archivo tenga permisos 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
Cuando una aplicación reporta Permission denied pero los permisos del archivo parecen correctos, verifica todos los directorios padres en la ruta. El comando namei -l /path/to/file lista los permisos de cada nivel. Antes de continuar, restaura los permisos del directorio con chmod u+x inbox.
5. Cambiar el propietario con chown y chgrp
chown cambia el propietario, el grupo o ambos:
sudo chown appuser order.csv # solo cambiar el propietario
sudo chown appuser:dev order.csv # cambiar propietario y grupo
sudo chown :dev order.csv # solo cambiar el grupo
sudo chown -R appuser:appuser /opt/shop
Algunos puntos a recordar:
- Solo
rootpuede cambiar el propietario. Un usuario normal no puede “regalar” su archivo a otra persona. - Un usuario normal puede cambiar el grupo de su archivo, pero solo a un grupo del que es miembro. El comando es
chgrp dev order.csv. chown -Rsigue el árbol de directorios. Cuando encuentra un enlace simbólico, por defecto cambia el propietario del archivo al que apunta el enlace. Añade-hsi quieres cambiar el propietario del enlace mismo.
Un patrón común al desplegar es hacer que el servicio se ejecute como un usuario dedicado, mientras que el equipo de operaciones comparte un grupo:
sudo chown -R appuser:ops /var/log/shop
sudo chmod -R u=rwX,g=rX,o= /var/log/shop
Con esta configuración, el servicio puede escribir logs, el grupo ops puede leer logs, y otros usuarios no pueden acceder.
6. Permisos predeterminados con umask
Cuando se crea un archivo nuevo, los programas generalmente solicitan permisos 666 para archivos y 777 para directorios. Luego el kernel desactiva los bits presentes en umask. El resultado es:
permisos reales = permisos solicitados AND (NOT umask)
umask # ver el valor actual
umask 027 # cambiar para el shell actual
touch customer.csv && mkdir archive
stat -c '%a %n' customer.csv archive
0022
640 customer.csv
750 archive
Con umask 027: los archivos reciben 666 menos los bits 027, quedando 640; los directorios reciben 777 menos 027, quedando 750. Otros pierden todos los permisos, el grupo pierde permiso de escritura.
Nota:
umaskes una operación de máscara de bits, no una resta aritmética. Conumask 033, los archivos nuevos tienen permisos644, no633. La razón es que el bitxno está presente en666, así que no hay nada que desactivar.
Algunos valores comunes:
| umask | Archivo | Directorio | Cuándo usar |
|---|---|---|---|
022 |
644 | 755 | predeterminado en la mayoría de distribuciones |
027 |
640 | 750 | servidor, compartir solo dentro del grupo |
077 |
600 | 700 | datos sensibles |
El comando umask solo tiene efecto en el proceso actual y sus procesos secundarios. Para establecer permanentemente en un servicio systemd, lo declaras en el archivo de unidad:
[Service]
User=appuser
UMask=0027
7. Tres bits especiales: setuid, setgid, sticky
Además de los 9 bits básicos, Linux tiene tres bits especiales. Se escriben como el cuarto dígito al frente en formato octal:
- setuid (4): el programa se ejecuta con los permisos del propietario. Por ejemplo,
/usr/bin/passwdse muestra como-rwsr-xr-x. - setgid (2): cuando se establece en un directorio, los archivos recién creados dentro heredarán el grupo del directorio. Este bit es muy útil para directorios de trabajo compartido.
- sticky (1): cuando se establece en un directorio, solo el propietario del archivo puede eliminarlo.
/tmpusa este bit y se muestra comodrwxrwxrwt.
Por ejemplo, crea un directorio compartido para el grupo dev:
sudo mkdir /srv/shared && sudo chown :dev /srv/shared
sudo chmod 2770 /srv/shared
stat -c '%a %A' /srv/shared
2770 drwxrws---
Nota: No asignes setuid a scripts o archivos propios si no comprendes realmente los riesgos. Un programa setuid root defectuoso es un ataque de escalación de privilegios clásico.
8. Manipular permisos POSIX desde Java
Java NIO admite permisos POSIX a través de PosixFilePermissions. La siguiente clase escribe un archivo secreto y asegura que solo el propietario pueda leerlo:
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-------") convierte cadena simbólica a Set<PosixFilePermission>. Luego setPosixFilePermissions actúa como el comando chmod 600.
Es posible que intentes pasar PosixFilePermissions.asFileAttribute(...) a Files.createFile. Este enfoque aún sufre la aplicación de umask, exactamente como la llamada del sistema open(). Para archivos sensibles, llama a setPosixFilePermissions después de crear el archivo para garantizar el resultado. Una forma aún más segura es crear el archivo con permisos restrictivos desde el inicio, para que no haya un período de tiempo en el que otros puedan leer el archivo.
Prueba con JUnit 5 y 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 omitirá la prueba en Windows, donde no hay vista de atributos posix. Sin esta línea, la prueba lanzará UnsupportedOperationException.
9. Errores comunes
- Usar
chmod 777para “reparar rápidamente”. Esto abre permisos de escritura para todos los usuarios en la máquina. Determina qué usuario ejecuta el proceso (ps -o user= -p <pid>), luego otorga permisos solo a ese usuario o grupo. - Ejecutar
chmod -R 755en el árbol de directorios. Este comando hace que cada archivo sea ejecutable. UsaXmayúscula ofindcomo en la sección 3.3. - Verificar solo los permisos del archivo. Necesitas verificar también los directorios padres con
namei -l. - Ejecutar
sudoy olvidar cambiar el propietario. Los archivos creados consudopertenecen aroot, por lo que una aplicación ejecutada como usuario normal no puede escribir en ellos. Ejecutachowninmediatamente después. - Montar volúmenes en Docker. El UID dentro del contenedor puede diferir del UID en el host. El kernel verifica los permisos por número de UID, no por nombre de usuario.
- SSH rechaza la clave. SSH ignora
~/.ssh/authorized_keyso la clave privada si los permisos son demasiado permisivos. El directorio~/.sshnecesita permisos700, la clave privada necesita permisos600.
10. Conclusión
El modelo de permisos de Linux consta de tres grupos (usuario, grupo, otros) y tres permisos (r, w, x). Estos permisos tienen significados diferentes entre archivos y directorios.
Usas chmod para cambiar permisos: formato simbólico cuando solo necesitas modificar una parte, formato octal cuando deseas asignar completamente. Usas chown y chgrp para asignar archivos al usuario y grupo correcto. Usas umask para que los archivos nuevos se creen con permisos seguros desde el inicio.
El principio fundamental es otorgar el mínimo de permisos posibles. Ejecuta los servicios como un usuario dedicado, comparte a través del grupo, y deja otros en nivel 0 para todo lo sensible.