Tema 15

15. Contenedores seguros: imágenes, privilegios, namespaces y hardening

Los microservicios suelen ejecutarse en contenedores, y eso cambia la forma en que se modelan privilegios, aislamiento y superficie de ataque. Un contenedor no es una frontera mágica: si se construye con imágenes débiles o se ejecuta con privilegios excesivos, el riesgo se traslada directamente al runtime.

Objetivo Reducir superficie de ataque en el runtime
Enfoque Imágenes mínimas y privilegios acotados
Resultado Contenedores más predecibles y menos peligrosos

15.1 Introducción

Los contenedores simplifican empaquetado, despliegue y portabilidad, pero no eliminan el problema de seguridad. En realidad, trasladan parte del riesgo al proceso de construcción de imágenes y al entorno de ejecución. Una imagen demasiado grande, un contenedor con privilegios excesivos o una mala exposición del filesystem pueden convertir un microservicio en una puerta de entrada útil para un atacante.

Por eso la seguridad de contenedores no debe limitarse a "funciona en Docker". Hay que revisar qué se empaqueta, con qué usuario corre, qué capacidades del kernel conserva, qué recursos monta y qué aislamiento real existe frente al host y frente a otros contenedores.

Este tema aborda el hardening del contenedor como unidad de runtime. En los temas siguientes eso se extenderá a Kubernetes y a las políticas del clúster.

15.2 Qué protege y qué no protege un contenedor

Un contenedor aísla procesos, filesystem, red y otros recursos mediante mecanismos del kernel, pero ese aislamiento no es absoluto. No debe pensarse como si fuera una máquina virtual completamente independiente.

Un contenedor protege mejor cuando:

  • Corre con privilegios mínimos.
  • Usa imágenes reducidas y controladas.
  • Evita montajes peligrosos y capacidades innecesarias.
  • Se ejecuta dentro de una plataforma con controles adicionales.
Un contenedor no compensa una mala imagen ni un runtime sobreprivilegiado. El aislamiento efectivo depende de cómo se lo construye y ejecuta.

15.3 Imágenes como superficie de ataque

La seguridad del contenedor empieza mucho antes de arrancarlo. Empieza en la imagen. Cada paquete extra, intérprete innecesario, herramienta de depuración o dependencia sin revisar amplía superficie de ataque y complejidad de parcheo.

Una imagen demasiado amplia puede incluir:

  • Binarios que el servicio nunca usa.
  • Librerías vulnerables o sin soporte.
  • Shells o utilidades que facilitan post-explotación.
  • Archivos temporales o secretos heredados del build.

15.4 Imágenes mínimas y reproducibles

Una buena práctica es construir imágenes lo más pequeñas y específicas posible. Esto reduce superficie de ataque, acelera distribución y simplifica auditoría. También conviene que el proceso de build sea reproducible y que la procedencia de la imagen base sea confiable.

Práctica Qué aporta Riesgo que reduce
Base mínima Menos componentes en runtime Menor superficie vulnerable
Multi-stage build Evita dejar herramientas de compilación en producción Menos ruido y menor post-explotación
Origen confiable Mejor trazabilidad de dependencias Menor riesgo de imagen contaminada

15.5 Ejecutar como no root

Uno de los principios más importantes del runtime es evitar que el proceso principal del contenedor corra como root. Aunque el contenedor tenga cierto aislamiento, ejecutar como root incrementa el daño potencial si el proceso es comprometido.

Cuando el servicio corre con un usuario no privilegiado:

  • Se reducen acciones posibles dentro del contenedor.
  • Disminuye el impacto de ciertos escapes o abusos.
  • Se fuerza a declarar mejor qué recursos realmente necesita escribir o montar.

15.6 Privileged containers y por qué evitarlos

Un contenedor privilegiado recibe acceso amplio al host y al kernel, perdiendo gran parte del aislamiento esperado. En microservicios de aplicación general esto casi nunca debería ser necesario.

Permitir privilegios amplios por comodidad operativa es una mala práctica, porque transforma un compromiso de aplicación en un riesgo mucho más cercano al host o al nodo donde corre.

15.7 Linux capabilities

Las capabilities permiten granular privilegios que antes se asociaban enteramente al usuario root. Esto es útil porque permite entregar menos permisos al proceso del contenedor.

Desde el punto de vista de hardening, lo importante es:

  • Quitar todas las capabilities innecesarias.
  • Agregar solo las estrictamente requeridas por el proceso.
  • Entender que cada capability extra amplía superficie de abuso.

15.8 Namespaces y aislamiento

Los namespaces son parte del mecanismo que aísla recursos entre procesos y contenedores: PID, red, mount, usuarios y otros ámbitos. Son fundamentales, pero no deberían interpretarse como garantía suficiente por sí solos.

Un buen hardening asume que el aislamiento del namespace necesita apoyo de otras capas: privilegios mínimos, restricciones de syscalls, políticas de plataforma y buena segmentación de red.

15.9 Filesystem de solo lectura y volúmenes

Si un proceso no necesita escribir en el filesystem raíz del contenedor, conviene ejecutarlo como read-only. Esto reduce la capacidad de modificar binarios, dejar herramientas persistentes o alterar archivos de configuración en runtime.

Cuando sí se necesitan escrituras, conviene acotar explícitamente dónde:

  • Directorios temporales específicos.
  • Volúmenes bien delimitados.
  • Montajes con permisos mínimos.

15.10 Seccomp, AppArmor y perfiles de runtime

Los mecanismos como seccomp y AppArmor permiten restringir llamadas al sistema y comportamientos del proceso en runtime. No siempre se configuran manualmente desde cero, pero es importante entender su propósito: reducir qué puede hacer el proceso aun si es comprometido.

Esto ayuda a limitar:

  • Syscalls innecesarias.
  • Accesos indebidos a ciertos recursos del sistema.
  • Comportamientos anómalos que deberían ser bloqueados tempranamente.

15.11 Secretos en contenedores

Los contenedores consumen secretos, pero no deberían cargarlos dentro de la imagen. Si un secreto viaja en el Dockerfile, en layers, en archivos copiados o en variables mal controladas, la exposición puede quedar persistida mucho más allá del runtime.

Las buenas prácticas aquí se alinean con el tema anterior:

  • No bakear secretos en la imagen.
  • Inyectarlos en runtime con controles adecuados.
  • Evitar que queden visibles en logs o artefactos temporales.

15.12 Recursos y límites

Definir límites de CPU, memoria y otros recursos no es solo una cuestión de costo o performance. También ayuda a contener daño. Un contenedor sin límites claros puede consumir recursos excesivos y afectar estabilidad de otros workloads en el mismo nodo.

Esto es relevante tanto para errores de software como para abuso intencional o comportamientos inesperados bajo carga.

15.13 Observabilidad del runtime

Hardening no termina en la configuración inicial. También hay que observar comportamiento del contenedor en ejecución. Procesos inesperados, escrituras fuera de patrón, conexiones anómalas o cambios en el sistema de archivos pueden ser señales valiosas de compromiso o mala configuración.

Conviene monitorear al menos:

  • Uso de recursos anómalo.
  • Procesos no esperados.
  • Actividad de red inusual.
  • Errores de permisos o violaciones de perfil.

15.14 Errores comunes

  • Usar imágenes base grandes y antiguas por comodidad.
  • Ejecutar como root sin necesidad real.
  • Montar volúmenes sensibles con permisos excesivos.
  • Conceder privilegios elevados para evitar resolver un problema puntual de configuración.
  • Asumir que porque algo corre en contenedor ya está suficientemente aislado.
Un contenedor seguro no es el que tiene más opciones activadas, sino el que tiene menos superficie, menos privilegios y más claridad sobre lo que realmente necesita.

15.15 Qué debes recordar de este tema

  • La seguridad del contenedor empieza en la imagen y continúa en el runtime.
  • Conviene usar imágenes mínimas, reproducibles y de origen confiable.
  • Ejecutar como no root y quitar privilegios innecesarios es fundamental.
  • Filesystem readonly, capabilities mínimas y perfiles de runtime mejoran contención.
  • El contenedor no reemplaza otras capas de aislamiento y observabilidad.

15.16 Conclusión

El contenedor es la unidad práctica donde termina corriendo gran parte del sistema, y por eso su seguridad impacta directamente en la postura general de la arquitectura. Cuando se construyen imágenes mínimas, se acotan privilegios y se endurece el runtime, el daño potencial de un compromiso disminuye y la operación gana previsibilidad.

En el próximo tema estudiaremos seguridad en Kubernetes: pods, RBAC, network policies y admission control para ampliar este hardening desde el contenedor individual hacia el clúster completo.