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 (
nginxoapacheen lugar dewww-data) y SELinux añade su propia capa de control. - Un usuario no root con privilegios
sudo. En esta guía se llamayour_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:
| Permiso | En un archivo | En un directorio |
|---|---|---|
r | Leer el contenido. | Listar los nombres que contiene. |
w | Modificar el contenido. | Crear, borrar o renombrar archivos dentro. |
x | Ejecutarlo 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:
| Permiso | Valor | Efecto habitual |
|---|---|---|
| setuid | 4000 | Un ejecutable se ejecuta con los privilegios de su propietario (por ejemplo, passwd). |
| setgid | 2000 | En un directorio, los archivos nuevos heredan el grupo del directorio. |
| sticky | 1000 | En 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)
Notasi acabas de añadir un usuario a un grupo con
usermod -aG, el cambio solo se aplica en las sesiones nuevas. Cierra sesión y vuelve a entrar, o reinicia el servicio afectado.
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
755y archivos644: 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.
