// Engineering Log

Desarrollo seguro: Parte 6 — Seguridad de contenedores

Publicado el 22.09.2026

// Ruta rapida

Este articulo pertenece al tema Seguridad y proteccion.

El contenedor aísla la aplicación del host, pero el aislamiento no es absoluto: todos los contenedores usan un mismo kernel. Una vulnerabilidad en la imagen, un proceso con privilegios root o privilegios excesivos convierten un fallo en la aplicación en un problema para todo el servidor. Esta parte trata sobre qué hacer con una imagen ya construida y cómo ejecutarla. La correcta construcción del Dockerfile (caché, build multi-etapa, fijación por digest) se analiza en la parte 5, la verificación de dependencias — en la parte 4.

De dónde vienen los riesgos

La imagen consiste en capas: la imagen base con los paquetes de la distribución, las dependencias de la aplicación, el propio código. Las vulnerabilidades suelen venir no de su código, sino de la imagen base: un python:3.13 completo o node:22 contiene compilador, gestor de paquetes, shell y cientos de librerías que la aplicación en ejecución no necesita. Cada una de ellas es una potencial vulnerabilidad y una herramienta lista para un atacante si llega dentro del contenedor.

El segundo grupo de riesgos son los parámetros de ejecución: proceso como root, escritura en cualquier parte del sistema de archivos, el conjunto completo de capacidades del kernel (capabilities), ausencia de límites de memoria y número de procesos.

Imagen base mínima

Cuanto menos haya en la imagen, menor será la superficie de ataque y menos hallazgos tendrá el escáner.

  • -slim (python:3.13-slim, node:22-slim) — Debian sin herramientas de compilación ni documentación. Opción razonable por defecto: se mantiene la compatibilidad con glibc y el tamaño es notablemente menor que la imagen completa.
  • Alpine — imagen muy pequeña sobre musl libc. Adecuada para programas compilados estáticamente; para Python y Node.js con módulos nativos pueden surgir problemas de compatibilidad y tiempo de compilación más largo.
  • Distroless de Google — en la imagen solo está el runtime, sin shell ni gestor de paquetes. Las imágenes actuales están basadas en Debian 13: gcr.io/distroless/static-debian13 para binarios estáticos (Go, Rust), gcr.io/distroless/python3-debian13, gcr.io/distroless/nodejs22-debian13 y otras. La mayoría tiene la etiqueta :nonroot — el proceso se ejecuta directamente no como root, y la etiqueta :debug incluye un shell busybox para depuración.

No use versiones sin soporte: Python 3.9 y Node.js 18 ya no reciben correcciones de seguridad. Para imágenes nuevas — Python 3.12 o 3.13, Node.js 22 o 24.

La ausencia de shell en una imagen distroless es a la vez protección e inconveniente: docker exec -it … sh no funcionará. Para analizar problemas use la variante :debug en un entorno de pruebas o un contenedor de depuración separado, en lugar de añadir un shell a la imagen de producción.

Ejecutar sin root

Por defecto el proceso en el contenedor corre como root. Si un atacante supera la aplicación, tendrá privilegios máximos dentro del contenedor y, ante una vulnerabilidad en el runtime, la posibilidad de obtener root en el host.

En el Dockerfile cree un usuario y cámbiese a él:

dockerfile
FROM python:3.13-slim
RUN useradd --system --uid 10001 --no-create-home app
WORKDIR /app
COPY --chown=app:app . .
USER app
CMD ["python", "main.py"]

Para distroless es suficiente con la etiqueta :nonroot. También se puede especificar el usuario al ejecutar: docker run --user 10001:10001 …. Una aplicación sin root no puede escuchar puertos por debajo de 1024 — use el puerto 8080 y publíquelo según corresponda.

Restricciones al ejecutar

Los parámetros de docker run (en Compose — las claves homónimas del servicio) cierran la mayor parte de las vías típicas de escalada de privilegios:

bash
docker run -d --name app \
  --read-only --tmpfs /tmp \
  --cap-drop=ALL \
  --security-opt no-new-privileges \
  --memory 512m --cpus 1 --pids-limit 200 \
  --user 10001:10001 \
  registry.example.ru/app@sha256:...
  • --read-only monta el sistema de archivos raíz en solo lectura: el atacante no podrá sustituir archivos de la aplicación ni escribir herramientas. Los directorios donde la aplicación realmente escribe se montan por separado: --tmpfs /tmp o un volumen para datos.
  • --cap-drop=ALL elimina todas las capacidades del kernel. Si la aplicación necesita algo — por ejemplo, NET_BIND_SERVICE para el puerto 80 — añada exactamente eso mediante --cap-add.
  • --security-opt no-new-privileges impide que los procesos obtengan nuevos privilegios, incluido por su, sudo y archivos con el bit setuid.
  • --memory, --cpus, --pids-limit limitan recursos: una fuga de memoria o una fork-bomb en un contenedor no tumbará todo el servidor.

Nunca ejecute contenedores de producción con --privileged ni monte dentro de ellos el socket de Docker (/var/run/docker.sock): el acceso al socket equivale a privilegios root en el host.

Seccomp y modo rootless

Seccomp filtra llamadas al sistema. El perfil por defecto de Docker funciona como una lista de llamadas permitidas y bloquea alrededor de 44 de más de 300 — las que se usan para ataques al kernel y que casi no necesitan las aplicaciones comunes. La documentación de Docker no recomienda cambiarlo; lo principal es no desactivarlo con la opción --security-opt seccomp=unconfined, que a veces se sugiere en instrucciones «para que funcione».

El modo rootless ejecuta no solo los contenedores, sino también el propio demonio Docker como un usuario normal. Incluso al escapar del contenedor, el atacante obtiene los privilegios de ese usuario, no de root. Para la instalación se requieren las utilidades newuidmap y newgidmap (paquete uidmap) y al menos 65 536 identificadores subordinados en /etc/subuid y /etc/subgid; luego, como usuario, se ejecuta dockerd-rootless-setuptool.sh install. El modo rootless restringe algunas capacidades (red, puertos privilegiados), por lo que vale la pena probarlo en su stack antes de migrar.

Escaneo de imágenes

La imagen construida debe verificarse en busca de vulnerabilidades conocidas antes del despliegue y de forma periódica después: se descubren nuevos CVE en paquetes ya publicados. El mismo Trivy que verifica dependencias (ver parte 4) también escanea la imagen entera:

bash
trivy image --severity HIGH,CRITICAL --exit-code 1 registry.example.ru/app:1.4.2

Con --exit-code 1 el escaneo hace fallar el pipeline cuando hay hallazgos de alto nivel. Los resultados del escaneo suelen ser un orden de magnitud menores en imágenes -slim o distroless que en la imagen completa: es la forma más barata de reducir la lista de vulnerabilidades.

Firma y verificación de imágenes

La firma garantiza que al servidor llega la imagen que construyó su CI y no una sustituida en el registro. La herramienta estándar es cosign del proyecto Sigstore:

bash
cosign generate-key-pair
cosign sign --key cosign.key registry.example.ru/app@sha256:...
cosign verify --key cosign.pub registry.example.ru/app@sha256:...

Se debe firmar y verificar por digest, no por etiqueta: la etiqueta puede apuntar a otra imagen. Cosign también soporta un modo sin claves propias, con confirmación vía un proveedor OIDC. La verificación de la firma tiene sentido solo si está integrada en el despliegue: el script de despliegue o el controlador en Kubernetes rechaza ejecutar una imagen no firmada.

Lista de comprobación

  1. Imagen base -slim o distroless, versión del runtime con soporte, fijación por digest.
  2. USER no root; puerto de la aplicación por encima de 1024.
  3. Ejecución con --read-only, --cap-drop=ALL, no-new-privileges, límites de memoria, CPU y procesos.
  4. Nada de --privileged, socket de Docker dentro del contenedor ni seccomp=unconfined.
  5. Escaneo de la imagen en CI y regularmente para imágenes ya desplegadas.
  6. Firma de la imagen en CI y verificación de la firma en el despliegue.
  7. Donde sea posible — modo rootless de Docker.

Errores típicos

  • «El contenedor es una sandbox». Todos los contenedores comparten el kernel del host; el aislamiento depende de los permisos y límites que usted haya configurado.
  • Root por defecto. El hallazgo más común en auditorías —y el más fácil de corregir.
  • Seccomp desactivado o --privileged usado para depuración, y que queda en la configuración de producción.
  • Escaneo solo en la compilación. Una imagen construida hace seis meses puede haber acumulado decenas de nuevos CVE.
  • Etiqueta latest en producción. No queda claro qué se está ejecutando y no se puede verificar la firma.

// Tarea parecida

Si estas resolviendo algo parecido

Este articulo pertenece a uno de los temas principales de trabajo. Puedes seguir leyendo sobre el tema, ir a la pagina principal para entender a que me dedico o abrir directamente los servicios.

Tema del articulo

Seguridad y proteccion

SSL, hardening, accesos, proteccion de servicios y configuraciones seguras.

Tareas frecuentes de esta tema

  • Configurar SSL, certificados y conexiones seguras
  • Restringir accesos y cerrar puntos de entrada innecesarios
  • Reforzar la configuracion del servidor y los servicios

// Siguiente paso

Si necesitas ayuda con este tema y no solo otro articulo, es mejor ir directo a la pagina del servicio. La pagina principal y la seleccion de materiales quedan como rutas secundarias.

Abrir servicios

// Reviews

Reseñas relacionadas

Había que hacer funcionar n8n, Redis y la base de datos. Lo había encargado antes a otro proveedor; todo se rompía constantemente. Se lo encargué a Mijaíl y al día siguiente todo funcionó rápido, ¡como un reloj!

Había que poner en marcha n8n, redis y la base de datos. Contraté antes a otro proveedor, y todo se rompía constantemente. Lo encargué a Mikhail, y al día siguiente ¡todo empezó a funcionar rápido, como un reloj!

christ_media

Instalación de n8n en su servidor VPS. Configuración de n8n, Docker, IA, Telegram

24.09.2025 · ★ 5/5

Comprador experimentado

ladohinpy

Instalación de n8n en su servidor VPS. Configuración de n8n, Docker, IA, Telegram

25.08.2025 · ★ 5/5

// Contact

¿Necesitas ayuda?

Escríbeme y te ayudaré a resolver el problema

Respondo en un día laborable (03:00-13:00 GMT)

Или оставьте заявку здесь:

Confirme que no es un bot.

Escribir y recibir una respuesta rápida