Un Permission denied en Linux casi nunca se arregla con chmod 777: esa solución abre el archivo a cualquier usuario del sistema y suele ocultar la causa real, que muchas veces es un directorio intermedio de la ruta o el usuario con el que se ejecuta el servicio. En este tutorial aprenderás a leer los permisos de toda una ruta, a comprobar qué puede hacer un usuario concreto y a corregir los casos más frecuentes en Ubuntu 24.04: sitios web con Nginx, claves SSH, carpetas compartidas entre usuarios, servicios y ACL.

Requisitos previos

Para seguir esta guía necesitas:

  • Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath. Los comandos son idénticos en Debian 12; en Rocky Linux 9 cambia el usuario del servidor web (nginx o apache en lugar de www-data) y SELinux añade su propia capa de control.
  • Un usuario no root con privilegios sudo. En esta guía se llama your_user.

Cómo funcionan los permisos

Cada archivo tiene un propietario, un grupo y tres juegos de permisos: para el propietario, para el grupo y para el resto de usuarios. Cada juego combina lectura (r), escritura (w) y ejecución (x):

-rw-r----- 1 your_user www-data 2048 Sep 25 10:00 config.php

Aquí your_user puede leer y escribir, los miembros de www-data solo leer y el resto no tiene acceso. En notación numérica, r vale 4, w 2 y x 1, y se suman por juego: rw-r----- es 640.

El significado cambia en los directorios, y es la fuente de la mayoría de confusiones:

PermisoEn un archivoEn un directorio
rLeer el contenido.Listar los nombres que contiene.
wModificar el contenido.Crear, borrar o renombrar archivos dentro.
xEjecutarlo como programa.Entrar en él y acceder a lo que contiene.

Para abrir /var/www/sitio/index.html, un usuario necesita x en todos los directorios de la ruta (/, /var, /var/www, /var/www/sitio) y r en el archivo. Por eso un archivo con permisos correctos puede seguir dando Permission denied.

Existen además tres permisos especiales:

PermisoValorEfecto habitual
setuid4000Un ejecutable se ejecuta con los privilegios de su propietario (por ejemplo, passwd).
setgid2000En un directorio, los archivos nuevos heredan el grupo del directorio.
sticky1000En un directorio, solo el propietario de un archivo puede borrarlo (por ejemplo, /tmp).

Paso 1: Leer los permisos de un archivo y de su ruta

ls -l muestra los permisos de un archivo, y stat los muestra también en formato numérico:

stat -c '%A %a %U:%G %n' /var/www/your_domain/index.html
-rw-r--r-- 644 your_user:your_user /var/www/your_domain/index.html

La herramienta más útil para diagnosticar es namei -l, que muestra los permisos de cada componente de la ruta, de la raíz al archivo:

namei -l /home/your_user/sitio/index.html
f: /home/your_user/sitio/index.html
drwxr-xr-x root      root      /
drwxr-xr-x root      root      home
drwxr-x--- your_user your_user your_user
drwxr-xr-x your_user your_user sitio
-rw-r--r-- your_user your_user index.html

El archivo es legible por todos, pero /home/your_user tiene permisos 750: quien no sea your_user ni pertenezca a su grupo no puede atravesarlo. Desde Ubuntu 21.04, los directorios personales se crean con estos permisos, y es la causa de muchos errores 403 cuando un servidor web sirve archivos desde /home.

Comprueba también con qué usuario y grupos trabajas:

id your_user
uid=1000(your_user) gid=1000(your_user) groups=1000(your_user),27(sudo),33(www-data)

Paso 2: Comprobar el acceso como otro usuario

La forma más fiable de confirmar un problema de permisos es repetir la operación con el mismo usuario que falla. Con sudo -u y el comando test puedes comprobar lectura (-r), escritura (-w) y ejecución (-x):

sudo -u www-data test -r /home/your_user/sitio/index.html && echo "puede leer" || echo "NO puede leer"
NO puede leer

Para comprobar si puede crear archivos en un directorio, intenta crear uno de prueba y bórralo:

sudo -u www-data touch /var/www/your_domain/uploads/prueba && sudo -u www-data rm /var/www/your_domain/uploads/prueba && echo "puede escribir"

Si el comando falla con Permission denied, usa namei -l sobre esa ruta para ver en qué directorio se corta el acceso.

Paso 3: Corregir scripts que no se ejecutan

Al ejecutar un script sin permiso de ejecución obtienes:

-bash: ./backup.sh: Permission denied

Añade el permiso de ejecución solo para el propietario, o para todos si otros usuarios deben poder ejecutarlo:

chmod u+x backup.sh
ls -l backup.sh
-rwxr--r-- 1 your_user your_user 812 Sep 25 10:15 backup.sh

Si el script tiene permiso x y sigue fallando, comprueba si el sistema de archivos está montado con la opción noexec, que impide ejecutar cualquier programa en él:

findmnt -no OPTIONS --target "$PWD"

Si aparece noexec, mueve el script a otra ubicación, como /usr/local/bin, o ejecútalo a través del intérprete: bash backup.sh.

Paso 4: Corregir los permisos de un sitio web

En Ubuntu, Nginx y PHP-FPM se ejecutan como www-data. Un esquema seguro para /var/www/your_domain es:

  • Los archivos pertenecen a tu usuario (que los despliega) y al grupo www-data (que los lee).
  • Directorios 755 y archivos 644: el servidor web lee, pero no puede modificar el código.
  • Solo los directorios donde la aplicación debe escribir (subidas, caché) son propiedad de www-data.

Primero, identifica el error. Nginx registra los problemas de permisos con el código 13:

sudo tail -n 20 /var/log/nginx/error.log
2026/09/25 10:20:14 [crit] 1203#1203: *41 stat() "/home/your_user/sitio/index.html" failed (13: Permission denied), client: 198.51.100.24, server: your_domain, request: "GET / HTTP/2.0"

La mejor solución para un sitio en /home es moverlo a /var/www, en lugar de abrir el directorio personal:

sudo mkdir -p /var/www/your_domain
sudo rsync -a /home/your_user/sitio/ /var/www/your_domain/

Asigna el propietario y el grupo a todo el árbol:

sudo chown -R your_user:www-data /var/www/your_domain

Aplica 755 a los directorios y 644 a los archivos. Usar find evita dar permiso de ejecución a los archivos, algo que chmod -R 755 haría con todos:

sudo find /var/www/your_domain -type d -exec chmod 755 {} +
sudo find /var/www/your_domain -type f -exec chmod 644 {} +

Da permiso de escritura a www-data solo en el directorio de subidas. En WordPress es wp-content/uploads:

sudo chown -R www-data:www-data /var/www/your_domain/wp-content/uploads

Los archivos con credenciales, como wp-config.php o .env, no deben ser legibles por el resto de usuarios:

sudo chmod 640 /var/www/your_domain/wp-config.php

Actualiza la directiva root del sitio en Nginx para que apunte a /var/www/your_domain, recarga (sudo nginx -t && sudo systemctl reload nginx) y comprueba el acceso:

sudo -u www-data test -r /var/www/your_domain/index.php && echo "puede leer"
curl -I https://your_domain/
puede leer
HTTP/2 200

Paso 5: Corregir los permisos de las claves SSH

OpenSSH rechaza la autenticación por clave si el directorio personal, ~/.ssh o authorized_keys pueden ser modificados por otros usuarios. El cliente solo ve que se le vuelve a pedir la contraseña o Permission denied (publickey); el motivo real está en el registro del servidor:

sudo journalctl -u ssh -n 30 --no-pager | grep -i 'bad ownership\|authentication refused'
sshd[2210]: Authentication refused: bad ownership or modes for directory /home/your_user/.ssh

Aplica los permisos que exige OpenSSH desde una sesión del usuario afectado (por ejemplo, entrando con contraseña o desde otra cuenta con sudo -i -u your_user):

chmod go-w ~
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

Si los archivos se crearon con sudo y pertenecen a root, devuélvelos antes a su usuario:

sudo chown -R your_user:your_user /home/your_user/.ssh

En el equipo cliente, la clave privada también debe ser privada, o ssh se negará a usarla con el aviso UNPROTECTED PRIVATE KEY FILE!:

chmod 600 ~/.ssh/id_ed25519

Comprueba el resultado:

ls -ld ~ ~/.ssh ~/.ssh/authorized_keys
drwxr-x--- 5 your_user your_user 4096 Sep 25 10:30 /home/your_user
drwx------ 2 your_user your_user 4096 Sep 25 10:30 /home/your_user/.ssh
-rw------- 1 your_user your_user  102 Sep 25 10:30 /home/your_user/.ssh/authorized_keys

Antes de cerrar la sesión actual, abre una segunda terminal y confirma que puedes entrar con la clave.

Paso 6: Compartir un directorio entre varios usuarios

Cuando varios usuarios deben editar los mismos archivos, lo correcto es un grupo común y el bit setgid, no dar permisos a todo el mundo. Crea el grupo y añade a los usuarios:

sudo groupadd equipo
sudo usermod -aG equipo your_user
sudo usermod -aG equipo other_user

Crea el directorio con el grupo equipo y permisos 2775. El 2 inicial es el bit setgid: todo lo que se cree dentro pertenecerá al grupo equipo, sin importar quién lo cree:

sudo mkdir -p /srv/proyecto
sudo chown root:equipo /srv/proyecto
sudo chmod 2775 /srv/proyecto
ls -ld /srv/proyecto
drwxrwsr-x 2 root equipo 4096 Sep 25 10:40 /srv/proyecto

La s en la posición de ejecución del grupo confirma el setgid. Los archivos nuevos se crean con la máscara umask del usuario, que en Ubuntu es 002 para usuarios normales, así que el grupo podrá escribir en ellos. Compruébalo iniciando una sesión nueva como other_user (por SSH, o con sudo -i -u other_user) y creando un archivo:

touch /srv/proyecto/prueba.txt
ls -l /srv/proyecto/prueba.txt
-rw-rw-r-- 1 other_user equipo 0 Sep 25 10:42 /srv/proyecto/prueba.txt

Paso 7: Dar acceso a un usuario concreto con ACL

Los permisos clásicos solo admiten un propietario y un grupo. Si necesitas que un usuario más acceda a un directorio sin cambiar el grupo ni abrirlo a todos, usa listas de control de acceso (ACL). Instala las herramientas:

sudo apt install acl

Por ejemplo, para que Nginx (www-data) pueda leer un directorio de informes que pertenece a your_user:

sudo setfacl -R -m u:www-data:rX /srv/informes
sudo setfacl -d -m u:www-data:rX /srv/informes

La X mayúscula da permiso de ejecución solo a los directorios (y a los archivos que ya lo tuvieran), y la opción -d define una ACL por defecto que heredarán los archivos nuevos. Consulta el resultado:

getfacl /srv/informes
# file: srv/informes
# owner: your_user
# group: your_user
user::rwx
user:www-data:r-x
group::r-x
mask::r-x
other::---
default:user::rwx
default:user:www-data:r-x
default:group::r-x
default:mask::r-x
default:other::---

Un + al final de los permisos en ls -l indica que el archivo tiene ACL. Para eliminar la entrada de un usuario, usa sudo setfacl -R -x u:www-data /srv/informes.

Paso 8: Revisar los permisos de servicios y bases de datos

Los servicios del sistema necesitan que sus archivos pertenezcan a su propio usuario. El caso típico es restaurar una copia de MySQL con root como propietario: el servicio no arranca y el registro muestra Permission denied o errno 13. Comprueba y corrige el propietario:

sudo ls -ld /var/lib/mysql
sudo chown -R mysql:mysql /var/lib/mysql
sudo systemctl start mysql

Para cualquier servicio, consulta con qué usuario se ejecuta en su unidad de systemd y compáralo con el propietario de los archivos que usa:

systemctl show -p User -p Group nombre_servicio

Si los permisos son correctos y el acceso sigue denegado, puede estar bloqueándolo AppArmor, que en Ubuntu restringe las rutas a las que acceden servicios como MySQL. Busca denegaciones en el registro del núcleo:

sudo journalctl -k | grep 'apparmor="DENIED"' | tail -n 5

Paso 9: Auditar permisos inseguros

Una revisión periódica detecta archivos que cualquiera puede modificar o ejecutables con privilegios elevados. La opción -xdev limita la búsqueda al sistema de archivos raíz y evita recorrer /proc.

Archivos que cualquier usuario puede modificar:

sudo find / -xdev -type f -perm -0002 -ls

Directorios con escritura para todos y sin el bit sticky (cualquiera podría borrar archivos ajenos):

sudo find / -xdev -type d -perm -0002 ! -perm -1000 -ls

Archivos sin propietario o grupo válido, normalmente restos de usuarios eliminados:

sudo find / -xdev \( -nouser -o -nogroup \) -ls

Ejecutables con setuid o setgid:

sudo find / -xdev -type f \( -perm -4000 -o -perm -2000 \) -ls

Guarda la última lista y compárala en revisiones futuras: un binario setuid nuevo fuera de los paquetes del sistema merece una investigación.

Solución de problemas

sudo: /etc/sudoers is world writable o sudo deja de funcionar: alguien cambió los permisos de /etc/sudoers y sudo se niega a usarlo. Arranca el servidor en modo de recuperación desde la consola del VPS y restaura los permisos con chmod 0440 /etc/sudoers.

Tras un chmod -R 777 el sistema funciona mal: no existe un comando para deshacerlo. Restaura desde una copia de seguridad o reaplica el esquema correcto directorio a directorio como en el paso 4; en rutas del sistema, reinstalar los paquetes afectados con sudo apt install --reinstall recupera sus permisos originales.

Operation not permitted incluso como root: el archivo puede tener el atributo inmutable. Compruébalo con lsattr archivo (aparece una i) y quítalo con sudo chattr -i archivo si es intencionado.

El usuario ya está en el grupo pero sigue sin acceso: la sesión o el servicio se iniciaron antes del cambio. Vuelve a iniciar sesión o reinicia el servicio.

Conclusión

Has aprendido a leer los permisos de toda una ruta con namei -l, a comprobar el acceso con el usuario que falla y a corregir los casos más comunes: scripts sin permiso de ejecución, sitios web servidos por www-data, claves SSH, directorios compartidos con setgid y accesos puntuales con ACL. Como siguientes pasos, revisa el umask de tus servicios, programa la auditoría del paso 9 de forma periódica y documenta el esquema de permisos de cada aplicación para aplicarlo igual en cada despliegue.