Rootless Docker, user namespaces y capabilities | Nicolás Garzón
Texto
Copiar rootless → daemon y runtime sin root del host
user namespace → UID 0 interno mapeado a UID no privilegiado externo
capabilities → privilegios root divididos en permisos específicosNinguno es una solución completa. Se combinan con usuario no root, seccomp, MAC, mounts mínimos, patching y seguridad de la aplicación.
docker
Copiar RUN groupadd --gid 10001 app \
&& useradd --uid 10001 --gid app --no-create-home --shell /usr/sbin/nologin app
USER 10001:10001Esto evita ejecutar la aplicación como UID 0 dentro del namespace.
menos acceso a archivos internos;
menor impacto de vulnerabilities;
ownership más explícito;
evita instalaciones/modificaciones accidentales.
capabilities pueden ampliar privilegios;
mounts pueden exponer archivos accesibles;
Docker socket domina el riesgo;
un kernel vulnerable sigue compartido.
Un user namespace permite que IDs internos y externos sean diferentes.
Texto
Copiar container UID 0
→ host UID 100000Desde dentro, el proceso parece root para ciertos recursos del namespace. En el host corresponde a un usuario sin privilegios normales.
Si un proceso atraviesa otra frontera o escribe en storage, el UID externo tiene menos capacidad que root real.
ownership numérico en bind mounts;
rangos subordinate UID/GID;
compatibilidad de volumes;
devices y recursos del host;
herramientas que asumen IDs iguales;
backups con ownership inesperado.
Docker Engine puede configurar remapping para containers administrados por un daemon rootful.
Esto difiere de rootless:
daemon puede seguir como root;
procesos de containers usan mappings;
ciertos recursos del daemon siguen privilegiados.
Verifica configuración con:
No cambies remapping en un host con datos sin planificar migración: el ownership del data root y volumes puede cambiar.
Rootless ejecuta daemon y containers dentro del user namespace de un usuario no root.
Texto
Copiar usuario nicolas
→ dockerd rootless
→ container root interno
→ IDs no privilegiados en hostReduce impacto de una vulnerabilidad en daemon/runtime porque el proceso externo no posee root real.
Rootless puede depender de:
subordinate UID/GID;
user namespaces habilitados;
herramientas de networking user-space;
cgroup v2 para controles completos;
storage driver compatible;
sesión/servicio del usuario.
Las instrucciones exactas dependen del sistema y versión. Sigue documentación oficial y política del host.
Bind de puertos bajos puede requerir configuración del sistema o un reverse proxy externo.
Texto
Copiar container rootless escucha 8080
→ proxy/rootful load balancer expone 443Puede utilizar forwarding user-space y presentar diferencias de rendimiento o source IP.
Sin configuración adecuada, límites pueden no aplicarse como esperas.
Drivers y filesystem disponibles pueden variar. Prueba rendimiento y compatibilidad.
Acceso a devices del host es limitado y requiere permisos reales del usuario.
El daemon puede depender de la sesión o servicios del usuario. Diseña startup tras reboot, logs y ownership.
Un proceso todavía puede:
leer secrets entregados;
atacar services alcanzables;
consumir recursos dentro de límites disponibles;
explotar el kernel;
modificar bind mounts permitidos;
filtrar datos.
Rootless reduce blast radius en el host, no corrige el workload.
Linux divide privilegios tradicionalmente asociados a root.
NET_BIND_SERVICE: bind a puertos privilegiados;
CHOWN: cambiar ownership;
SETUID/SETGID: cambiar identidad;
NET_ADMIN: administrar red;
SYS_ADMIN: conjunto muy amplio y peligroso.
Docker conserva un conjunto por defecto en configuraciones normales. Para hardening:
Bash
Copiar docker run \
--cap-drop ALL \
--cap-add NET_BIND_SERVICE \
my-proxyUna API en 3000 puede usar:
Bash
Copiar docker run --cap-drop ALL my-apisi todas sus operaciones funcionan sin capabilities.
Linux maneja sets como permitted, effective, inheritable, bounding y ambient. No necesitas memorizar cada bit para usar Docker, pero debes comprender que una capability puede:
estar disponible para el proceso;
heredarse o no;
quedar limitada por bounding set;
interactuar con exec y user IDs.
Bash
Copiar docker exec container sh -c 'grep Cap /proc/1/status' O capsh en una image de diagnóstico.
A menudo se describe como “la nueva root” por su alcance amplio. Añadirla para resolver mounts, FUSE o errores genéricos puede anular gran parte del hardening.
Busca una alternativa específica:
configuración del host;
helper aislado;
service dedicado;
device/mount exacto;
arquitectura diferente.
Permite modificar interfaces, rutas y reglas dentro del namespace, pero puede ser poderosa en combinación con host networking u otras configuraciones.
No la entregues a una app web normal.
Permite bind a puertos bajos. Alternativas:
escuchar en 8080 y publicar host 80;
reverse proxy;
ajustar política rootless.
En containers, el proceso no necesita escuchar internamente en 80 solo porque el host publica 80:
Bash
Copiar docker run -p 80 :8080 my-apiBash
Copiar docker run \
--security-opt no-new-privileges \
my-apiEvita adquirir privilegios nuevos mediante exec. Combínalo con cap-drop y usuario no root.
Texto
Copiar image USER 10001
volume contiene archivos UID 999
init job que ajusta ownership;
IDs coordinados;
opciones del storage;
permisos de grupo;
diseño donde la aplicación no necesita escribir.
Rootless añade mappings externos. Revisa tanto identidad interna como representación en host.
YAML
Copiar services :
api :
image : my- api
user : "10001:10001"
cap_drop : [ ALL]
security_opt :
- no- new- privileges: true
read_only : true
tmpfs :
- /tmp: size=64m, noexec, nosuid
Engine rootless o userns remap;
reverse proxy separado para 443;
volume con ownership compatible;
cgroup v2 y límites verificados.
Bash
Copiar docker run --rm my-api id Bash
Copiar docker inspect container --format '{{json .HostConfig.CapDrop}}'
docker inspect container --format '{{json .HostConfig.CapAdd}}' Bash
Copiar docker info | grep -i rootlessPrueba lectura/escritura real en cada mount.
Confirma que memory/CPU limits se aplican, no solo que el comando fue aceptado.
Hardening perdido. Revisa configuración efectiva.
El usuario externo no posee el path. Ajusta permisos sin hacer world-writable.
El problema puede ser seccomp, MAC, device o filesystem. No escales a privileged sin evidencia.
Publica host 80 hacia container 8080 en lugar de dar privilegios internos.
Builds pueden diferir en storage/network; prueba cache, secrets y multi-platform.
User namespaces pueden mapearlo, pero revisa configuración real.
Reduce privilegios externos; kernel sigue siendo frontera.
Introduce un permiso demasiado amplio.
La base cambia UID y rompe volumes.
La app falla en producción. Ejecuta integración con hardening activo.
Usuario non-root, user namespaces y rootless actúan en capas distintas.
Rootless reduce privilegios del daemon en host.
User namespace mapea IDs internos a externos.
Capabilities deben eliminarse y agregarse una por una.
SYS_ADMIN es especialmente peligrosa.
Ownership y cgroups deben probarse con el workload real.
Comprueba lo aprendido
Explica la diferencia entre rootless y userns-remap.
¿Por qué una API en container no necesita NET_BIND_SERVICE para publicar host 80?
Diseña ownership para un volume usado por UID 10001.
¿Qué investigarías antes de añadir SYS_ADMIN?
¿Qué amenazas siguen existiendo en rootless mode?
Límites de recursos y cgroups , donde se controla consumo sin confundir límites con reservas o capacidad garantizada.