Capa 5 — Container hardening¶
Qué hace¶
Tres mecanismos combinados reducen drásticamente la superficie de un RCE:
- Imagen distroless (
gcr.io/distroless/static-debian12:nonroot) — sin shell, sinapt/apk, sincurl/wget. Solo el binario qrsgen y los certs CA. - Root filesystem read-only — el binario no escribe a disco; toda la persistencia vive en Postgres. Cualquier mutación es indicio de compromiso.
- Tmpfs en
/tmp(64 MB) — único path escribible, en memoria, se vacía con cada redeploy. - Usuario
nonroot:nonroot— sin capabilities adicionales, no puedechmod,chownni escalar a root.
Configuración¶
services:
qrsgen:
image: qrsgen:0.23.0-rc1 # distroless en Dockerfile.release
read_only: true
volumes:
- type: tmpfs
target: /tmp
tmpfs:
size: 67108864 # 64 MB
Qué mitiga¶
Vector #2 (RCE escalation). Un atacante con código en el proceso:
- No puede instalar herramientas (rootfs read-only + sin package manager).
- No puede persistir un implante (solo /tmp escribible, se vacía con cada redeploy, además limitado a 64 MB y volátil).
- No puede escalar a root (nonroot user, sin capabilities extra).
Cómo verificarla¶
# Intentar escribir en cualquier path fuera de /tmp debe fallar.
sudo docker exec qrsgen_qrsgen.X sh -c 'echo x > /test' 2>&1 || echo "blocked OK"
# (en distroless ni siquiera hay sh, así que se ve doble protección)
# Inspeccionar metadata del container:
sudo docker inspect $(sudo docker ps -q -f name=qrsgen_qrsgen) \
--format '{{.HostConfig.ReadonlyRootfs}}' # debe ser true
Glosario¶
Container hardening: combinación de técnicas para reducir la superficie de ataque de un container. Distroless + read-only + nonroot es el stack típico.
Distroless: imagen Docker mínima sin shell ni paquetes auxiliares. Solo contiene el binario y sus deps. Si comprometen el proceso, no hay shell donde caer.
Rootfs read-only: configuración Docker donde el sistema de archivos del container es de solo lectura. Cualquier intento de escribir falla. qrsgen lo usa porque el binario no necesita escribir nada.
tmpfs: sistema de archivos en memoria. qrsgen monta uno en /tmp
(64 MB) como único path escribible. Se vacía con cada redeploy.
nonroot user: usuario sin privilegios root dentro del container. La imagen distroless lo provee con UID/GID 65532.
Capabilities (Linux): permisos granulares que un proceso puede tener. qrsgen no usa ninguna extra — solo las default mínimas.
Implante: malware que un atacante instala tras comprometer un sistema, para mantener acceso persistente. El rootfs read-only lo previene (no puede escribir).
Superficie de ataque: conjunto de puntos donde un atacante puede intentar romper el sistema. Distroless + read-only la minimiza dramáticamente.
Package manager (apt, apk, yum): herramienta para instalar
software dentro del container. La imagen distroless no lo tiene — el
atacante no puede instalar herramientas.