- Los contenedores comparten kernel con el host, por lo que su aislamiento depende de namespaces, cgroups y del endurecimiento del sistema.
- Podman aporta seguridad extra frente a Docker clásico al evitar un daemon root central y facilitar la ejecución rootless.
- Las redes bridge, host y rootless (slirp4netns, pasta) en Podman permiten ajustar el equilibrio entre conectividad y aislamiento.
- Combinar KVM, contenedores y buenas prácticas (rootless, MAC, seccomp, escaneo de imágenes) ofrece una plataforma robusta para producción.
Cuando empiezas a montar un entorno serio de contenedores en Linux, enseguida aparece la misma duda: ¿hasta qué punto es seguro apoyarse solo en contenedores y cuándo conviene subir el nivel con KVM o micro‑VMs? En entornos corporativos, donde se mezclan Docker, Podman, LXC, máquinas virtuales KVM y distintos hipervisores, entender bien el modelo de aislamiento es clave para no meter la pata.
Además, no son raros los escenarios algo enrevesados, como intentar usar Docker Desktop o Podman dentro de una máquina virtual en VirtualBox o KVM, donde entran en juego varios niveles de virtualización (anidada o no) y es fácil encontrarse con errores tipo “KVM no está habilitado en el host”. Vamos a ordenar todas estas piezas: contenedores, VMs, Podman, KVM, redes y seguridad, y ver cómo combinarlas para conseguir una virtualización realmente segura y práctica.
Virtualización clásica con KVM y máquinas virtuales
La base de todo este tinglado sigue siendo la virtualización de sistemas operativos completa mediante hipervisores como KVM, Xen o ESXi. Aquí el hardware físico ejecuta un hipervisor que se encarga de “partir” la máquina en varias máquinas virtuales independientes.
En este modelo, cada máquina virtual arranca su propio kernel y su propio sistema operativo invitado, totalmente separado del host. El hipervisor (por ejemplo KVM, integrado en el kernel de Linux) actúa como guardián entre el hardware y las VMs, y es el responsable del aislamiento fuerte entre ellas.
Hablamos generalmente de dos tipos de hipervisor: los de tipo 1 o bare metal, que van directamente sobre el hardware, y los de tipo 2, alojados sobre un sistema operativo existente. VirtualBox o VMware Workstation serían de tipo 2, mientras que un KVM gestionado por libvirt en un servidor Linux se acerca más a un escenario de tipo 1, aunque comparta espacio con el propio host.
El mayor beneficio de este enfoque es que cada VM tiene su pila completa: kernel, espacio de usuario, servicios, red, almacenamiento. Esto aporta mucha flexibilidad y la posibilidad de ejecutar distintos sistemas operativos y versiones de kernel en la misma máquina física.
Para gestionar estas plataformas virtualizadas en GNU/Linux se recurre casi siempre a libvirt como API de orquestación, encima de la cual se apoyan herramientas gráficas y de línea de comandos, o proyectos más grandes como OpenStack, que monta nubes completas combinando VMs, bare metal y contenedores.
Requisitos de hardware y problemas habituales con KVM
Antes de meterse a ejecutar KVM o a usar soluciones que dependen de él (como algunas configuraciones de Docker Desktop o de micro‑VMs), hay que asegurarse de que el procesador soporta virtualización por hardware (VT‑x en Intel o SVM/AMD‑V en AMD) y que está activada en la BIOS/UEFI.
En Linux tenemos una comprobación rápida: si el comando grep -E ‘svm|vmx’ /proc/cpuinfo no devuelve nada, o la opción sigue desactivada en firmware, KVM no va a funcionar. Además, en escenarios donde correremos VMs dentro de otra VM (virtualización anidada), el hipervisor de primer nivel tiene que exponer explícitamente esas extensiones al huésped.
Cuando algo falla, aparecen mensajes típicos como el famoso popup de Docker Desktop en RHEL 9 dentro de VirtualBox: “KVM no está habilitado en el host”. Aquí el problema no está en Docker ni en RHEL directamente, sino en que la VM de VirtualBox no recibe las extensiones de virtualización necesarias para montar KVM en su interior.
Comandos como modprobe kvm o modprobe kvm_amd / kvm_intel deberían cargar los módulos del kernel sin mensajes de error. Si no aparece nada relevante en dmesg, o lsmod | grep kvm solo muestra kvm “pelado” e irqbypass y faltan los módulos específicos de la CPU, es muy probable que la virtualización por hardware no se esté exponiendo al sistema invitado o esté desactivada en firmware.
En estos escenarios, aunque se active todo lo posible dentro del huésped, si VirtualBox no ofrece virtualización anidada o está mal configurada, KVM simplemente no se va a poder usar. La solución pasa por revisar la configuración del hipervisor anfitrión (VirtualBox, VMware, etc.) y habilitar nested virtualization si está disponible.
Contenedores: virtualización ligera a nivel de sistema operativo
Frente a las VMs tradicionales, los contenedores proponen otra vía: no se emula todo el hardware, sino que se reutiliza el kernel del host y se aíslan procesos y recursos mediante funciones del kernel de Linux.
Un contenedor no deja de ser un conjunto de procesos encapsulados usando namespaces y cgroups. Dentro de ese “entorno”, la aplicación cree que corre en su propia máquina, con su sistema de ficheros, sus interfaces de red y sus PIDs, pero en realidad comparte kernel con el host.
Los contenedores ofrecen un rendimiento casi nativo, porque no hay un segundo kernel por medio ni emulación de hardware completa. El overhead es pequeño: lo justo para montar los espacios de nombres, restringir recursos con cgroups y aplicar políticas de seguridad (seccomp, AppArmor, SELinux, etc.).
Ahora bien, este diseño trae una consecuencia importante: todas las cargas comparten el mismo kernel físico. Una vulnerabilidad grave en ese kernel puede convertirse de golpe en un posible vector de escape para todos los contenedores que se ejecutan en ese nodo.
Por eso se suele hablar de que el aislamiento de contenedores no es “absoluto” como el de una máquina virtual. Si alguien consigue explotar un bug crítico del kernel, los namespaces dejan de ser un muro efectivo y se pueden romper los límites entre contenedores y host.
Namespaces y cgroups: la base del aislamiento en contenedores
En Linux, el aislamiento de contenedores se construye sobre dos pilares principales: namespaces para separar vistas del sistema y cgroups para limitar y contabilizar recursos.
Los namespaces aíslan cosas como PIDs, red, puntos de montaje, IPC, hostname (UTS) y usuarios. De este modo, un proceso solo “ve” los procesos, interfaces de red, sistema de ficheros y nombre de host de su propio namespace, lo que le da la sensación de estar en un sistema diferente.
Por su parte, los cgroups permiten marcar cuánta CPU, memoria, I/O de disco, número de procesos, etc., puede consumir un contenedor. Así se evita que una carga descontrolada sature todo el host, algo esencial cuando se comparten nodos entre equipos o proyectos.
Sin embargo, conviene tener muy claro que namespaces y cgroups por sí solos no son un sistema de seguridad completo. No bloquean la explotación de fallos del kernel, no filtran llamadas al sistema ni gestionan políticas de acceso finas. Son la base, pero hay que reforzarla.
La manera sensata de endurecer el entorno pasa por combinar filtros de syscalls con seccomp, políticas MAC (AppArmor o SELinux) y una reducción drástica de capacidades de los procesos, idealmente junto con namespaces de usuario y modo sin root (rootless) para minimizar el daño si alguien consigue salir del contenedor.
Amenazas de seguridad en plataformas de contenedores
En una plataforma real de contenedores, la superficie de ataque se reparte en varias capas: el kernel y el runtime (runc, containerd, CRI‑O), la cadena de suministro de imágenes, la configuración de contenedores/orquestadores y la infraestructura subyacente (nube, almacenamiento, IAM, red).
Los vectores de ataque habituales incluyen vulnerabilidades de software en el kernel o en el runtime del contenedor, usar imágenes inseguras o sin mantenimiento, cometer errores de configuración en Kubernetes o Docker (contenedores privilegiados, montajes peligrosos del host, modos de red excesivamente abiertos), y fallos de seguridad en registros o pipelines de CI/CD.
En muchísimos incidentes reales no hay un “bug mágico” aislado, sino una combinación de vulnerabilidad conocida más configuración laxa: contenedores corriendo como root, uso del flag –privileged, montajes del sistema de ficheros del host dentro del contenedor y ausencia de seccomp o MAC.
A todo eso se suma la cuestión de la cadena de suministro: las imágenes base públicas suelen arrastrar CVEs, paquetes obsoletos y dependencias poco controladas. Es relativamente fácil que se cuele malware o que una librería comprometida quede latente hasta que se den las condiciones para ejecutarse.
Por ello, en producción no es opcional: hay que escanear imágenes buscando vulnerabilidades con herramientas como Trivy, Clair o Grype, controlar qué registros se usan (Harbor, Quay, GHCR con políticas) y firmar/verificar las imágenes antes de desplegarlas en clúster.
Docker vs Podman: impacto en el modelo de amenazas
Dentro del mundo de contenedores de aplicaciones, Docker y Podman cubren funciones parecidas, pero su arquitectura difiere bastante y eso cambia el modelo de seguridad con el que trabajamos en un host.
Docker, en su modo clásico rootful, se apoya en un demonio central (dockerd) que corre como root. La CLI se comunica con ese demonio a través de /var/run/docker.sock o mediante TCP, y es este proceso privilegiado el que crea y destruye contenedores, gestiona imágenes, redes y volúmenes, y habla con los registros.
Esto implica que quien tenga acceso al socket de Docker prácticamente tiene root sobre el host, porque puede lanzar contenedores privilegiados, montar sistemas de ficheros arbitrarios y tocar configuración crítica. El demonio se convierte, de facto, en un punto único de fallo.
Podman, por el contrario, nació con la idea de ser un motor de contenedores sin daemon y lo más “nativo” posible en Linux. No hay un proceso central permanente; los contenedores cuelgan del usuario o de systemd, y la integración con este último permite controlar contenedores mediante unidades de servicio.
Este enfoque encaja mejor con el modelo Unix de “cada proceso pertenece al usuario que lo lanza” y evita depender de un demonio de altos privilegios. Además, Podman ofrece compatibilidad con comandos de Docker, simplificando la migración (incluso se puede hacer alias docker=podman en entornos donde no queremos instalar Docker CE).
Modo rootless y espacios de nombres de usuario
Uno de los grandes avances prácticos para reducir el impacto de un posible escape de contenedor es el modo sin raíz (rootless) combinado con namespaces de usuario. Aquí la pregunta que se intenta responder es: “¿Qué pasa si finalmente el contenedor se escapa?”.
Los espacios de nombres de usuario permiten remapear el UID 0 del contenedor a un UID sin privilegios en el host. Es decir, dentro del contenedor la aplicación crea que es root, pero visto desde el host ese proceso es, en realidad, un usuario corriente de un rango subuid/subgid asignado.
De esta forma, aunque un atacante consiga privilegios de root dentro del contenedor y logre encadenar un exploit de kernel o runtime, al volver al host lo hace como usuario sin privilegios. No puede sobrescribir binarios de root, montar sistemas de ficheros sensibles ni manipular dispositivos donde no tiene capacidades.
Podman se diseñó desde el principio con este modelo en la cabeza: contenerización rootless por defecto siempre que sea posible, uso de SELinux/AppArmor y cgroups incluso sin root, y un énfasis fuerte en minimizar los privilegios estructurales.
Docker también dispone hoy de un modo rootless “de verdad”, donde dockerd y los contenedores viven dentro de un user namespace, distinto del antiguo userns-remap en el que el demonio seguía siendo root. Desde un punto de vista de seguridad, esto evita que un exploit contra dockerd sea un acceso automático al root del host.
Redes de contenedores con Podman: bridge, host, macvlan e interfaces rootless
Otra pieza relevante de la seguridad y del funcionamiento práctico es la capa de red de los contenedores. Podman ofrece distintos mecanismos para dar conectividad, con especial cuidado a los escenarios rootless.
En Podman 4 se introdujo netavark, un driver de red que sigue el modelo CNI para proporcionar a los contenedores direcciones IP en redes bridge, macvlan, etc. Con netavark, los contenedores rootful suelen conectarse por defecto a una red llamada “podman” asociada a un Linux bridge (podman0) en el host, con direccionamiento 10.88.0.0/16.
Esta red bridge por defecto proporciona conectividad básica a internet mediante reglas SNAT y permite exponer puertos del contenedor al host usando -p o –publish, lo que genera reglas DNAT en iptables/nftables. Por compatibilidad con Docker, esta red no activa un servidor DNS interno.
Además podemos crear redes bridge definidas por el usuario, que aíslan grupos de contenedores entre sí y sí que ofrecen DNS interno. Esto permite que los contenedores se resuelvan por nombre dentro de su red privada, facilita separar servicios que no deberían verse entre sí y da más control sobre MTU, firewall, etc.
En entornos más avanzados es posible usar redes macvlan o ipvlan para conectar los contenedores directamente a la red física del host. Macvlan permite que los contenedores se comuniquen entre sí, mientras que ipvlan tiende a aislarlos más; son opciones útiles en despliegues donde cada contenedor debe verse como un host real para el resto de la red.
Redes rootless: slirp4netns y pasta
Cuando el usuario no es root, la cosa se complica: un usuario sin privilegios no puede modificar libremente el namespace de red global ni crear interfaces arbitrarias, así que Podman tiene que tirar de soluciones en espacio de usuario para dar conectividad.
Ahí entra en juego slirp4netns, el mecanismo clásico usado por Podman rootless. Este proyecto genera un entorno de red aislado dentro del contenedor y utiliza el módulo slirp del kernel para traducir direcciones (NAT) y permitir que el contenedor salga a internet a través de la red del host.
En la práctica, slirp4netns crea una interfaz TAP dentro del namespace de red del contenedor (por ejemplo tap0 con IP 10.0.2.100 y gateway 10.0.2.2) y enruta el tráfico vía una pila TCP/IP implementada en espacio de usuario. Es funcional, pero añade algo de overhead y ciertas limitaciones.
Una de esas limitaciones es que los usuarios sin privilegios no pueden usar puertos por debajo del 1024. El parámetro sysctl net.ipv4.ip_unprivileged_port_start marca desde qué puerto se consideran “no privilegiados” (por defecto, 1024), aunque se puede ajustar con cuidado si el modelo de seguridad lo permite.
En Podman 5, slirp4netns se sustituye por pasta como mecanismo rootless por defecto. Pasta también funciona íntegramente en espacio de usuario, pero gracias a la interfaz tap y a técnicas de cero‑copy consigue un rendimiento de red casi nativo, reduciendo el impacto en latencia y throughput frente a slirp4netns.
Redes bridge y host en contenedores rootless
Incluso trabajando en modo sin root podemos enganchar contenedores rootless a la red bridge por defecto “podman” usando –network=podman. De esta forma se aprovechan las capacidades de netavark y se mapean puertos al host con -p, igual que en modo rootful.
También es posible crear redes bridge definidas por el usuario en entornos rootless. En este caso, los bridges y dispositivos asociados no se crean en el namespace de red global del host, sino en el espacio de nombres del usuario, por lo que desde el host no veremos esas interfaces con un simple sudo ip a.
Para inspeccionar estos bridges rootless, Podman ofrece el comando podman unshare –rootless-netns, que abre una shell en el namespace de red del usuario. Desde ahí sí se observan los bridges internos y se puede comprobar la conectividad directa con los contenedores.
Este aislamiento tiene un efecto curioso: desde el host puede no haber conectividad directa con las IP de esos contenedores rootless, pero sí con los puertos mapeados a la IP del propio host, que actúan de entrada al servicio.
Para la comunicación entre contenedores, el servidor DNS integrado en la red bridge definida por el usuario permite que hablen por nombre, lo que facilita mucho la configuración de aplicaciones distribuidas.
En el otro extremo está la red host (–network=host), donde el contenedor comparte la pila de red con el host sin IP propia. En este modo, si un contenedor rootless levanta un servidor en el puerto 8080/tcp, el servicio estará directamente disponible en el puerto 8080 del host, lo que ahorra mapeos de puertos pero reduce aislamiento.
Podman Desktop, herramientas de imágenes y orquestación local
Para quien prefiere algo más visual, Podman Desktop proporciona un entorno gráfico para gestionar contenedores, pods, imágenes y configuraciones de red en máquinas de desarrollo, actuando como alternativa directa a Docker Desktop pero apoyándose en el motor sin daemon.
La obtención de imágenes se hace con podman pull, que descarga desde los registros definidos en /etc/containers/registries.conf. Si indicamos la imagen sin nombre de registro, Podman intenta localizarla en el orden de registros configurados, usando la etiqueta latest por defecto.
Además de la CLI de Podman, podemos recurrir a skopeo como herramienta específica para gestionar, inspeccionar y copiar imágenes entre registros y almacenes locales. Comandos como copy, delete o sync permiten automatizar la sincronización entre repositorios, marcar imágenes para garbage collection o mover artefactos entre entornos.
Una vez descargada la imagen, podman run permite arrancar contenedores indicando transporte y ruta (por defecto, transporte docker y búsqueda en los registros configurados). Las opciones habituales (-d, -p, –name, –pod, etc.) cubren tanto escenarios de laboratorio como despliegues más serios.
La gestión diaria se completa con podman ps para listar contenedores, podman stop / start para detener o arrancar instancias existentes, y podman rm para eliminar contenedores parados. Si un contenedor se ha modificado y queremos convertirlo en imagen nueva, podman commit guarda esos cambios bajo un nuevo nombre.
Pods y modelos avanzados de ejecución con Podman
Inspirándose en Kubernetes, Podman permite agrupar contenedores en pods que comparten la misma pila de red y ciertos recursos. Es un patrón perfecto para cosas como una base de datos y su cliente, o varios microservicios que deben comunicarse con baja latencia.
El comando podman pod create genera un nuevo pod y devuelve su ID, pero por defecto el pod queda detenido hasta que se arranca algún contenedor asociado o se lanza explícitamente con podman pod start.
Para ver qué pods hay en el sistema se usa podman pod ls, que listará también el contenedor INFRA asociado a cada uno. Este contenedor especial mantiene viva la red y otros recursos compartidos del pod, por lo que el número de contenedores nunca es cero.
Podman ofrece comandos específicos para iniciar, detener o reiniciar pods completos (podman pod start/stop/restart), pero también podemos actuar solo sobre un contenedor concreto dentro del pod sin modificar el resto, manteniendo el aislamiento de procesos a nivel de aplicación.
Para añadir más contenedores a un pod existente se utiliza podman run –pod NOMBRE_POD, siguiendo la misma sintaxis que para contenedores sueltos. No se puede “sacar” un contenedor de un pod sin destruirlo, ya que la pertenencia al pod va ligada a su ciclo de vida.
La supervisión se completa con podman ps –pod o comandos específicos para monitorizar los procesos de todos los contenedores en todos los pods, identificando rápidamente qué forma parte de qué grupo y cómo están distribuidos los servicios.
Contenedores, LXC y virtualización de aplicaciones
Aunque Docker y Podman dominen la conversación, no son las únicas opciones de contenedorización en GNU/Linux. Existen soluciones como LXC/LXD, systemd‑nspawn o Kata Containers que abordan el problema desde ángulos algo distintos.
LXC proporciona contenedores de sistema muy ligeros, con su propio hostname, IP, sistema de ficheros y un init completo. En combinación con LXD, que actúa como hipervisor de contenedores y VMs, se consigue una experiencia muy cercana a tener múltiples máquinas ligeras con desempeño casi bare metal.
Mientras Docker/Podman se centran en contenedores de aplicación compatibles con OCI, LXC brilla cuando se necesitan entornos persistentes, con varios servicios y procesos, que se comporten como una VM pero con menor overhead y una integración muy natural con el kernel de Linux.
Desde el punto de vista del rendimiento, LXC puede ejecutar aplicaciones empresariales de uso intensivo de datos sin apenas penalización, lo que lo convierte en una opción potente para cargas sensibles a la latencia pero que no requieren el aislamiento fuerte de una VM tradicional.
Junto a esto, también han ganado fuerza tecnologías orientadas a micro‑VMs y aislamiento reforzado como Kata Containers, que lanzan cada pod dentro de una pequeña máquina virtual con su propio kernel, o gVisor, que intercala un “kernel sandbox” escrito en Go entre la aplicación y Linux. Ambas reducen la dependencia del kernel compartido y se acercan al modelo de seguridad de las VMs manteniendo la ergonomía de los contenedores.
Buenas prácticas de endurecimiento para Docker y Podman
Con todo lo anterior, el enfoque razonable para una plataforma de contenedores robusta es trabajar en capas: segmentación con namespaces/cgroups, políticas de filtrado con seccomp y MAC, y restricción de privilegios estructurales mediante modo rootless.
En la práctica, conviene adoptar como estándar que los contenedores se ejecuten en modo sin root siempre que sea posible, especialmente en entornos multi‑tenant o dentro de nubes compartidas. Podman lo facilita de serie, y Docker rootless cubre la misma filosofía cuando se requiere compatibilidad.
El kernel del host debe mantenerse al día de parches de seguridad. Vulnerabilidades como Dirty COW, Dirty Pipe y compañía se resuelven con actualizaciones que hay que aplicar relativamente rápido si no queremos dejar abierto el camino a escapes de contenedores.
Otra medida muy eficaz es utilizar distribuciones inmutables orientadas a contenedores, como openSUSE MicroOS o Flatcar Linux. Su raíz de solo lectura y su sistema de actualizaciones atómicas reducen mucho la superficie de ataque y facilitan rollback si algo sale mal tras un parche.
En cuanto a configuración: nada de contenedores privilegiados en producción salvo casos hipercontrolados, no ejecutar aplicaciones como root dentro del contenedor, usar sistemas de ficheros de solo lectura cuando sea viable y limitar las capacidades al mínimo necesario. SELinux/AppArmor y perfiles seccomp ajustados a las llamadas reales de la aplicación son casi obligatorios.
Por último, no hay que olvidar la parte de monitorización del tiempo de ejecución. Herramientas como Falco, Sysdig Secure o Aqua ayudan a detectar anomalías, intentos de escape (por ejemplo accesos raros a /proc o /sys), tráfico de red atípico o comandos sospechosos, dando margen de reacción antes de que el ataque se consolide.
Al combinar virtualización clásica con KVM, contenedores gestionados por Podman (preferiblemente en modo rootless), redes bien segmentadas y buenas prácticas de endurecimiento, se puede montar una plataforma de virtualización muy flexible y bastante segura. Entender que el kernel compartido es el punto más delicado, apoyarse en VMs o micro‑VMs cuando el riesgo o el cumplimiento normativo lo exigen, y cuidar la cadena de suministro de imágenes marca la diferencia entre un entorno de laboratorio y una infraestructura lista para producción.
Tabla de Contenidos
- Virtualización clásica con KVM y máquinas virtuales
- Requisitos de hardware y problemas habituales con KVM
- Contenedores: virtualización ligera a nivel de sistema operativo
- Namespaces y cgroups: la base del aislamiento en contenedores
- Amenazas de seguridad en plataformas de contenedores
- Docker vs Podman: impacto en el modelo de amenazas
- Modo rootless y espacios de nombres de usuario
- Redes de contenedores con Podman: bridge, host, macvlan e interfaces rootless
- Redes rootless: slirp4netns y pasta
- Redes bridge y host en contenedores rootless
- Podman Desktop, herramientas de imágenes y orquestación local
- Pods y modelos avanzados de ejecución con Podman
- Contenedores, LXC y virtualización de aplicaciones
- Buenas prácticas de endurecimiento para Docker y Podman