Docker image hardening: non-root, capabilities y filesystem
Controles prácticos para reducir daño si un contenedor se compromete: usuario no-root, capacidades mínimas, filesystem read-only y secretos fuera de la imagen.
Antes de empezar necesitás
- Saber construir una imagen Docker simple
- Entender usuarios y permisos básicos en Linux
Al terminar vas a poder
- Identificar riesgos de correr como root
- Reducir capabilities innecesarias
- Separar secretos de la imagen
- Diseñar un checklist de hardening reproducible
Un contenedor no es una VM mágica. Comparte kernel con el host y tiene permisos. Hardening es reducir qué puede hacer el proceso si algo sale mal.
Dockerfile más seguro
Patrón mínimo:
FROM node:22-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
RUN addgroup -S app && adduser -S app -G app
USER app
CMD ["node", "server.js"]
No alcanza para producción, pero evita correr la app como root dentro del contenedor.
Runtime con menos permisos
docker run --rm \
--read-only \
--cap-drop=ALL \
--tmpfs /tmp \
app-de-juguete:localPuede romper apps que escriben en disco o necesitan capabilities. Eso no es malo: te muestra dependencias ocultas.
Checklist de evidencia
control evidencia
USER no-root Dockerfile con USER app
capabilities runbook con --cap-drop=ALL y excepciones justificadas
filesystem --read-only + tmpfs donde haga falta
secretos no hay .env ni tokens en layers
docker socket no montado salvo caso justificado y defensivo
Lo que practicás en este lab
Llevátelo a tu repo si querés, pero no es obligatorio: es tu aprendizaje.
- Dockerfile vulnerable y versión endurecida
- Checklist de controles aplicados
- Writeup: qué riesgo reduce cada control
Reto
Tomá una imagen de juguete. Proponé un Dockerfile que cree usuario no-root y un comando docker run con cap-drop y read-only. Explicá qué podría romper y cómo lo detectarías.
Resolvelo y escribí dos líneas explicando qué pasó. Con eso lo fijás.