Limiti fondamentali dell'isolamento dei container
La containerizzazione è diventata il metodo dominante per il confezionamento e la distribuzione di applicazioni in architetture cloud e a microservizi. I container consentono di raggruppare codice, librerie e dipendenze, garantendo un'elevata portabilità ed efficienza operativa.
Tuttavia, la migrazione massiccia di carichi di lavoro critici verso i container rende la resilienza dei meccanismi di isolamento una preoccupazione fondamentale. Ricerche e sondaggi condotti tra i team DevOps e di sicurezza mostrano costantemente che la sicurezza dei container rimane una delle preoccupazioni principali: le vulnerabilità del kernel e del runtime, insieme agli errori di configurazione, portano regolarmente a violazioni dell'isolamento nel mondo reale.
La domanda chiave è: l'isolamento dei container è assoluto?
La risposta è no e il motivo risiede nella loro architettura. In questo articolo approfondiremo la questione ed esploreremo le misure disponibili per mitigare i rischi di vulnerabilità.
Contenitori e macchine virtuali: il kernel condiviso
Macchina virtuale (VM):
-
Ogni macchina virtuale esegue il proprio kernel del sistema operativo, completamente separato dall'host.
-
L'isolamento è garantito da un hypervisor (KVM, Xen, ESXi, ecc.).
-
Un attacco deve in genere colpire l'hypervisor, restringendo in modo significativo i vettori di attacco.
Contenitori:
-
Un contenitore è un processo isolato utilizzando le primitive del kernel Linux (spazi dei nomi, cgroup, ecc.).
-
Un container contiene solo lo spazio utente (librerie, runtime, applicazioni) ma condivide un kernel con l'host e gli altri container.
-
Qualsiasi vulnerabilità critica del kernel diventa automaticamente un potenziale vettore di attacco per tutti i container del nodo.
La scelta architettonica di un kernel condiviso è ciò che rende l'isolamento dei container fondamentalmente non assoluto: lo sfruttamento di una vulnerabilità del kernel permette a un attaccante di aggirare i confini di qualsiasi namespace.

Il ruolo degli spazi dei nomi e dei gruppi C nella creazione dell'isolamento
Le fondamenta dell'isolamento dei container in Linux poggiano su Namespaces e Cgroups.
-
Namespaces - Isolamento della vista del sistema: PID, Mount, Network, User, IPC, UTS e altri. I processi "vedono" solo il proprio spazio: il proprio filesystem, il proprio PID, il proprio hostname e la propria rete.
-
Cgroups - Controllo e limitazione delle risorse: CPU, memoria, I/O, numero di processi, ecc.
Gli spazi dei nomi creano l'illusione di un sistema separato e i Cgroup impediscono a un contenitore di monopolizzare le risorse dell'host. Tuttavia, è fondamentale notare che i Namespace sono un meccanismo di isolamento, non un vero e proprio meccanismo di sicurezza contro l'aggiramento deliberato delle barriere. Non impediscono lo sfruttamento delle vulnerabilità del kernel, non risolvono i problemi legati ai privilegi eccessivi di root o ai container privilegiati, non filtrano le chiamate di sistema e non gestiscono i criteri di accesso.
Pertanto, i namespace e i Сgroup sono solo un livello di base che deve essere rafforzato:
-
filtraggio delle chiamate di sistema (Seccomp)
-
Criteri MAC (AppArmor/SELinux)
-
Restrizioni di capacità
-
Idealmente, User Namespaces + modalità rootless
Sicurezza della piattaforma container: minacce critiche e vettori di attacco
La superficie di attacco di una piattaforma container è a più livelli. Si possono identificare le seguenti superfici principali:
-
Runtime e kernel (runc, containerd, CRI-O, kernel Linux).
-
Catena di fornitura delle immagini.
-
Configurazione dei container e degli orchestratori.
-
Cloud e infrastruttura (IAM, API, storage, rete).
Panoramica dei vettori di attacco e delle vulnerabilità critiche: Fuga dei container

I vettori tipici in un ambiente container includono: lo sfruttamento di una CVE nel kernel Linux o nel runtime del container; l'utilizzo di immagini non sicure (pacchetti obsoleti, kit di exploit); errori di configurazione in Kubernetes/Docker (privilegi eccessivi, montaggio del filesystem dell'host, stack di rete dell'host); compromissione del registro e del CI/CD.
È importante capire che un Container Escape è spesso una combinazione di fattori:
Vulnerabilità Kernel/Runtime + Scarsa configurazione (esecuzione come root, --privilegi, montaggio del filesystem host, MAC/Seccomp mancante).
Vulnerabilità della catena di fornitura
La catena di fornitura delle immagini dei container è uno dei rischi principali, perché le immagini di base dei registri pubblici spesso contengono CVE note. Le dipendenze (moduli pip/npm/gem/go) vengono estratte transitivamente e non sono sempre controllate. Inoltre, il malware può essere "dormiente" e manifestarsi solo in fase di esecuzione.
Pertanto, le seguenti best practice sono obbligatorie in produzione:
-
Scansione delle immagini per i CVE, utilizzando strumenti come Trivy, Clair, Grype, ecc.
-
Utilizzo di registri con applicazione di policy, come Harbor, Quay o GHCR.
-
Firma e verifica delle immagini prima della distribuzione.
Errori di configurazione e contenitori privilegiati
L'errore più comune che apre le porte alle vulnerabilità è l'esecuzione di processi all'interno del contenitore come root! Se l'applicazione nel container viene lanciata con diritti di superutente, al momento della compromissione del container, l'attaccante ottiene root all'interno del container. Un'evasione riuscita all'esterno del contenitore semplifica poi in modo significativo l'escalation verso l'host root.
Vietare il lancio di applicazioni come root e utilizzare UID/GID non privilegiati è la regola di base di un corretto hardening.
I container privilegiati che usano il flag --privileged disabilitano in pratica i meccanismi di isolamento chiave, in particolare alcuni Cgroup, AppArmor/SELinux e le restrizioni Seccomp. In questo modo il contenitore ha accesso diretto ai dispositivi dell'host e agli pseudo-filesystem (/proc, /sys, /dev, ecc.). Da un contenitore privilegiato è facile montare i dischi dell'host e modificare il filesystem e le configurazioni.
In sostanza, un contenitore privilegiato equivale a un'escalation di privilegi intenzionale. Ricordate che questo è accettabile solo in scenari rari e ben isolati, e certamente non in un ambiente multi-tenant.
Ci sono anche errori di configurazione "silenziosi" che possono rendere l'ambiente del contenitore estremamente vulnerabile, anche senza usare il flag --privileged. I più pericolosi sono:
-
Montare hostPath su directory host sensibili, come /etc o /var/run/docker.sock.
-
La concessione di funzionalità non necessarie, ad esempio CAP_SYS_ADMIN, che praticamente concede i diritti di root sull'host.
-
Uso ingiustificato delle modalità hostNetwork o hostPID.
Queste impostazioni apparentemente minori possono consentire a un aggressore di sfuggire al contenitore e ottenere il controllo del kernel o dell'intero sistema host.
Architettura сonfronto: Docker vs Podman
Le differenze architettoniche tra Docker e Podman influenzano direttamente il modello di minaccia. Docker, con il suo demone ad alto privilegio, presenta il rischio principale: un singolo punto di guasto.
Docker e la modalità rootful
Nell'architettura classica, o standard, di Docker, la CLI di Docker comunica con il demone dockerd tramite il socket /var/run/docker.sock o TCP. Il demone dockerd viene eseguito come utente root ed esegue le operazioni principali:
-
Creazione/eliminazione di contenitori
-
Creazione di immagini
-
Gestione di reti/volumi
-
Interazione con i registri
Ne consegue che l'accesso diretto all'API di Docker equivale a un accesso root quasi completo all'host, consentendo a un aggressore di montare il filesystem, lanciare container privilegiati, ecc. In questo scenario, il demone diventa un singolo punto di fallimento: se viene compromesso, l'attaccante ottiene l'accesso all'host.
Pertanto, Docker richiede un controllo rigoroso dell'accesso al socket, l'abilitazione obbligatoria di MAC e Seccomp e la restrizione delle funzionalità. È fondamentale ridurre al minimo il numero di utenti e servizi che hanno accesso all'API di Docker.
Podman senza demoni: l'approccio nativo di Linux
Podman è stato sviluppato da Red Hat e dalla comunità come alternativa Linux "nativa" più sicura. È stato originariamente progettato con un'architettura senza demoni, il che significa che non esiste un demone persistente e centralizzato. I container sono invece processi figli dell'utente che ha eseguito il comando. La base è una profonda integrazione con systemd, dove unità e servizi vengono creati direttamente.
Questo elimina il singolo punto di fallimento presentato dal demone root e si allinea meglio al modello Unix tradizionale: "chi ha lanciato il processo lo possiede".
Modalità senza root: Gli spazi dei nomi utente come principale barriera di sicurezza
La modalità rootless è essenzialmente la risposta logica al pericolo fondamentale: "Cosa succede se il contenitore evade?".
Il ruolo degli spazi dei nomi utente e la mitigazione dei danni
Gli spazi dei nomi utente isolano UID/GID e consentono il remapping dell'UID. Ad esempio, se un processo all'interno di un container ha UID 0 (root), lo stesso processo viene mappato su un UID non privilegiato dell'intervallo subuid/subgid dell'host.
Pertanto, anche se un attaccante all'interno del container ha privilegi di root e riesce a sfruttare una vulnerabilità del runtime/kernel per passare al livello host, continua a operare come utente non privilegiato sull'host.
Molte catene di sfruttamento, tra cui runc-escape, sono quindi interrotte o hanno un impatto significativamente ridotto. Questo perché l'attaccante non può sovrascrivere i file binari/configurazioni di proprietà di root, non può montare i filesystem o gestire i dispositivi senza Capabilities e le politiche MAC continuano a funzionare anche sull'host.
Podman senza root: Limitazioni e stack di rete
Podman è stato progettato fin dall'inizio con un focus sulle operazioni senza root. I contenitori sono creati e gestiti da un utente non privilegiato, gli spazi dei nomi degli utenti sono abilitati per impostazione predefinita e SELinux/AppArmor e Cgroups sono utilizzati per quanto possibile senza accesso root.
Tuttavia, questo schema presenta alcuni compromessi. Quando un contenitore senza root opera nell'intervallo subuid/subgid, sono possibili errori di mappatura se l'immagine contiene file con valori UID/GID che non rientrano in questo intervallo.
Un altro svantaggio indiretto è che un utente non privilegiato non può configurare liberamente i namespace di rete. Dall'inizio del 2025, Podman utilizza la pasta dello stack in spazio utente come sostituto di slirp4netns. Questo plugin opera interamente nello spazio utente, ma grazie all'interfaccia tap e alla giunzione zero-copy, offre prestazioni di rete quasi native. Tuttavia, i rallentamenti delle prestazioni sono ancora possibili e sono sicuramente garantiti se gli utenti continuano a usare slirp4netns.
Docker senza radici: implementazione e requisiti
Docker supporta anche una modalità rootless a tutti gli effetti, in cui il demone dockerd e i container vengono lanciati all'interno di uno spazio dei nomi utente senza i privilegi dell'utente root dell'host. Si tratta di una differenza importante rispetto alla vecchia funzione userns-remap, in cui il demone stesso rimaneva privilegiato.
Dal punto di vista della sicurezza, questo garantisce che una violazione dei container non comporti l'escalation automatica al livello dell'utente root dell'host. In caso di attacco riuscito a dockerd in modalità rootless, si accede solo alle risorse dell'utente non privilegiato, non all'intero sistema.

La scelta tra modalità rootful e rootless è principalmente un equilibrio tra la massimizzazione delle prestazioni e la massima limitazione dei danni potenziali in caso di violazione.
Conclusioni e raccomandazioni per l'hardening
L'analisi conferma che l'isolamento dei container non è assoluto a causa della loro architettura. Poiché i container condividono il kernel del sistema host, le vulnerabilità a livello di kernel o di runtime possono, per definizione, "vedere" attraverso i namespace, violando i confini di sicurezza previsti.
In pratica, la maggior parte degli attacchi riusciti è legata a una combinazione di una vulnerabilità nota (kernel/runc) e di una configurazione debole (root, --privileged, hostPath).
Un modello di sicurezza affidabile per i container deve essere costruito come un sistema a più livelli:
Livello 1 - Segmentazione: (namespace, cgroup)
Livello 2 - Politiche e filtri: (Seccomp, MAC, Capabilities)
Livello 3 - Restrizione dei privilegi architetturali: (tramite modalità rootless e spazi dei nomi utente)
Lista di controllo: hardening dell'host per i container Docker e Podman
Senza root per impostazione predefinita
Considerate Podman o Docker in modalità rootless come modello predefinito per i carichi di lavoro interni e multi-tenant.
Irrigidimento e patch del kernel
Aggiornare tempestivamente il kernel (Dirty COW, Dirty Pipe e CVE simili sono quasi sempre già risolti da patch). Utilizzare distribuzioni con una buona storia di patch di sicurezza (RHEL, OpenSUSE, Ubuntu LTS, ecc.).
Adottare distribuzioni specializzate di sistemi operativi immutabili
Per migliorare la sicurezza dell'ambiente container, utilizzate distribuzioni di sistemi operativi immutabili come openSUSE MicroOS o Flatcar Linux. Il loro filesystem principale è di sola lettura per impostazione predefinita, riducendo in modo significativo la superficie di attacco. Il meccanismo chiave è quello degli aggiornamenti atomici: viene creata una nuova immagine del sistema operativo e l'aggiornamento viene applicato completamente o completamente annullato, impedendo la corruzione del sistema e accelerando il ripristino.
Principio del minimo privilegio
Non usare mai il flag --privileged in produzione, tranne che per ambienti altamente specializzati e isolati. Evitate di eseguire le applicazioni all'interno dei container come utente root e utilizzate un filesystem di sola lettura, l'insieme minimo di Capabilities e profili Seccomp rigorosi.
Criteri MAC e Seccomp
Abilitare e configurare SELinuxAppArmor per i container. Usare i profili Seccomp, idealmente quelli personalizzati basati sulle chiamate di sistema effettive dell'applicazione, non solo sui valori predefiniti di Docker.
Sicurezza della catena di fornitura
Implementare la scansione delle immagini dei container. Limitate le fonti delle immagini di base ai registri ufficiali o interni e abilitate la firma e la verifica delle immagini prima della distribuzione.
Monitoraggio del runtime
Utilizzate strumenti di rilevamento delle anomalie e IDS/EDR per container (Falco, Sysdig Secure o Aqua Security) in grado di rilevare i tentativi di fuga dei container e di monitorare l'esecuzione di comandi dannosi. Tracciate il traffico di rete atipico, le chiamate di sistema inattese e l'accesso a file sensibili.
Il futuro dell'isolamento: Kata Containers, gVisor e Micro-VMs
La modalità Rootless riduce significativamente il rischio di compromissione totale dell'host, ma non risolve tutto, poiché le dipendenze dal kernel dell'host rimangono e alcune classi di attacchi non vengono eliminate.
Pertanto, gli approcci ibridi stanno guadagnando popolarità per i carichi di lavoro critici:
- Kata Containers: Ogni pod o container viene lanciato all'interno di una micro-VM leggera con un kernel separato, combinando i vantaggi delle VM e dei container.
- gVisor: Un kernel sandbox scritto in Go che funge da livello intermedio tra l'applicazione e il vero kernel Linux.
Queste soluzioni riducono la dipendenza dal kernel condiviso e ci avvicinano al modello di isolamento delle macchine virtuali, preservando la natura dichiarativa e la comodità degli orchestratori di container.
Trasformate le indicazioni di questo post in sicurezza reale nella vostra prossima implementazione.
Procuratevi un server VPS o dedicato e iniziate oggi stesso a configurare una piattaforma di container sicura.
Passare alla selezione della configurazione.

Nuovi post:
Docker vs Podman: Una guida completa alla containerizzazione senza demoni