Debian 13: Permiso denegado en bind mounts — arregla el mapeo UID/GID de forma limpia (caso #41)

¿Te fue útil?

Actualizas un host a Debian 13, redeployas un contenedor, y de repente tu bind mount “perfectamente normal” se convierte en un ladrillo:
Permission denied al leer, escribir, mkdir, a veces incluso al ls. Los registros no ayudan. Tus colegas sugieren
chmod -R 777 como si fuera un suplemento vitamínico.

Esto usualmente no es un problema de “permisos”. Es un problema de identidad. Los bind mounts no traducen la propiedad; exponen fielmente
el sistema de archivos del host al proceso dentro del namespace objetivo. Si el mapeo UID/GID no coincide, obtienes denegación—con razón.
La solución limpia es alinear el mapeo UID/GID (o traducirlo deliberadamente con idmapped mounts), no empezar un incendio de chmod en producción.

Guía de diagnóstico rápido

Cuando la página te despierta a las 02:13, no necesitas filosofía. Necesitas una secuencia corta y fiable que encuentre el cuello de botella rápido.
Aquí está la ruta más rápida que he encontrado para llegar a la verdad en hosts Debian 13 que ejecutan contenedores y/o mounts de systemd.

1) Confirma qué está fallando: permisos del kernel, mapeo de namespaces o LSM?

  • Primero: reproduce en el host vs dentro del contenedor. Si funciona en el host pero falla en el contenedor, casi siempre es mapeo o LSM.
  • Segundo: comprueba los IDs numéricos de propiedad en la ruta montada (stat), no los nombres. Los nombres engañan; los números no.
  • Tercero: verifica si eres rootless, userns-remapped, o estás usando idmapped mounts. El mapeo determina si el UID 1000 significa “tú” o “alguien más”.
  • Cuarto: elimina rápidamente AppArmor/SELinux como factor confuso (incluso si “no lo usas”).

2) Decide qué clase de solución necesitas

  • Mismo host, mismo contenedor: alinea UID/GID dentro del contenedor para que coincida con los archivos del host, o mapea los IDs del host intencionalmente usando idmapped mounts.
  • Multi-host / NFS: estandariza los UID/GID numéricos vía directorio/SSSD o asignación explícita de UID; no “arregles” cada nodo a mano.
  • Kubernetes hostPath: trátalo como “el sistema de archivos del nodo”. Usa IDs consistentes, fsGroup, o un init container controlado, no chmods aleatorios.
  • Rootless: revisa /etc/subuid y /etc/subgid, y verifica que el mapeo desplazado cubra los IDs que necesitas.

3) Valida con una prueba definitiva

Crea un archivo desde dentro del contenedor y verifica su UID/GID en el host. Luego al revés: crea en el host y escribe desde el contenedor.
Si los IDs no coinciden con lo esperado, deja de ajustar bits de modo. Estás depurando identidad, no bits de acceso.

Qué hacen realmente los bind mounts (y qué nunca harán)

Un bind mount es brutalmente literal. Toma un directorio (o archivo) de un lugar del árbol de archivos y lo hace aparecer en otro sitio.
Eso es todo. No hay traducción de propiedad. No hay mediación de permisos más allá de lo que el kernel ya aplica. No hay renombrado amistoso de usuarios y grupos
solo porque moviste la ruta bajo /var/lib/containers.

Esta literalidad es la razón por la que los bind mounts son rápidos y predecibles. También es la razón por la que los bind mounts se convierten en un generador
recurrente de incidentes cuando mezclas: contenedores rootless, user namespaces, almacenamiento multi-host y defaults “útiles” de orquestadores.

El modelo mental que evita malas soluciones

El kernel decide el acceso usando IDs numéricos (UID/GID), bits de modo, ACLs y política LSM. Un proceso dentro de un contenedor sigue siendo un proceso.
Si ese proceso tiene UID 1000 dentro de su namespace, el kernel puede tratarlo como UID 100000 fuera, dependiendo del mapeo. Si el directorio
en el host es propiedad del UID 1000, y el proceso es efectivamente UID 100000, no es el dueño. La escritura falla. Con razón.

Si “arreglas” esto con chmod -R 777, no resolviste el desajuste de identidad. Solo renunciaste a la seguridad y empeoraste la depuración futura.
Además, tus auditores empezarán a usar palabras como “sistémico”.

Broma #1: “chmod 777” es como apagar la alarma de humo porque hace ruido. Dormirás mejor hasta que no lo hagas.

Hechos interesantes e historia que explican el dolor actual

  1. Los UIDs y GIDs son números primero. Los nombres de usuario son un azúcar de presentación sobrepuestos (tradicionalmente vía /etc/passwd o NSS).
  2. Los bind mounts llegaron mucho antes que los contenedores. Eran un truco de sistema de archivos para chroots y cambios de layout; los contenedores los heredaron como primitiva de volumen.
  3. Los user namespaces son relativamente “Linux moderno”. Existieron años antes de volverse mainstream, y algunas distros los habilitaron con cautela por historia de seguridad.
  4. Los contenedores rootless hicieron visible el desajuste. En modo rootful, UID 0 a menudo pasa por encima de problemas (hasta que choca con NFS root-squash o reglas LSM).
  5. NFS siempre ha castigado la gestión descuidada de identidades. Para NFS clásico, el UID/GID numérico debe coincidir entre clientes o obtendrás caos de propiedad “funciona en mi nodo”.
  6. Los idmapped mounts son una característica del kernel, no del contenedor. Permiten que el kernel traduzca la propiedad por mount sin cambiar la propiedad en disco.
  7. Las ACLs pueden anular bits de modo que parecen correctos. Un directorio puede ser drwxrwx--- y aun así negar acceso por una entrada ACL que olvidaste que existía.
  8. Los sistemas de archivos overlay complican la propiedad percibida. Con filesystems por capas, puedes ver una propiedad en la capa del contenedor y otra en el backing store del host.

La causa principal: desajuste UID/GID entre namespaces

El patrón recurrente detrás de “Permission denied en bind mounts” es simple:
el UID/GID efectivo del proceso, como lo ve el kernel, no posee los archivos que intenta acceder.
Y los bind mounts no cambian esa relación—la exponen.

Dónde se culpa a Debian 13 (a menudo injustamente)

Debian 13 no está “rompiendo los bind mounts”. Lo que cambia en sistemas modernos es la prevalencia de:
Podman/Docker rootless, unidades de mount de systemd, defaults más estrictos y configuraciones de user namespace. Cualquier pequeña deriva en la coordinación UID/GID
se vuelve visible cuando actualizas componentes y redeployas.

Escenarios típicos de desajuste

  • Contenedor rootless + directorio del host propiedad de tu UID real. El “UID 0” del contenedor se mapea a un rango alto de host (desde /etc/subuid).
  • Diferente asignación de UID entre hosts. El usuario app es UID 1001 en el nodo A y UID 1002 en el nodo B. NFS almacena solo números.
  • Usuario de la imagen del contenedor no coincide con el usuario del servicio en el host. La imagen corre como UID 1000, el directorio del host es propiedad de UID 2000, y asumiste que los nombres importaban.
  • Contenedor rootful + NFS root-squash. “Pero es root en el contenedor” choca con “root se convierte en nobody en el servidor”.
  • ACLs o política LSM niegan a pesar de bits de modo correctos. Puedes mirar ls -l todo el día y nunca ver una negación de AppArmor.

Cita de ingeniería de confiabilidad (idea parafraseada): Werner Vogels ha dicho que construyes sistemas esperando fallos, no fingiendo que no ocurrirán.
Los desajustes de identidad son una forma de “falla esperada” en Linux multi-tenant; diseña para ello.

Tareas prácticas: comandos, salidas y decisiones (12+)

Estos son los comandos que realmente ejecuto. Cada tarea incluye: el comando, qué te dice la salida y la decisión que tomas a partir de ello.
Asume que la ruta bind-mounted en cuestión es /srv/app/data en el host y aparece como /data en el contenedor.

Task 1: Confirma que es un bind mount (no otro sistema de archivos)

cr0x@server:~$ findmnt -T /srv/app/data -o TARGET,SOURCE,FSTYPE,OPTIONS
TARGET        SOURCE         FSTYPE OPTIONS
/srv/app/data /dev/sdb1[/data] ext4   rw,relatime

Significado: Estás en ext4 y no estás viendo una sorpresa NFS/overlay.
Decisión: Si esto muestra nfs, salta a las comprobaciones de identidad NFS. Si muestra overlay, trátalo como comportamiento de capa de contenedor.

Task 2: Inspecciona la propagación del mount y la bind-ness explícitamente

cr0x@server:~$ findmnt -o TARGET,SOURCE,FSTYPE,OPTIONS,PROPAGATION | grep -E '(/srv/app/data|/data)'
/srv/app/data  /dev/sdb1[/data] ext4  rw,relatime shared

Significado: La propagación puede afectar si los mounts aparecen dentro de contenedores.
Decisión: Si la propagación es private y dependes de mounts anidados, tendrás problemas de “datos faltantes”, no permiso denegado. Es otro tipo de fallo.

Task 3: Comprueba la propiedad numérica y los bits de modo (host)

cr0x@server:~$ stat -c 'path=%n uid=%u gid=%g mode=%a owner=%U group=%G' /srv/app/data
path=/srv/app/data uid=2000 gid=2000 mode=750 owner=app group=app

Significado: El kernel requerirá UID 2000 o GID 2000 (o ACL) para escribir.
Decisión: Si tu contenedor corre como UID 1000, va a fallar. Arregla el mapeo, no el modo.

Task 4: Identifica la identidad del proceso dentro del contenedor

cr0x@server:~$ podman exec -it appctr sh -lc 'id && umask'
uid=1000(app) gid=1000(app) groups=1000(app)
0022

Significado: Dentro del contenedor, el proceso es UID/GID 1000.
Decisión: Debes alinear esto con la propiedad del host (UID 2000) o traducirlo con idmapped mounts.

Task 5: Prueba el fallo con una escritura controlada (contenedor)

cr0x@server:~$ podman exec -it appctr sh -lc 'touch /data/.perm_test && echo ok'
touch: cannot touch '/data/.perm_test': Permission denied

Significado: No es tu app; es el sistema de archivos. Bien—ahora puedes dejar de culpar al código.
Decisión: Continúa con diagnósticos de mapeo en lugar de logs de la aplicación.

Task 6: Compara con una escritura en el host como el usuario esperado

cr0x@server:~$ sudo -u app touch /srv/app/data/.host_test && ls -ln /srv/app/data/.host_test
-rw-r--r-- 1 2000 2000 0 Dec 30 12:01 /srv/app/data/.host_test

Significado: UID 2000 funciona, y los archivos son propiedad como se espera.
Decisión: O ejecutas el contenedor como UID 2000, o mapeas su UID a 2000.

Task 7: Si es rootless, inspecciona los rangos de IDs subordinados

cr0x@server:~$ grep -E '^cr0x:' /etc/subuid /etc/subgid
/etc/subuid:cr0x:100000:65536
/etc/subgid:cr0x:100000:65536

Significado: Rootless user namespaces mapean IDs de contenedor en 100000+ en el host.
Decisión: Si el directorio del host es propiedad de UID 2000, un contenedor rootless mapeado a 100000 no coincidirá. Necesitas idmapped mount o cambiar la propiedad del host.

Task 8: Inspecciona el mapeo de user namespace del proceso del contenedor

cr0x@server:~$ pid=$(podman inspect -f '{{.State.Pid}}' appctr); echo "$pid"; sudo cat /proc/$pid/uid_map
24188
         0     100000      65536

Significado: UID 0 del contenedor es UID 100000 del host; UID 1000 del contenedor es UID 101000 del host.
Decisión: Deja de intentar chown el mount desde dentro del contenedor; no aterrizará en los IDs del host que piensas.

Task 9: Comprueba ACLs que anulen bits de modo

cr0x@server:~$ getfacl -p /srv/app/data | sed -n '1,20p'
# file: /srv/app/data
# owner: app
# group: app
user::rwx
group::r-x
other::---

Significado: No hay sorpresas de ACL aquí.
Decisión: Si ves una ACL que niega escrituras, arregla las ACLs; no toques UID/GID todavía.

Task 10: Comprueba denegaciones de AppArmor (común en Debian)

cr0x@server:~$ sudo journalctl -k -g DENIED --no-pager | tail -n 3
Dec 30 12:03:14 server kernel: audit: type=1400 apparmor="DENIED" operation="open" class="file" profile="containers-default" name="/srv/app/data/" pid=24188 comm="myapp"

Significado: Esto no es UID/GID. AppArmor está bloqueando.
Decisión: Arregla el perfil o el etiquetado de AppArmor; no cambies la propiedad. Si no hay denegaciones, vuelve al mapeo.

Task 11: Confirma las opciones del mount (nosuid/nodev/noexec no son “permission denied”, pero pueden parecerlo)

cr0x@server:~$ findmnt -T /srv/app/data -o OPTIONS
OPTIONS
rw,relatime

Significado: No hay rarezas de noexec.
Decisión: Si tu app falla al ejecutar binarios desde el mount y ves noexec, esa es la solución.

Task 12: Verifica que el usuario efectivo del contenedor coincida con las expectativas (rootful vs rootless)

cr0x@server:~$ podman info --format '{{.Host.Security.Rootless}}'
true

Significado: Eres rootless. Excelente para seguridad y terrible para suposiciones.
Decisión: Planea soluciones que funcionen con el mapeo userns, no en su contra.

Task 13: Reproduce el desajuste de propiedad con listado numérico (contenedor)

cr0x@server:~$ podman exec -it appctr sh -lc 'ls -ldn /data; ls -ln /data | head'
drwxr-x--- 2 2000 2000 4096 Dec 30 12:01 /data
-rw-r--r-- 1 2000 2000 0 Dec 30 12:01 .host_test

Significado: El contenedor ve UID 2000 en los archivos, pero corre como UID 1000. Esa brecha es el problema.
Decisión: Elige entre: (a) cambiar el usuario del contenedor a 2000, (b) cambiar la propiedad del host al ID mapeado, (c) idmapped mount.

Task 14: Confirma root-squash de NFS cuando aplique

cr0x@server:~$ findmnt -T /srv/app/data -o FSTYPE,SOURCE
FSTYPE SOURCE
nfs4   nfs01:/exports/appdata
cr0x@server:~$ sudo -u root touch /srv/app/data/.root_touch 2>&1 | tail -n 1
touch: cannot touch '/srv/app/data/.root_touch': Permission denied

Significado: Probablemente root-squash o política de exportación del servidor. Root en el cliente no es root en el servidor.
Decisión: Arregla la identidad en el servidor NFS (alineación UID/GID), o ajusta las opciones de exportación deliberadamente. No ejecutes todo como root para “arreglarlo”.

Soluciones limpias que puedes defender en una revisión

Hay tres familias “buenas” de soluciones. Elige según tu realidad operativa: host único vs flota, rootless vs rootful,
requisitos de cumplimiento y si el almacenamiento es local o en red.

Familia de soluciones A: Alinear UID/GID del runtime del contenedor con los datos del host

Esta es la solución más simple y aburrida: si /srv/app/data es propiedad del UID 2000, ejecuta el proceso del contenedor como UID 2000.
Funciona con bind mounts, con la mayoría de filesystems y no requiere características avanzadas del kernel.

Con imágenes que lo soportan, establece el usuario de runtime explícitamente. En flujos tipo Compose pondrías user: "2000:2000".
Para Podman:

cr0x@server:~$ podman run -d --name appctr --user 2000:2000 -v /srv/app/data:/data:Z docker.io/library/alpine sleep 1d
...output...

Significado: El proceso corre con el mismo UID/GID que posee el directorio del host.
Decisión: Usa esto cuando controlas el UID de la aplicación y no necesitas los semánticos de “UID 0” en el contenedor.

El problema: si operas en múltiples hosts, debes mantener el UID 2000 consistente en todos. Eso no es opcional. Es el punto entero.

Familia de soluciones B: Estandarizar UID/GID en la flota (identidad respaldada por directorio)

Esto es lo que hacen entornos maduros: asignan cuentas de servicio con IDs numéricos fijos, las gestionan centralmente y hacen el almacenamiento consistente.
Los usuarios locales creados ad-hoc en cada host derivarán. La deriva se convierte en cortes.

Si no puedes centralizar, al menos reserva un rango de UID/GID para identidades de servicio y aplícalo en el aprovisionamiento.
“Recordaremos crear el usuario app con el mismo UID en cada servidor” no es un proceso; es una plegaria.

Familia de soluciones C: Usar idmapped mounts para traducir la propiedad limpiamente

Los idmapped mounts son lo más cercano que tiene Linux a “bind mount pero traducir IDs”. Permiten montar un directorio de modo que la propiedad
se mapea solo para ese mount—sin cambiar la propiedad en disco, sin renumerar global de UIDs.

Esto es especialmente atractivo cuando:

  • Tienes datos existentes propiedad de un UID/GID, pero la imagen del contenedor espera otro.
  • No puedes chown (datasets grandes, cumplimiento, mounts compartidos).
  • Estás ejecutando rootless y quieres que el contenedor vea una propiedad “razonable”.

El cableado exacto varía según el runtime y el soporte del kernel. El principio se mantiene: define un mapeo y aplícalo al mount.
En kernels modernos de Debian, los idmapped mounts son viables, pero no asumas que cada capa de la pila (systemd, runtime de contenedores, orquestación)
lo hará por ti automáticamente.

Qué no hacer (a menos que te gusten los incidentes recurrentes)

  • No hagas chmod 777 a un directorio de datos compartido “para que funcione”. Romperás el principio de menor privilegio y seguirás teniendo deriva de identidad.
  • No hagas chown de terabytes de datos durante un incidente a menos que lo hayas medido y puedas revertir. Es lento, disruptivo y fácil de equivocarse.
  • No “arregles” ejecutándolo todo como root. NFS se burlará, LSM se quejará y tendrás nuevos problemas.

Broma #2: Cada vez que alguien sugiere “solo ejecútalo privilegiado”, un ingeniero de seguridad gana otra cana y la llama con tu nombre.

Tres mini-historias del mundo corporativo

1) Incidente causado por una suposición equivocada: “los nombres coinciden, así que los IDs deben coincidir”

Una empresa mediana ejecutaba un servicio con estado en contenedores a través de varios hosts Debian. La cuenta de servicio se llamaba app
en todas partes. El equipo asumió que eso significaba la misma identidad en todos lados. No fue así.

Un nodo se había instalado antes y tenía algunos usuarios locales extra. En esa máquina, app terminó siendo UID 1002. En nodos más nuevos,
era UID 1001. El almacenamiento era un export NFS montado en /srv/app/data, y NFS almacenaba propiedad numérica, no “la vibra del nombre de usuario”.

El modo de fallo fue sutil: el servicio funcionaba en dos nodos y fallaba en uno con Permission denied al escribir archivos nuevos,
pero podía leer los antiguos. Los ingenieros pasaron horas hurgando en configuraciones de contenedores, luego en opciones de mount de NFS, luego en “tal vez Debian cambió algo”.

El avance vino de alguien haciendo lo poco glamoroso: ls -ln en el directorio de datos desde cada nodo, más id app.
Vieron el desajuste al instante. La solución fue igual de aburrida: estandarizar UID/GID para app vía su pipeline de aprovisionamiento y
corregir la propiedad en el servidor NFS para el UID previsto.

La lección: los nombres de usuario son UI. Los IDs numéricos son el contrato.

2) Optimización que salió mal: “evitar chown cambiando a rootless”

Otra organización quería reducir la superficie de ataque por escapes de contenedor. Cambiaron una parte de sus workloads a contenedores rootless.
Gran movimiento en general. Pero lo trataron como un cambio solo de seguridad y no ejecutaron una comprobación de compatibilidad de almacenamiento.

Sus bind mounts apuntaban a directorios del host propiedad de usuarios de servicio con UIDs bajos. El mapeo rootless desplazó los IDs del contenedor
al rango 100000+. De repente, cargas que antes escribían en /srv comenzaron a fallar. La primera reacción del equipo fue “optimizar”:
quitar comprobaciones de grupo ampliando permisos en directorios. Eso hizo que algunas cargas funcionaran y empeoró los hallazgos de auditoría.

Luego intentaron un arreglo “rápido”: chown recursivo de los directorios del host al rango UID mapeado. Eso funcionó para los contenedores pero rompió
herramientas de mantenimiento del host que asumían que el usuario de servicio poseía sus datos. Ahora tenían dos universos de propiedad, ninguno feliz.

La solución estable eventual fue dejar de tratar la identidad como incidental. Definieron una estrategia explícita de UID/GID para servicios, documentaron
qué workloads eran rootless y usaron una combinación de ejecutar contenedores con UIDs alineados y uso selectivo de idmapped mounts donde los datos heredados no podían moverse.
La “optimización” no fue rootless; fue la gobernanza alrededor de ello.

3) Práctica aburrida pero correcta que salvó el día: comprobaciones preflight en CI para identidad de almacenamiento

Un equipo de plataforma mayor tenía una costumbre que parecía tonta hasta que no lo fue: cada pipeline de despliegue tenía un job preflight que ejecutaba un contenedor
con el usuario de runtime previsto, montaba la ruta del host prevista y realizaba operaciones básicas de sistema de archivos (mkdir, touch, fsync, rename).

El job producía un pequeño artefacto: el UID/GID visto por el contenedor, el UID/GID en el host para el archivo creado y cualquier negación del kernel/LSM.
No era sofisticado. Era consistente. La gente ocasionalmente se quejaba de que añadía minutos y “nunca encuentra nada”.

Entonces una serie de nodos se reconstruyó con un orden de aprovisionamiento de identidad ligeramente distinto. Algunas cuentas de servicio cambiaron asignación de UID.
El preflight lo detectó antes del rollout a producción. El arreglo se hizo en aprovisionamiento, no durante un incidente. No llamadas a medianoche, no chmods de emergencia, no chown de almacenamiento compartido.

Esto es lo que parece confiabilidad: no heroísmo, solo un proceso que hace la falla aburrida y temprana.

Errores comunes: síntoma → causa raíz → solución

1) Síntoma: Funciona en el host, falla en el contenedor con “Permission denied”

Causa raíz: UID/GID del contenedor no coincide con la propiedad del host, a menudo por mapeo userns rootless.
Solución: Ejecuta el contenedor como el UID/GID del directorio del host, o usa idmapped mounts, o ajusta la propiedad del host al ID mapeado intencionalmente.

2) Síntoma: Contenedor rootful aún no puede escribir en bind mount NFS

Causa raíz: NFS root-squash o permisos en el servidor. Root en el contenedor no es especial en el servidor.
Solución: Alinea los IDs numéricos entre clientes y servidor; ajusta las exportaciones NFS deliberadamente; evita “solo ejecútalo como root”.

3) Síntoma: ls -l muestra permiso de escritura, pero las escrituras fallan

Causa raíz: ACL niega o política LSM lo niega (AppArmor/SELinux).
Solución: Inspecciona ACLs con getfacl; revisa negaciones en logs del kernel; arregla la política/perfil en lugar de chmod/chown.

4) Síntoma: Archivos creados en el contenedor aparecen en el host con UIDs raros (100000+)

Causa raíz: Mapeo de user namespace rootless usando /etc/subuid//etc/subgid.
Solución: Espéralo; no lo luches. Usa IDs alineados vía idmapped mount o define correctamente la estrategia de UID de la aplicación.

5) Síntoma: Algunos nodos funcionan, otros fallan, mismo despliegue

Causa raíz: Deriva de UID/GID entre hosts; NFS o almacenamiento compartido lo hace visible.
Solución: Centraliza la identidad o aplica IDs numéricos fijos en el aprovisionamiento; verifica con id y ls -ln entre nodos.

6) Síntoma: Cambiar permisos “arregla” pero rompe después

Causa raíz: Enmascaraste el desajuste de identidad ampliando permisos; más tarde, otro servicio o control de seguridad espera modos más restrictivos.
Solución: Revierta al menor privilegio; arregla el mapeo de identidad; añade una prueba preflight que valide la escribibilidad bajo el UID/GID correcto.

Listas de verificación / plan paso a paso

Plan paso a paso para un único host Debian 13 (lo más común)

  1. Confirma la ruta que falla y el tipo de mount.
    Usa findmnt -T. Si es NFS, salta al plan NFS.
  2. Registra la propiedad del host numéricamente.
    Usa stat -c 'uid=%u gid=%g mode=%a' en la ruta del host.
  3. Registra la identidad del runtime del contenedor.
    Dentro del contenedor: id. También captura si eres rootless.
  4. Comprueba el mapeo userns (especialmente rootless).
    Inspecciona /proc/$pid/uid_map. Si los UIDs están desplazados, asume desajuste hasta probar lo contrario.
  5. Elimina ACLs y denegaciones LSM.
    Ejecuta getfacl y revisa journalctl -k -g DENIED.
  6. Elige una solución:

    • Si controlas el usuario del contenedor: ejecuta el contenedor como UID/GID del host.
    • Si no puedes cambiar el usuario del contenedor: idmapped mount (preferido) o cambio de propiedad controlado.
    • Si es un entorno compartido: estandariza IDs en lugar de parchear un host.
  7. Valida con una prueba de escritura bidireccional.
    Crea un archivo desde el contenedor y verifica su UID/GID en el host; luego crea desde el host y escribe desde el contenedor.
  8. Hazlo permanente.
    Pon las decisiones de UID/GID en gestión de configuración, no en conocimiento tribal.

Plan paso a paso para bind mounts respaldados por NFS

  1. Identifica el servidor NFS y la política de exportación. Confirma expectativas de root-squash.
  2. Verifica consistencia numérica de UID/GID entre todos los clientes. La misma cuenta de servicio debe tener el mismo UID en todas partes.
  3. Arregla la identidad en la fuente. Identidad respaldada por directorio o aprovisionamiento consistente supera hacks locales.
  4. Evita tormentas de chown. Si es necesaria corrección de propiedad, hazla server-side en una ventana controlada.
  5. Vuelve a probar con el UID real de la aplicación. “Funciona como root” no es criterio de éxito en NFS.

Plan paso a paso para contenedores rootless en Debian 13

  1. Confirma el modo rootless. podman info o equivalente del runtime.
  2. Revisa /etc/subuid y /etc/subgid. Asegura que existan rangos y sean suficientemente grandes.
  3. Inspecciona uid_map/gid_map. Sabe qué IDs de contenedor se convierten en el host.
  4. Elige una estrategia de propiedad. O los datos son propiedad de los IDs host mapeados, o usas idmapped mounts para presentar los IDs esperados.
  5. Documenta. Rootless sin documentación de identidad es suscripción a outages.

Preguntas frecuentes (FAQ)

¿Por qué apareció esto después de moverme a Debian 13?

Usualmente porque cambiaste una capa: runtime de contenedores, defaults rootless, conjunto de características del kernel, comportamiento de systemd o el orden de aprovisionamiento de identidad.
La regla subyacente no cambió: el acceso lo aplica los IDs numéricos y la política.

¿Por qué ls -l muestra un nombre de usuario que reconozco, pero el contenedor no puede escribir?

Porque el UID del proceso del contenedor no coincide con el UID numérico que posee el directorio. Los nombres se resuelven vía NSS y pueden diferir por entorno.
Siempre comprueba los IDs numéricos con ls -ln o stat.

¿Puedo simplemente chown -R el directorio para que coincida con el contenedor?

Puedes, pero a menudo es la peor opción: lento en árboles grandes, riesgoso en datos compartidos y consolida mapeos accidentales.
Prefiere alinear UID/GID de runtime o idmapped mounts cuando necesites traducción sin reescribir la propiedad.

¿Cuál es la solución más limpia cuando varios servicios comparten la misma ruta del host?

Estandariza los UIDs/GIDs de cuentas de servicio en la flota y asigna la propiedad de grupo con criterio.
Si los servicios realmente necesitan vistas de propiedad distintas, los idmapped mounts son mejor herramienta que el caos de chmod.

¿Por qué rootless crea archivos con UIDs como 101000 en el host?

Porque los user namespaces mapean los IDs del contenedor en un rango subordinado definido en /etc/subuid//etc/subgid.
El UID 1000 del contenedor se convierte en UID (base subuid + 1000) en el host.

¿AppArmor realmente causa “Permission denied” que parece un problema de sistema de archivos?

Sí. Puede negar operaciones open/create incluso cuando los bits de modo parecen permisivos. Revisa los logs del kernel para negaciones.
Si ves denegaciones de AppArmor, arregla el perfil; no cambies propiedad.

¿Y los volúmenes hostPath de Kubernetes?

Mismo principio: la identidad del proceso del pod debe poder acceder la ruta del nodo. Usa runAsUser/runAsGroup explícitos,
o un paso init controlado, o una clase de almacenamiento que evite hostPath para datos con estado.

¿Es suficiente fsGroup?

A veces. Si los permisos de grupo están correctamente establecidos (g+rwx) y el proceso está en ese grupo, funciona.
Pero no arreglará directorios de solo propietario y no ayudará si el mapeo user namespace impide que el GID coincida.

¿Cómo demuestro que es mapeo UID/GID y no “permisos raros de Debian”?

Usa una prueba bidireccional: crea un archivo dentro del contenedor e inspecciona su propietario numérico en el host. Luego invierte la operación.
Si los IDs no se alinean, el veredicto es desajuste de mapeo.

¿Cuándo debo usar idmapped mounts en lugar de cambiar el usuario del contenedor?

Usa idmapped mounts cuando no puedas cambiar el UID de la aplicación (imagen de vendor), no puedas cambiar la propiedad en disco (compartido o inmenso),
o necesites múltiples vistas del mismo dato con semánticas de propiedad distintas.

Próximos pasos que no te perseguirán

“Permission denied” en un bind mount raramente es un misterio y casi nunca se arregla haciendo los permisos más permisivos.
Trátalo como un problema de mapeo de identidad hasta que se demuestre lo contrario.

Haz esto a continuación, en orden:

  1. Captura UID/GID numérico de la ruta del host y del proceso del contenedor.
  2. Confirma si los user namespaces están desplazando IDs (rootless es el sospechoso usual).
  3. Elimina denegaciones ACL y AppArmor con comprobaciones dirigidas.
  4. Elige una solución limpia: alinear runtime UID/GID, estandarizar IDs en hosts, o usar idmapped mounts para traducción.
  5. Codifícalo en aprovisionamiento y CI con una prueba preflight de escritura.

Si solo haces una cosa: deja de confiar en los nombres de usuario al depurar permisos de almacenamiento. Los IDs numéricos son la verdad, y los bind mounts son honestos hasta la exageración.

← Anterior
DLSS explicado: el mayor truco de FPS de la década
Siguiente →
Conjuntos de datos ZFS por carga de trabajo: el truco de gestión que evita el caos

Deja un comentario