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, d es un directorio, l es 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 requiere x para 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 root puede 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 -R sigue el árbol de directorios. Cuando encuentra un enlace simbólico, por defecto cambia el propietario del archivo al que apunta el enlace. Añade -h si 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: umask es una operación de máscara de bits, no una resta aritmética. Con umask 033, los archivos nuevos tienen permisos 644, no 633. La razón es que el bit x no está presente en 666, 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/passwd se 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. /tmp usa este bit y se muestra como drwxrwxrwt.

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 777 para “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 755 en el árbol de directorios. Este comando hace que cada archivo sea ejecutable. Usa X mayúscula o find como en la sección 3.3.
  • Verificar solo los permisos del archivo. Necesitas verificar también los directorios padres con namei -l.
  • Ejecutar sudo y olvidar cambiar el propietario. Los archivos creados con sudo pertenecen a root, por lo que una aplicación ejecutada como usuario normal no puede escribir en ellos. Ejecuta chown inmediatamente 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_keys o la clave privada si los permisos son demasiado permisivos. El directorio ~/.ssh necesita permisos 700, la clave privada necesita permisos 600.

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.

linuxchmodchownumaskseguridad