Problema
En entornos que ejecutan Docker o Kubernetes, el proceso containerd actúa como motor de bajo nivel para crear y gestionar contenedores. Cuando una imagen contiene archivos de usuarios o grupos extremadamente grandes, el parser interno de moby/sys/user intenta leerlos sin aplicar límites de tamaño. El consumo de RAM crece rápidamente, el kernel dispara el OOM killer y termina con containerd. La API de runtime desaparece, lo que impide crear nuevos contenedores y, en clústers, deja fuera de línea al nodo de control de Kubernetes. El impacto es de disponibilidad: los pods que dependan de ese nodo fallan, y el plano de control pierde capacidad de programar workloads hasta que se reinicie el proceso o el nodo.
Este patrón no es exclusivo de una versión concreta; cualquier runtime que delegue la deserialización de /etc/passwd y /etc/group dentro de la capa de la imagen está expuesto. Por eso, aunque el CVE‑2026‑47262 está catalogado con CVSS 6.5, la repercusión práctica puede ser crítica en despliegues de alta disponibilidad.
Causa
Parsing sin límites
El código de moby/sys/user recorre los archivos de usuarios y grupos extraídos de la capa de la imagen para construir la tabla de UID/GID dentro del contenedor. No hay verificación de longitud ni de número de entradas, lo que permite que una imagen con miles de líneas provoque una asignación de memoria proporcional.
Falta de validación en la cadena de suministro
- Imágenes no firmadas: los registries públicos pueden alojar imágenes creadas por actores malintencionados.
- Importación directa: herramientas como
ctr images importodocker loadaceptan cualquier tarball sin inspección previa. - CI/CD sin escaneo: pipelines que construyen imágenes a partir de dependencias externas pueden arrastrar este tipo de payload sin notarlo.
Configuración predeterminada del runtime
containerdse ejecuta como proceso del sistema sin restricciones de cgroup específicas, lo que permite que su consumo de memoria alcance el total disponible.- Los sistemas basados en systemd no definen un slice con límites de RAM para el daemon, por lo que el OOM killer actúa solo cuando el nodo está bajo presión.
Solución
1. Aplicar los parches oficiales
Los proyectos Docker y containerd publicaron versiones corregidas que introducen límites de tamaño al parser:
| Versión afectada | Versión corregida |
|---|---|
| 1.7.x | ≥ 1.7.33 |
| 2.0.x | ≥ 2.0.10 |
| 2.1.x | ≥ 2.1.9 |
| 2.2.x | ≥ 2.2.5 |
| 2.3.x | ≥ 2.3.2 |
En RHEL/OpenShift la actualización se distribuye mediante RHSA‑2026:35111. Ejecutar el gestor de paquetes del SO es suficiente:
sudo dnf update containerd docker-ce
En distribuciones basadas en Debian/Ubuntu:
sudo apt-get update && sudo apt-get install --only-upgrade containerd docker-ce
2. Restringir la superficie de importación
- Whitelist de registries: configura Docker y Kubernetes para aceptar imágenes solo de dominios de confianza (
/etc/docker/daemon.jsonyimagePolicyWebhook). - Firma de imágenes: utiliza Notary v2 o
cosignpara validar la integridad antes de la importación. - Admission controller: en Kubernetes, habilita
ImagePolicyWebhookque rechaza imágenes que superen un umbral de tamaño de capas o que contengan archivos de usuarios mayores a X KB.
3. Limitar recursos del daemon
Crear un slice de systemd con límite de RAM evita que containerd agote toda la memoria del host:
sudo mkdir -p /etc/systemd/system/containerd.service.d
cat <<'EOF' | sudo tee /etc/systemd/system/containerd.service.d/override.conf
[Service]
MemoryMax=2G
EOF
sudo systemctl daemon-reload
sudo systemctl restart containerd
Ajusta MemoryMax según la carga típica del nodo; 2 GB es un punto de partida razonable para la mayoría de clusters.
4. Monitoreo y alertas
- Prometheus: añade la métrica
containerd_process_resident_memory_bytesy crea una regla de alerta cuando supere el 80 % del límite configurado. - Logs: habilita
containerden modo debug (--log-level=debug) para capturar intentos de parsing fallidos.
5. Pruebas de regresión
Incluye en el pipeline de CI una prueba que construya una imagen con un archivo /etc/passwd de 10 MB y verifique que la creación del contenedor falla de forma controlada (se espera un error de “memory limit exceeded”). Esto garantiza que futuras actualizaciones no reintroduzcan la vulnerabilidad.
Cuándo aplicar esta solución
- Síntomas:
containerddesaparece del puerto 2375/2376,docker infomuestra “Cannot connect to the Docker daemon”, o los pods del nodo entran en estadoPendingcon mensaje “FailedCreatePodSandBox”. - Entornos: cualquier clúster que use Docker como runtime, o que ejecute
containerddirectamente (por ejemplo, CRI‑O con compatibilidad). - No aplica: si el nodo ya está configurado con un runtime que no usa
moby/sys/user(por ejemplo,cri-osin la capa de parsing) y la versión del runtime está por debajo del rango vulnerable.
Código
# 1. Actualizar paquetes vulnerables
sudo dnf update containerd docker-ce # RHEL / Fedora
# sudo apt-get update && sudo apt-get install --only-upgrade containerd docker-ce # Debian / Ubuntu
# 2. Crear slice de systemd con límite de RAM
sudo mkdir -p /etc/systemd/system/containerd.service.d
cat <<'EOF' | sudo tee /etc/systemd/system/containerd.service.d/override.conf
[Service]
MemoryMax=2G
EOF
sudo systemctl daemon-reload
sudo systemctl restart containerd
# 3. Configurar whitelist de registries (Docker)
cat <<'EOF' | sudo tee /etc/docker/daemon.json
{
"insecure-registries": [],
"registry-mirrors": [],
"allow-nondistributable-artifacts": [],
"disable-legacy-registry": true,
"disable-legacy-registry": true,
"default-runtime": "runc",
"runtimes": {
"runc": {
"path": "runc"
}
}
}
EOF
sudo systemctl restart docker
Verificación
-
Comprobar versión
containerd --version docker version --format '{{.Server.Version}}'La salida debe mostrar una versión igual o superior a la lista de correcciones.
-
Validar límite de memoria
systemctl show containerd -p MemoryCurrent -p MemoryMaxMemoryMaxdebe reflejar el valor configurado (p. ej.2G). -
Test de imagen maliciosa
- Construye una imagen con un
/etc/passwdde 20 MB. - Intenta crear un contenedor:
docker run --rm malicious:latest. - El proceso debe fallar con un error de “memory limit exceeded” y containerd debe permanecer activo.
- Construye una imagen con un
-
Monitorizar
- En Prometheus, verifica que la métrica
containerd_process_resident_memory_bytesnunca supera el 80 % del límite. - Revisa los logs de
containerdpara asegurarte de que no aparecen mensajes de OOM.
- En Prometheus, verifica que la métrica
Notas adicionales
- Limitar la memoria del daemon protege al host, pero no sustituye la actualización del código vulnerable. Siempre aplique los parches tan pronto como estén disponibles.
- La firma de imágenes añade una capa de confianza, pero requiere gestión de claves y políticas de rotación; evalúe el coste operativo antes de adoptarla.
- En clusters con alta rotación de pods, considere habilitar
PodSecurityAdmissionpara forzarrunAsNonRooty reducir la exposición a archivos de usuarios dentro de la imagen. - Si su infraestructura usa CRI‑O, revise su versión; a partir de 1.28 incluye la misma mitigación que containerd.
- Mantenga una lista de “imágenes de base” aprobadas y escanee periódicamente con herramientas como Trivy o Clair; esto detecta no solo este patrón, sino también otras vulnerabilidades de capas.