Haces un bind-mount de un directorio del host en un contenedor LXC y todo parece estar bien—hasta que el contenedor intenta escribir, chmod, chown o incluso listar el directorio.
Entonces los registros te informan cortésmente: permission denied. A los sistemas productivos les encantan los fallos caballerosos porque te hacen perder tiempo con dignidad.
Este problema rara vez es “un problema de permisos de Linux” en el sentido genérico. En Proxmox, suele ser una desalineación específica y predecible entre
el mapeo de IDs de contenedor no privilegiado y la propiedad/ACLs en el host, a menudo aderezada con la semántica de almacenamiento en red.
La solución no es terapia aleatoria con chmod. La solución es alinear identidades.
Qué está pasando realmente (y por qué tus ojos mienten)
Los contenedores LXC no privilegiados de Proxmox son una medida de seguridad: root dentro del contenedor no es root en el host.
Está mapeado a un UID no privilegiado alto en el host, normalmente empezando en 100000.
Ese es todo el punto: si el contenedor se ve comprometido, “root” no puede escribir en /etc del host, cargar módulos del kernel o borrar tus datos a la ligera.
Los bind mounts (Proxmox mp0, mp1, etc.) abren un agujero controlado en ese aislamiento. Pero no traducen la propiedad de archivos mágicamente.
Si el directorio del host está en propiedad de root:root (0:0) y el root del contenedor está mapeado a 100000:100000, entonces dentro del contenedor el montaje puede parecer
propiedad de root, pero las comprobaciones de permisos subyacentes se realizan contra los IDs del host mapeados.
Por eso “pero dentro del contenedor parece root” es una trampa. Tu root del contenedor es un usuario ordinario en el host. Un usuario ordinario con número muy alto.
Traducción de jerga: el error no trata sobre “montar”. Se trata de identidad y autorización en el límite del VFS.
Un chiste corto, porque vamos a hablar de offsets de UID durante 30 minutos: los bind mounts son como tarjetas de acceso de oficina—tu identificación funciona genial hasta que intentas entrar al servidor.
Guía rápida de diagnóstico
Si estás en medio de un incidente, no divagues. Comprueba esto en orden. Esta secuencia encuentra el cuello de botella rápido y minimiza el “cambié tres cosas y empeoró”.
1) Confirma que el contenedor es no privilegiado y anota el idmap
- Si es no privilegiado, asume que el mapeo UID/GID es el problema hasta que se demuestre lo contrario.
- Si es privilegiado, los permisos siguen siendo relevantes, pero los modos de fallo son distintos.
2) Identifica la ruta del host detrás de mp0 e inspecciona la propiedad/ACLs del host
- La propiedad en el lado del host importa más que la propiedad dentro del contenedor para bind mounts.
- Las ACLs pueden anular expectativas de chmod, y los sistemas de archivos en red añaden sus propias reglas.
3) Prueba escritura y chown desde dentro del contenedor
- Si las escrituras fallan pero las lecturas funcionan: desajuste de propiedad/ACL.
- Si chown falla pero las escrituras funcionan: probablemente no necesitas chown; arregla la aplicación o establece la propiedad correcta una vez.
4) Revisa la semántica del backend de almacenamiento (NFS root_squash, SMB, datasets ZFS)
- NFS con root_squash es famoso por convertir “root” en “nobody”, especialmente en contenedores.
- Los datasets ZFS pueden imponer su propio modo ACL y herencia de permisos.
5) Solo entonces considera “workarounds”
- Cambiar a un contenedor privilegiado es la opción nuclear. Resuelve síntomas al quitar una característica de seguridad.
- Random
chmod -R 777no es una solución; es una confesión.
Datos interesantes y contexto (lo que aprendes después del incidente)
- Los espacios de nombres de UID llegaron al kernel de Linux años antes de que muchas distribuciones los activaran por defecto; la tecnología de contenedores esperaba a que la seguridad y las herramientas maduraran.
- LXC precede a Docker en uso general; es más cercano a “contenedores de sistema” que a “contenedores de proceso único”. Ergonomía distinta, dolores distintos.
- Proxmox eligió contenedores no privilegiados como recomendación por defecto porque reducen drásticamente el radio de explosión en el escenario común de “aplicación web comprometida”.
- El mapeo por defecto de Proxmox suele empezar en
100000y abarcar65536IDs. Ese número no es mágico; es un tamaño típico de rango de IDs subordinados. - Los permisos POSIX se evalúan con los IDs del host después del mapeo. Lo que ves dentro del contenedor es una capa de traducción, no la verdad del host.
- NFS root_squash se inventó para evitar que usuarios root remotos se conviertan en root en el servidor NFS. Los contenedores no tienen una exención especial.
- Shiftfs existió como solución fuera del árbol en Ubuntu para el dolor del desplazamiento de UID. No es la respuesta mainstream hoy, y confiar en ella a largo plazo es una apuesta.
- Los idmapped mounts son una característica más nueva del kernel que puede resolver esto de forma limpia, pero tu versión de Proxmox/kernel y el soporte de herramientas deciden si es práctico.
- Las ACLs son más antiguas de lo que muchos ingenieros piensan en entornos Linux empresariales; no son exóticas y a menudo explican “chmod dice sí pero aún dice no”.
Modelo mental: mapeo UID/GID, rangos idmap y bind mounts
LXC no privilegiado funciona usando un espacio de nombres de usuario. Dentro del contenedor, los procesos usan “UIDs del contenedor”.
El kernel los mapea a “UIDs del host” antes de realizar comprobaciones de permisos en los sistemas de archivos del host.
Un mapeo típico se ve así:
- Container UID 0 (root) → Host UID 100000
- Container UID 1 → Host UID 100001
- …
- Container UID 65535 → Host UID 165535
Así que si haces bind-mount de /tank/shared del host a /mnt/shared en el contenedor, y el directorio del host es propiedad de root:root,
entonces root del contenedor no es el propietario en términos del host. Root del contenedor es host UID 100000. Root del host es UID 0.
Ese desajuste produce los errores clásicos:
- Escritura falla: el directorio no es escribible por el host UID 100000.
- chown falla: el contenedor no privilegiado no puede cambiar la propiedad arbitrariamente en sistemas de archivos del host.
- chmod parece funcionar o no: depende del mapeo, del sistema de archivos y de si las ACLs lo niegan de todos modos.
Básicamente hay cuatro vías de producción:
- Hacer que la propiedad del host coincida con los IDs mapeados (la respuesta usual, aburrida y correcta).
- Usar ACLs en el host para otorgar acceso a los IDs mapeados sin cambiar la propiedad de todo.
- Usar idmapped mounts (si tu stack lo soporta de forma fiable).
- No bind-mountear rutas del host; en su lugar, usar almacenamiento diseñado para compartir (NFS/SMB con mapeo de usuario explícito, o un dataset dedicado por contenedor con la propiedad correcta).
Cambiar a un contenedor privilegiado es la “quinta vía”, pero es más como tirar de la alarma de incendios para apagar el microondas. Funciona, y todos lo recordarán.
Tareas prácticas: comandos, salida esperada y qué decisión tomar
Estas son las comprobaciones prácticas que realmente ejecuto. Cada tarea incluye: un comando, qué significa la salida y qué hacer a continuación.
Ejecuta comandos en el host en el nodo Proxmox y comandos dentro del LXC en el contenedor.
Task 1: Verify the container is unprivileged
cr0x@server:~$ pct config 103 | egrep 'unprivileged|lxc.idmap|mp0|mp1'
unprivileged: 1
mp0: /tank/shared,mp=/mnt/shared
Significado: unprivileged: 1 confirma que tienes mapeo UID/GID en juego.
Si ves líneas lxc.idmap, el mapeo está personalizado y debes respetarlo.
Decisión: Continúa asumiendo un desajuste de mapeo hasta que compruebes que el acceso está otorgado a los IDs del host mapeados.
Task 2: Inspect the container’s ID mapping from the host
cr0x@server:~$ cat /etc/pve/lxc/103.conf | egrep 'unprivileged|lxc.idmap'
unprivileged: 1
Significado: No ver líneas idmap personalizadas normalmente significa que aplican los rangos subordinados por defecto.
Decisión: Revisa los rangos subuid/subgid del nodo a continuación; eso determina el offset de UID del host.
Task 3: Check subordinate UID/GID ranges on the Proxmox node
cr0x@server:~$ grep -E '^root:' /etc/subuid /etc/subgid
/etc/subuid:root:100000:65536
/etc/subgid:root:100000:65536
Significado: Container UID 0 se mapea a host UID 100000, y tienes un rango de 65536 IDs.
Decisión: Cualquier directorio del host que bind-montees debe ser escribible por UID/GID de host 100000 (o por los IDs del host que correspondan al usuario del contenedor en ejecución).
Task 4: Find the host path for the mount point
cr0x@server:~$ pct config 103 | grep '^mp'
mp0: /tank/shared,mp=/mnt/shared
Significado: La ruta del host es /tank/shared. Esa es la ruta cuya propiedad y ACLs deben permitir los IDs mapeados.
Decisión: Inspecciona la propiedad y permisos en el host sobre esa ruta (no dentro del contenedor primero).
Task 5: Inspect host ownership, mode bits, and ACL presence
cr0x@server:~$ ls -ldn /tank/shared
drwxr-x--- 5 0 0 5 Dec 26 08:41 /tank/shared
Significado: Owner UID/GID es 0:0 (root del host). El modo 750 significa que solo propietario y grupo pueden escribir/leer; otros no.
El host UID 100000 es “others”, por lo que el root del contenedor no podrá escribir.
Decisión: O cambias la propiedad a 100000:100000, o añades una ACL que otorgue acceso a host 100000, o reestructuras el almacenamiento.
Task 6: Confirm what user the workload runs as inside the container
cr0x@server:~$ pct exec 103 -- ps -eo user,uid,group,gid,comm | head
USER UID GROUP GID COMMAND
root 0 root 0 ps
www-data 33 www-data 33 nginx
Significado: Tu aplicación puede no ejecutarse como root. Aquí, nginx corre como UID 33 dentro del contenedor.
Decisión: Mapea el UID de ejecución del contenedor al host: host UID = 100000 + 33 = 100033. Esa es la identidad que necesita acceso en el host.
Task 7: Calculate the mapped host UID/GID for a container user
cr0x@server:~$ echo $((100000 + 33))
100033
Significado: Container UID 33 corresponde a host UID 100033 (asumiendo el mapeo por defecto).
Decisión: Otorga permisos de escritura en el host a UID 100033 (y quizá GID 100033), no a UID 33.
Task 8: Prove the failure from inside the container (write test)
cr0x@server:~$ pct exec 103 -- bash -lc 'id && touch /mnt/shared/.permtest'
uid=0(root) gid=0(root) groups=0(root)
touch: cannot touch '/mnt/shared/.permtest': Permission denied
Significado: Clásico. El montaje es visible, pero los permisos del host niegan al ID mapeado.
Decisión: Arregla la propiedad/ACLs en el host. No “arregles” esto con chmod dentro del contenedor esperando que persista.
Task 9: Check mount type and options on the host
cr0x@server:~$ findmnt -no SOURCE,FSTYPE,OPTIONS /tank/shared
tank/shared zfs rw,xattr,posixacl
Significado: Dataset ZFS con soporte ACL habilitado. Bien. Esto te da opciones más controladas que solo chmod.
Decisión: Prefiere un permiso mediante ACL o un dataset por contenedor con propiedad mapeada, en lugar de ampliar globalmente los bits de modo.
Task 10: Fix ownership on the host to match the mapped IDs (simple case)
cr0x@server:~$ chown -R 100000:100000 /tank/shared
Significado: Ahora root del contenedor (host 100000) posee el árbol. Dentro del contenedor, root debería poder escribir.
Decisión: Usa esto cuando el directorio esté dedicado a ese contenedor. Evítalo para datos compartidos entre contenedores a menos que tengas un plan de identidad compartida.
Task 11: Alternatively, grant access via ACLs to the mapped user (less disruptive)
cr0x@server:~$ setfacl -m u:100033:rwx /tank/shared
cr0x@server:~$ getfacl -p /tank/shared | sed -n '1,12p'
# file: /tank/shared
# owner: root
# group: root
user::rwx
user:100033:rwx
group::r-x
mask::rwx
other::---
Significado: El usuario del host 100033 (www-data del contenedor) tiene acceso completo, sin cambiar la propiedad root.
Decisión: Elige ACLs cuando necesites que root del host siga siendo el propietario o cuando varias identidades necesiten acceso controlado. Recuerda establecer ACLs por defecto para nuevos archivos si es necesario.
Task 12: Set default ACLs so new files inherit permissions (common “it worked yesterday” fix)
cr0x@server:~$ setfacl -d -m u:100033:rwx /tank/shared
cr0x@server:~$ getfacl -p /tank/shared | grep -E '^default:user:100033'
default:user:100033:rwx
Significado: Los nuevos archivos y directorios creados bajo /tank/shared heredan la entrada ACL.
Decisión: Si tu aplicación crea subdirectorios y de repente no puede escribir en ellos, las ACLs por defecto suelen ser la pieza que falta.
Task 13: Validate from inside the container after changes
cr0x@server:~$ pct exec 103 -- bash -lc 'touch /mnt/shared/.permtest && ls -ln /mnt/shared/.permtest'
-rw-r--r-- 1 0 0 0 Dec 26 09:02 /mnt/shared/.permtest
Significado: El contenedor puede escribir ahora. No te obsesiones con el UID mostrado dentro del contenedor; el host está haciendo cumplir correctamente mediante mapeo/ACL.
Decisión: Pasa a las expectativas específicas de la aplicación: ¿requiere chown? ¿necesita escritura de grupo? gestiona eso explícitamente.
Task 14: Diagnose “chown: Operation not permitted” correctly
cr0x@server:~$ pct exec 103 -- bash -lc 'chown 33:33 /mnt/shared/.permtest'
chown: changing ownership of '/mnt/shared/.permtest': Operation not permitted
Significado: Los contenedores no privilegiados por lo general no pueden cambiar la propiedad en rutas bind-montadas del host como lo haría un root real. Esto es esperado.
Decisión: Arregla la propiedad en el host una vez, o ajusta la aplicación para que deje de intentar chown en tiempo de ejecución. Si la app requiere chown, necesitas diseñar alrededor de eso (dataset dedicado y árbol pre-propietario, o un método de compartición distinto).
Task 15: Check for NFS root_squash (if the host path is an NFS mount)
cr0x@server:~$ findmnt -no SOURCE,FSTYPE,OPTIONS /tank/shared
nas:/export/shared nfs4 rw,relatime,vers=4.2,hard,proto=tcp,timeo=600,retrans=2,sec=sys
Significado: Los permisos “reales” ahora están gobernados por el servidor NFS, además de opciones de exportación como root_squash y comportamiento de idmapping.
Decisión: Si sospechas root_squash, deja de intentar hacer que “root en el contenedor” posea cosas. Usa un export NFS dedicado con un UID/GID de servicio explícito que coincida con los IDs mapeados del host, o evita bind-mountear NFS en contenedores no privilegiados por completo.
Task 16: See what UID the host uses when the container writes (forensics)
cr0x@server:~$ rm -f /tank/shared/hostview.test
cr0x@server:~$ pct exec 103 -- bash -lc 'echo hi > /mnt/shared/hostview.test'
cr0x@server:~$ ls -ln /tank/shared/hostview.test
-rw-r--r-- 1 100000 100000 3 Dec 26 09:06 /tank/shared/hostview.test
Significado: El archivo es propiedad del host 100000:100000 porque fue creado por root del contenedor.
Decisión: Usa esto para validar tus asunciones de mapeo. Si esperabas 100033 pero ves 100000, tu proceso se está ejecutando como root dentro del contenedor.
Patrones de reparación que funcionan en producción
Pattern A: Dedicated host directory per container, owned by mapped root (fast, clean)
Si solo un contenedor usa el bind mount, el plan más sencillo es: crea un directorio dedicado y chownéalo al ID root mapeado del contenedor.
Esto es aburrido. Lo aburrido es bueno.
cr0x@server:~$ mkdir -p /tank/ct103-data
cr0x@server:~$ chown -R 100000:100000 /tank/ct103-data
cr0x@server:~$ chmod 0750 /tank/ct103-data
Por qué funciona: alineas el propietario del host con la identidad mapeada del root del contenedor.
Cuándo evitarlo: datos compartidos entre múltiples contenedores con diferentes rangos UID; acabarás con un cajón de propiedad desordenada.
Pattern B: ACL grants for specific container service users (surgical, scalable)
Este es el enfoque adulto para directorios compartidos: mantiene la propiedad host significativa, concede acceso a IDs mapeados vía ACLs, establece ACLs por defecto para herencia,
y deja de tratar chmod como tu única herramienta.
cr0x@server:~$ setfacl -m u:100033:rwx /tank/shared
cr0x@server:~$ setfacl -d -m u:100033:rwx /tank/shared
Por qué funciona: otorga exactamente el acceso requerido y sobrevive a la creación de nuevos archivos.
Compromiso: debes operacionalizar la visibilidad de ACL. Si tu equipo no puede confiarse a ejecutar getfacl, tendrás misterios recurrentes.
Pattern C: One dataset per container (especially with ZFS), ownership set once
Con ZFS en Proxmox, los datasets por contenedor son limpios porque mantienen cuotas, snapshots y replicación ordenados.
También reducen la complejidad de ACL porque cada dataset tiene una “historia de identidad” única.
Crea el dataset, móntalo en la ruta del host, chownéalo a los IDs mapeados y luego bind-móntalo en el contenedor. Mantén el límite de datos nítido.
Pattern D: idmapped mounts (modern, elegant, but check your platform)
Los idmapped mounts pueden presentar un árbol de sistema de archivos con un mapeo UID/GID diferente en el momento del montaje.
Eso es básicamente “hacer el desplazamiento en la capa VFS” en lugar de chownear todo.
En la práctica, tu decisión depende de la versión de Proxmox, soporte del kernel y si quieres ser la primera persona de tu equipo en depurarlo a las 3 a.m.
Si no puedes responder “¿cómo validamos este mapeo después de una actualización del kernel?”, mantente con propiedad/ACLs.
Pattern E: Stop bind-mounting and use a sharing protocol with explicit identity
A veces la solución correcta es arquitectónica: no bind-montees directorios del host en contenedores no privilegiados cuando el almacenamiento de respaldo es de por sí un sistema remoto,
o cuando múltiples contenedores necesitan escritura compartida con usuarios distintos.
Usa NFS/SMB dentro del contenedor con una cuenta de servicio bien definida y UID/GID consistentes entre sistemas. O expón el almacenamiento vía una capa de servicio.
Los bind mounts son fantásticos hasta que dejan de serlo.
Tres mini-historias del mundo corporativo (anonimizadas, dolorosamente plausibles)
Mini-story 1: The incident caused by a wrong assumption
Un equipo de plataforma migró una carga de trabajo heredada basada en VM a Proxmox LXC para ahorrar recursos. Hicieron las cosas bien—la dejaron no privilegiada, movieron configuraciones, montaron monitorización.
También bind-montaron un directorio del host que contenía uploads de la aplicación.
En staging “funcionó”. En producción, las subidas empezaron a fallar intermitentemente. La app lanzó errores genéricos de I/O, y el ingeniero on-call hizo lo que hacen los humanos bajo estrés:
culpó a la aplicación, luego a la red, luego al “almacenamiento”.
La suposición equivocada fue simple: “root dentro del contenedor es suficientemente root.” Miraron ls -l dentro del contenedor,
vieron root:root y asumieron que el contenedor debería poder escribir.
Mientras tanto, en el host, el directorio era propiedad de 0:0 con permisos 750.
Root del contenedor era host 100000—efectivamente “other”. Las escrituras fallaban cuando la app rotaba a nuevos subdirectorios con modos más estrictos.
La corrección fue una secuencia de comandos y un cambio de política: chown al directorio de uploads dedicado al servicio mapeado,
añadir ACLs por defecto para que las nuevas carpetas hereden permisos de escritura, y documentar la regla de mapeo en el runbook.
El postmortem fue corto, que es lo mejor.
Mini-story 2: The optimization that backfired
Otra organización quería reducir duplicación. Crearon un único directorio compartido en el host para “assets comunes” y lo bind-montearon en una flota de contenedores.
Para “hacerlo fácil”, ejecutaron un amplio chmod -R 777 en el árbol y siguieron con su vida. A nadie le gustan los tickets de permisos.
Dos meses después, un contenedor comprometido escribió un binario malicioso en ese directorio compartido. Otro contenedor—de un equipo completamente distinto—ejecutó un script auxiliar
desde la ruta compartida durante un paso de despliegue. No fue un ataque sofisticado. Fue simplemente un directorio compartido escribible comportándose exactamente como hacen los directorios compartidos escribibles.
La remediación no fue divertida. Tuvieron que desenmarañar qué contenedores necesitaban acceso solo lectura, cuáles necesitaban escritura y cuáles no debieron tener el montaje.
Terminaron reemplazando el montaje compartido escribible por un montaje readonly para la mayoría de contenedores y una ubicación de escritura separada por contenedor.
La ironía: la “optimización” para ahorrar espacio creó una responsabilidad de seguridad y fiabilidad. También aumentó la carga operativa porque auditar un directorio world-writable
en un entorno multi-tenant de contenedores es básicamente un pasatiempo, no un control.
Mini-story 3: The boring but correct practice that saved the day
Un equipo de servicios financieros (sí, esa gente) tenía una política: cada bind mount debe tener un ticket con (1) propósito, (2) dueño de datos, (3) modo de acceso, (4) UID/GID mapeados,
y (5) pasos de rollback. Los ingenieros pusieron los ojos en blanco. Luego lo siguieron de todos modos.
Una actualización del kernel pasó por el clúster, y un contenedor empezó a fallar al escribir en su montaje. El on-call abrió el ticket,
vio el UID/GID mapeado exacto y los permisos esperados de la ruta del host, y ejecutó los comandos de verificación.
La ACL del directorio del host había sido sobrescrita por un script de “limpieza” que normalizaba permisos en datasets.
Porque las expectativas estaban documentadas, la corrección fue determinista: restaurar las entradas y defaults de ACL, validar con una prueba de escritura,
y añadir una protección al script de limpieza para saltarse los mountpoints gestionados.
Nadie debatió si “simplemente hacerlo privilegiado”. Nadie adivinó.
Fue aburrido. Fue correcto. Evitó una escalada desordenada y mantuvo el radio de explosión pequeño.
La higiene operativa aburrida no te promociona en el momento, pero te deja dormir.
Errores comunes: síntoma → causa raíz → solución
1) Symptom: “Permission denied” on touch or writes inside the mount
Root cause: directorio del host no escribible por el UID/GID del host mapeado (normalmente 100000+).
Fix: chown del directorio del host a los IDs mapeados o otorgar ACLs a esos IDs; valida con ls -ln en el host y una prueba de escritura desde el contenedor.
2) Symptom: “Operation not permitted” on chown inside the container
Root cause: el contenedor no privilegiado no puede cambiar propiedad en bind mounts como lo haría un root real.
Fix: establece la propiedad correcta en el host de antemano; cambia la app para que deje de hacer chown en tiempo de ejecución; o usa datasets por contenedor y rutas preasignadas.
3) Symptom: Works on existing files, fails on newly created subdirectories
Root cause: faltan ACLs por defecto o el umask crea directorios sin permiso de escritura para el usuario de servicio mapeado.
Fix: establece ACLs por defecto en la ruta del host; asegura propiedad de grupo y bit setgid si usas patrones basados en grupo.
4) Symptom: NFS-mounted host path behaves strangely; root can’t write even after chown
Root cause: opciones de exportación NFS (root_squash), o desajuste de UID entre cliente y servidor, o problemas de idmapping en NFSv4.
Fix: usa un export NFS dedicado con UID/GID de servicio explícito que coincida con los IDs mapeados del host; considera montar NFS dentro del contenedor con un plan de identidad consistente.
5) Symptom: chmod/chown changes “don’t stick” or behave inconsistently
Root cause: ACLs o modelo de permisos a nivel de sistema de archivos (modo ACL de ZFS, semántica SMB) que anulan expectativas de chmod.
Fix: inspecciona y gestiona ACLs intencionalmente; confirma la configuración ACL del sistema de archivos; evita mezclar expectativas POSIX y NFSv4 sin plan.
6) Symptom: You “fixed” it by making the container privileged, and now security is nervous
Root cause: quitaste el mecanismo de seguridad en lugar de alinear identidades.
Fix: revierte a no privilegiado si es posible; implementa mapeo de propiedad/ACLs o rediseña el almacenamiento; reserva contenedores privilegiados para casos con excepción documentada y controles compensatorios.
7) Symptom: Only one node in a cluster fails after migrating the container
Root cause: rangos inconsistentes en /etc/subuid//etc/subgid entre nodos, o ACLs/propiedad inconsistentes en almacenamiento compartido.
Fix: estandariza rangos subordinados en todos los nodos; trátalos como configuración de clúster. Valida en cada nodo que pueda ejecutar el contenedor.
8) Symptom: Directory appears owned by “nobody” or odd IDs inside the container
Root cause: desajuste de mapeo, mapeo de identidades en el sistema de archivos remoto, o falta de servicio de nombres dentro del contenedor.
Fix: razona en IDs numéricos; usa ls -ln; no persigas nombres de usuario hasta entender el mapeo numérico.
Listas de verificación / plan paso a paso
Step-by-step: fix a single-container bind mount safely
- En el host: confirma
unprivileged: 1para el contenedor. - En el host: lee
/etc/subuidy obtiene la base (a menudo 100000). - En el host: identifica la ruta
mp0e inspeccionals -ldn. - Decide quién necesita acceso de escritura en el contenedor (root vs usuario de servicio).
- Calcula el UID/GID del host mapeado para ese usuario (base + UID del contenedor).
- Para directorio dedicado:
chownla ruta del host al UID/GID mapeado; establece bits de modo con prudencia. - Para directorio compartido: añade ACLs del host para UID/GID mapeado; establece también ACLs por defecto.
- Valida desde dentro del contenedor: prueba de escritura y (si hace falta) prueba de creación de directorio.
- Valida desde el host: comprueba que la propiedad de archivos muestra los IDs mapeados, no 0:0.
- Registra el mapeo y las expectativas en una entrada del runbook vinculada al ID del contenedor y al punto de montaje.
Step-by-step: making shared storage not turn into a security dumpster
- Deja de usar permisos world-writable como “arreglo temporal.” Temporal es cómo construyes incidentes permanentes.
- Crea un modelo de grupo compartido si procede, pero recuerda que los GID mapeados difieren en contenedores no privilegiados.
- Prefiere bind mounts solo lectura para assets compartidos. El acceso de escritura debe ser raro y justificado.
- Usa ACLs con IDs mapeados explícitos para los pocos contenedores que necesitan escribir.
- Establece ACLs por defecto y prueba la creación de directorios anidados, no solo un archivo único.
- Implementa auditorías periódicas: lista ACLs y propiedad para las raíces de mount gestionadas.
Segundo chiste corto, luego volvemos al trabajo: un chmod 777 es como apagar el detector de humo porque pita—silencioso, sí; sabio, no.
Preguntas frecuentes
1) Why does it say “permission denied” even when the directory looks like root owns it inside the container?
Porque la propiedad se traduce. Root del contenedor está mapeado a un UID distinto en el host (a menudo 100000). El sistema de archivos del host verifica permisos contra ese UID del host.
2) What’s the quickest reliable fix?
Si el directorio está dedicado a ese contenedor: chown en el host al UID/GID mapeado del contenedor (típicamente 100000:100000 para root del contenedor) y mantén permisos estrictos.
3) How do I map a container user (like www-data UID 33) to the host UID?
Toma la base subordinada de /etc/subuid (p.ej., 100000) y suma el UID del contenedor (33). Resultado: 100033. Misma cuenta para GID.
4) Can I just run chown inside the container?
En rutas bind-montadas del host en contenedores no privilegiados, usualmente no. Obtendrás “Operation not permitted.” Establece la propiedad en el host en su lugar, o usa ACLs.
5) Is switching to a privileged container acceptable?
A veces, pero trátalo como una excepción con controles compensatorios. Los contenedores privilegiados incrementan notablemente el riesgo para el host. Alinear UID/GID casi siempre es mejor.
6) Why does it work on one Proxmox node but not after migrating the container?
Razón común: diferentes rangos en /etc/subuid//etc/subgid entre nodos. El mismo UID de contenedor se mapea a distintos UIDs del host, así que los permisos se rompen.
7) What about NFS? Why is it extra painful?
NFS añade enforcement de permisos en el servidor y opciones de exportación como root_squash. El UID mapeado del contenedor puede no coincidir con lo que espera el servidor NFS.
Resuélvelo diseñando identidades de servicio consistentes o usando exports dedicados con una estrategia UID/GID explícita.
8) Should I use ACLs or chown?
Usa chown para rutas dedicadas por contenedor. Usa ACLs cuando la propiedad debe seguir siendo root del host o cuando múltiples identidades necesitan acceso controlado.
Si necesitas ambos, probablemente estés modelando almacenamiento compartido—trátalo como tal, no como un bind mount improvisado.
9) What’s the role of idmapped mounts here?
Pueden resolver la traducción UID/GID limpiamente en el momento del montaje sin chownear datos. Pero el soporte operativo varía por kernel/herramientas.
Si no puedes probarlo y monitorearlo con confianza, quédate con el enfoque probado de propiedad/ACLs.
10) How do I keep this from recurring?
Estandariza los rangos subuid/subgid en todo el clúster, documenta expectativas de propiedad/ACL por contenedor y añade una prueba de validación simple (crear archivo, crear directorio).
Conclusión: siguientes pasos que puedes hacer hoy
“Permission denied” en un bind mount LXC de Proxmox casi nunca es un misterio. Es matemáticas más política:
el usuario del contenedor se mapea a un UID/GID distinto en el host, y la ruta del host no lo permite.
Haz lo siguiente:
- Elige un contenedor con un bind mount que falle y escribe: ID del contenedor, ruta mpX, flag unprivileged y base subuid.
- Calcula el UID/GID del host mapeado para el usuario de servicio que necesita acceso.
- Arréglalo con chown dedicado (ruta single-tenant) o ACLs + ACLs por defecto (ruta compartida).
- Valida con una prueba de escritura y una prueba de “crear subdirectorio”. El futuro tú agradecerá la prueba de subdirectorio.
- Estandariza
/etc/subuid//etc/subgiden todos los nodos antes de la próxima sorpresa por migración.
Idea parafraseada de John Allspaw: el trabajo real en operaciones es diseñar sistemas y procesos que hagan que las fallas sean comprensibles y recuperables.