Puoi eseguire Docker in produzione per mesi senza problemi visibili. I container si avviano, le app rispondono, nulla si rompe. Poi una porta esposta o un permesso mal configurato crea un accesso che un attaccante non ha dovuto guadagnarsi. La maggior parte degli errori di sicurezza in Docker non sembrano errori finché qualcosa non va storto.
Questo articolo illustra le configurazioni specifiche che mettono a rischio gli ambienti container, cosa ciascuna consente a un attaccante, e include una checklist che puoi eseguire sulla tua infrastruttura oggi.
Perché la sicurezza di Docker è più complessa di quanto sembri
I container sembrano isolati. Ne avvii uno, esegue il suo spazio di processo e da dentro il container successivo non esiste. Ottieni isolamento, ma è solo parziale. I container condividono il kernel dell'host, il che significa che un processo dentro un container può, in specifiche circostanze, raggiungere completamente il sistema host.
I container Docker vengono configurati per la comodità dello sviluppatore, non per l'hardening in produzione. Accesso root attivo. Tutte le porte sono collegabili a tutte le interfacce. Nessun monitoraggio runtime. La maggior parte degli sviluppatori accetta quelle impostazioni, rilascia il container e passa oltre. È un approccio ragionevole per iniziare, ma non è una postura di sicurezza definitiva.
Secondo Rapporto 2024 di Red Hat sullo stato della sicurezza di Kubernetes, il 67% delle organizzazioni ha ritardato o rallentato il rilascio di applicazioni a causa di preoccupazioni relative alla sicurezza dei container o di Kubernetes. L'attrito di solito non proviene da attacchi. Proviene da team che scoprono come le loro configurazioni container avevano bisogno di hardening che non avevano implementato.
Spesso vediamo container in esecuzione in produzione con la stessa configurazione che avevano sul computer locale di uno sviluppatore. È lì che gli errori di sicurezza in Docker tendono a accumularsi silenziosamente, senza sintomi visibili finché qualcosa non viene controllato o non fallisce.
Gli errori che creano questi vuoti sono specifici, prevedibili e per lo più evitabili, partendo dal livello di configurazione.
Errori comuni di configurazione di Docker
La maggior parte delle violazioni di container non inizia con uno zero-day. Iniziano con una configurazione impostata il primo giorno, senza pensare molto all'esposizione di rete o all'ambito dei privilegi.
Le impostazioni predefinite di Docker sono costruite per funzionare. Il divario tra funzionale e sicuro è dove i rischi di sicurezza dei container in Docker si accumulano, specialmente nelle configurazioni self-hosted che vengono distribuite e mai riviste.
Vediamo spesso questo schema: container su server con IP pubblico con binding di porte, impostazioni utente e configurazioni di rete esattamente come erano al momento del rilascio iniziale.
Esecuzione di container come root
Quando avvii un container Docker senza specificare un utente, viene eseguito come root. Ciò significa che qualsiasi processo dentro il container, inclusa la tua applicazione, ha privilegi a livello di root all'interno dello spazio dei nomi del container.

Root dentro un container non è la stessa cosa che root sull'host, ma la separazione non è assoluta. Gli exploit di escalation dei privilegi che prendono di mira il runtime, come il CVE-2019-5736 di runc ben documentato e difetti runtime simili, richiedono frequentemente un processo container root per avere successo.
I container non-root rimuovono il requisito del processo root su cui questi exploit dipendono, restringendo significativamente la superficie di attacco per quella classe di vulnerabilità, anche se non eliminano completamente il rischio di escape del container.
Aggiungere una direttiva USER al tuo Dockerfile risolve questo problema. Alcune immagini ufficiali vengono fornite con un utente senza privilegi che puoi attivare con una direttiva USER, ma molte mantengono comunque il default di root senza un utente app pronto all'uso. In quei casi, crei l'utente nel Dockerfile prima di passare a esso. Per la maggior parte delle configurazioni self-hosted, questo singolo cambiamento elimina un'intera categoria di rischi di escalation.
Esposizione di troppe porte su Internet pubblico
Quando pubblichi una porta con Docker, Docker scrive direttamente le sue regole iptables. Quelle regole vengono eseguite prima delle regole del firewall a livello di host. Questo è un comportamento noto segnalato dalla comunità e documentato nella guida di filtrazione dei pacchetti di Docker, non una misconfiguration, e significa che UFW e strumenti simili non bloccano quello che Docker ha già aperto.

Docker scrive direttamente in iptables, ignorando le impostazioni predefinite di UFW e firewalld su molti host Linux. Questo significa che una porta associata a 0.0.0.0 può essere raggiungibile pubblicamente anche quando il firewall sembra configurato correttamente. I gruppi di sicurezza cloud e le regole della catena DOCKER-USER possono comunque bloccare quel traffico, quindi l'esposizione effettiva dipende dalla tua configurazione di rete specifica.
Associa i servizi a 127.0.0.1 dove possibile, instrada il traffico pubblico attraverso un reverse proxy, e pubblica solo ciò che richiede effettivamente accesso esterno. Un reverse proxy è il modo più affidabile per controllare cosa viene esposto dall'esterno dell'host.
Ignorare l'isolamento della rete tra i container
Qualsiasi container in quella rete può raggiungere qualsiasi altro container senza restrizioni. Il bridge predefinito non applica alcun filtro del traffico tra i container che lo condividono, e la maggior parte delle configurazioni non cambia mai questa impostazione.

Se un container viene compromesso, quella comunicazione aperta diventa un percorso di movimento laterale. Un container frontend può raggiungere un database, un API interno, o qualsiasi altra cosa sulla stessa rete bridge predefinita, anche quando quell'accesso non era mai stato intenzionato.
Le reti definite dall'utente ti danno il controllo esplicito su quali container possono comunicare, ma una singola rete personalizzata condivisa da tutti i tuoi servizi consente comunque il traffico libero tra i container. L'isolamento reale richiede di mettere i servizi che non dovrebbero comunicherà tra loro su reti separate. Disattivare il bridge predefinito è il punto di partenza, non il traguardo.
Trascurare il socket Docker
Il socket Docker in /var/run/docker.sock è l'interfaccia di controllo dell'intero motore Docker. Montarlo in un container dà a quel container accesso API diretto al daemon in esecuzione sull'host.

Con quell'accesso, un container può avviare nuovi container, montare directory dell'host, ispezionare e modificare container in esecuzione, e controllare effettivamente la macchina host. La superficie di attacco è equivalente a root sull'host, ed è per questo che qualsiasi strumento che richiede accesso al socket merita una valutazione attenta.
Per la maggior parte dei casi d'uso, ci sono alternative più sicure: API con ambito limitato o strumenti di gestione Docker che non richiedono accesso al socket. Docker-in-Docker ha i suoi compromessi di sicurezza e operativi e non è un sostituto diretto.
Gli errori di configurazione creano l'esposizione iniziale. Le scelte di immagine e dipendenza determinano come quell'esposizione si amplifica nel tempo.
Errori di immagini e segreti che persistono oltre il container
Quando arresti un container, gli errori di configurazione al suo interno si fermano con esso. Quando ricostruisci da un'immagine che porta una vulnerabilità o una credenziale hardcoded, il problema ricomincia con il container. Gli errori a livello di immagine non si resettano tra i deploy.
Viaggiano con l'immagine in ogni ambiente che la scarica, ogni registry che la archivia, e ogni membro del team che la esegue. Questa persistenza rende la gestione di immagini e segreti una categoria di rischio distinta, che merita di essere controllata separatamente dalla configurazione.
Vediamo spesso questo schema: un'immagine scelta con attenzione all'inizio del progetto e mai ricostruita da allora, che si allontana lentamente dalla baseline di sicurezza che rappresentava inizialmente.
Usare immagini non attendibili o obsolete
I registry pubblici sono aperti a chiunque. Immagini dannose sono state distribuite attraverso Docker Hub contenenti crypto-miner e backdoor incorporati nella cronologia dei layer che persistono nei riavvii dei container. La verifica prima del download è importante, soprattutto per le immagini da editori non ufficiali o sconosciuti.

Il problema separato è l'obsolescenza. Un'immagine ufficiale che hai scaricato sei mesi fa e mai ricostruita da allora ha accumulato vulnerabilità Docker non corrette con ogni CVE divulgato contro i suoi pacchetti. L'immagine non è rotta. È semplicemente non più aggiornata.
Il rapporto 2024 State of the Software Supply Chain di Sonatype ha rilevato che il 95% delle volte un componente vulnerabile viene consumato, una versione corretta è già disponibile, e l'80% delle dipendenze dell'applicazione rimane non aggiornato per oltre un anno. Questo schema è rilevante anche per le immagini base Docker, poiché si basano su gli stessi pacchetti open-source.
Usa immagini ufficiali da editori verificati e fissa tag di versione specifici invece di affidarti a "latest". Stabilisci una cadenza di rebuild regolare per mantenere le tue immagini aggiornate.
Hardcoding dei Segreti in Dockerfiles e Compose Files
Le credenziali scritte in un istruzione ENV o ARG di un Dockerfile, hardcoded in un blocco environment di Compose, passate come argomenti di build, o archiviate in un file .env committato nel version control non scompaiono quando fermi il container. Rimangono nella cronologia dei livelli dell'immagine o nel source control, accessibili a chiunque riesca a raggiungerli.

Questo è uno degli errori di sicurezza di Docker più trascurati perché non causa problemi visibili durante lo sviluppo. Una chiave API in un'istruzione ENV funziona correttamente. È anche nel tuo repository, incorporata nella tua immagine, e distribuita ovunque quell'immagine viaggi.
Modern Docker Compose supporta un meccanismo nativo di secrets che monta le credenziali a runtime senza incorporarle nell'immagine. Gli API di secrets di Docker e i gestori di secrets esterni seguono lo stesso principio. Queste sono le opzioni che mantengono le credenziali completamente fuori dai build artifacts e dai file committati.
Le variabili d'ambiente a runtime sono un miglioramento rispetto alle credenziali hardcoded, ma rimangono comunque esposte attraverso l'output di Docker inspect, i log e i crash dump. Sono un passo avanti rispetto ai segreti incorporati, non una soluzione definitiva.
Non Aggiornare Regolarmente le Immagini dei Container
Eseguire la stessa immagine per mesi è un'abitudine comune. Ogni giorno che passa dopo che una nuova vulnerabilità viene divulgata, ma prima che tu esegua un rebuild, i tuoi container portano una finestra di esposizione che cresce senza alcun cambiamento visibile.
Stabilisci una cadenza di rebuild coerente. Automatizza quel processo dove possibile, ed esegui periodicamente uno scanner di vulnerabilità sulle tue immagini attuali. L'obiettivo non è la perfezione. È ridurre il tempo tra il rilascio di una patch e il suo deployment.
Il controllo d'accesso e il monitoring possono essere deprioritizzati nei deployment veloci. Sono anche le categorie in cui gli incidenti rimangono inosservati più a lungo.
Lacune nel Controllo d'Accesso e nella Visibilità
Dopo che un container è in esecuzione con una configurazione solida e immagini aggiornate, rimangono due categorie di errori. Entrambe sono invisibili per natura: non noterai un problema di controllo d'accesso debole finché qualcuno non lo usa, e non noterai una lacuna di monitoring finché non avrai bisogno di indagare un'attività che non è mai stata registrata.
Lo stesso Ricerca Red Hat 2024 ha rilevato che il 42% dei team mancava di capacità sufficienti per affrontare la sicurezza dei container e le minacce correlate.
Troviamo che le lacune di monitoring generalmente emergono durante le indagini degli incidenti, non prima. Nel momento in cui la visibilità diventa una priorità, spesso è in risposta a qualcosa piuttosto che nel prevenirlo.
Autenticazione Debole e Dashboard di Gestione Esposti
Una dashboard di gestione container su un IP pubblico senza autenticazione non richiede un attaccante sofisticato. Richiede loro di conoscere l'indirizzo. È una barra più bassa di quanto la maggior parte dei team realizzi.

Gli strumenti di monitoring e gestione self-hosted tipicamente vengono forniti con un'interfaccia web accessibile su tutte le interfacce di rete. Lasciare quelli su un IP pubblico senza autenticazione davanti è l'equivalente container di lasciare un admin panel sbloccato.
L'autenticazione, un reverse proxy e il posizionamento della rete privata sono la base. Il controllo d'accesso è un passaggio di configurazione che aggiungi a qualsiasi interfaccia di gestione, non qualcosa che viene fornito abilitato.
Lo stesso principio si applica a Docker CLI e GUI management; l'accesso a livello admin al daemon comporta lo stesso rischio indipendentemente dall'interfaccia.
Non Monitorare Ciò Che Fanno i Tuoi Container
Se un container viene compromesso, l'attività dell'attaccante lascia tracce: cambiamenti nel comportamento dei processi, connessioni di rete insolite, modifiche di file impreviste. Senza raccolta di log, quelle tracce non esistono in una forma su cui puoi agire.
La raccolta centralizzata dei log, l'audit logging dei container e gli strumenti di monitoraggio runtime ti forniscono i dati per rilevare attività anomale prima che si aggravino. L'obiettivo non è analizzare ogni riga. È avere i dati disponibili quando hai bisogno di investigare.
I setup di container che girano in silenzio in produzione senza log pipeline e senza alert non sono a bassa manutenzione. Sono non ispezionati. Sono due stati operativi diversi.
Perché l'Ambiente Infrastrutturale È Importante
La sicurezza dei container parte dalla configurazione, ma la configurazione gira su un'infrastruttura. Un host con networking mal configurato, risorse condivise o nessun filtro a livello di rete crea condizioni che influenzano ogni container sopra di esso. Configurare correttamente il setup del container e configurare correttamente il server sono due compiti separati.
Molti gap di sicurezza Docker sono amplificati da condizioni che gli stessi container ereditano:
- Un server a tenancy condivisa senza isolamento hardware tra tenant
- Un kernel host senza patch
- Un host senza filtro di rete integrato a livello di rete
Questo non elimina la necessità dei passaggi di configurazione sopra descritti, poiché l'hardening corretto dei container è importante indipendentemente dal livello infrastrutturale. Partire da infrastruttura isolata rimuove uno strato di preoccupazione dall'equazione.
Su Cloudzy, offriamo due percorsi a seconda di quello che il tuo setup richiede:
- Linux VPS: un ambiente pulito per deployare Docker tu stesso e applicare i passaggi di hardening in questo articolo
- Portainer VPS: un'opzione one-click con Portainer preinstallato; il server si avvia e sei già nella dashboard
Entrambe le opzioni girano sulla stessa infrastruttura: virtualizzazione KVM, AMD Ryzen 9 CPUs fino a 5,7 GHz boost clock, memoria DDR5, storage NVMe SSD, fino a 40 Gbps di rete e protezione DDoS gratuita via filtri BuyVM, in 12 location globali con SLA di uptime del 99,95% SLA.
Per un'analisi più approfondita sull'esecuzione di Portainer su VPS, ne parliamo in un articolo dedicato.
Una Checklist di Sicurezza Pratica per Deploy Docker
I problemi di sicurezza Docker sopra descritti derivano per lo più da decisioni di configurazione singole prese una volta e mai revisionate. Eseguire questa checklist su un setup esistente individua quei gap. Funziona come audit, non come deployment guide.
Queste best practice di sicurezza Docker spiegano come proteggere i container Docker contro i fallimenti di configurazione più comuni descritti sopra.
Quick Reference: Tutti i 9 Errori
| Errore | Categoria | Fix One-Line |
| In esecuzione come root | Configurazione | Aggiungi USER direttiva nel tuo Dockerfile |
| Porte associate a 0.0.0.0 | Configurazione | Associa a 127.0.0.1 e instrada attraverso un reverse proxy |
| Nessun isolamento di rete | Configurazione | Dividi i servizi su reti separate definite dall'utente in base ai bisogni di accesso. |
| Socket Docker montato | Configurazione | Rimuovi il mount; usa APIs scoped o alternative |
| Immagini non attendibili o obsolete | Immagine | Usa immagini ufficiali con versioni fissate |
| Segreti hardcoded | Immagine | Sposta le credenziali a variabili d'ambiente runtime o un gestore di segreti |
| Nessuna pianificazione di ricostruzione immagini | Immagine | Stabilisci una cadenza di ricostruzione mensile; automatizza dove possibile |
| Dashboard non autenticati | Accesso | Aggiungi autenticazione e sposta le UI di gestione su reti private |
| Nessuna raccolta log dei container | Accesso | Configura il logging centralizzato e il monitoraggio runtime |
Ti consigliamo di eseguirlo prima su configurazioni esistenti, perché è lì che le lacune sono più probabilmente già presenti.
Container eseguiti come non-root: Controlla i tuoi Dockerfile per una direttiva USER. Se non esiste, il container viene eseguito come root.
Binding delle porte limitati a localhost o tramite proxy: Esegui docker ps e rivedi i binding delle porte. Una voce 0.0.0.0:PORT può essere raggiungibile pubblicamente su host dove nessun security group upstream, firewall esterno o regola DOCKER-USER chain la blocca.
Reti bridge personalizzate in uso: I container sul bridge predefinito di Docker possono raggiungersi liberamente. I container sulla stessa rete bridge definita dall'utente possono ancora comunicare tra loro, quindi distribuisci i servizi su reti separate per confini di trust per un isolamento effettivo.
Socket Docker non montato nei container: Controlla i file Compose e gli argomenti di esecuzione. Se /var/run/docker.sock appare come volume, conferma che è richiesto e intenzionale.
Immagini base da editori verificati con versioni fissate: Un FROM ubuntu:latest scarica una versione non specificata, potenzialmente obsoleta. Fissa una versione specifica.
Nessun segreto nei Dockerfile, file Compose o argomenti di build: La cronologia dei layer dell'immagine mantiene le credenziali dopo l'eliminazione del container. Usa i segreti Compose, i segreti Swarm, i montaggi di segreti di build o un gestore di segreti esterno. Le variabili d'ambiente runtime sono meglio dei valori hardcoded, ma compaiono comunque nell'output di inspect e nei log.
Pianificazione di ricostruzione immagini definita: Le vecchie immagini accumulano vulnerabilità. Una cadenza di ricostruzione mensile mantiene la finestra di esposizione gestibile per la maggior parte delle configurazioni.
Interfacce di gestione dietro autenticazione: Qualsiasi dashboard su un IP pubblico senza autenticazione è un punto di accesso aperto. Il posizionamento su rete privata è preferibile dove possibile.
Raccolta dei log del container in corso: Senza una pipeline di logging, il rilevamento degli incidenti dipende dall'impatto visibile del sistema. È un segnale troppo tardivo per agire.
Conclusione
La configurazione predefinita di Docker è pensata per la comodità, non per la sicurezza. La maggior parte degli errori trattati in questo articolo risale a impostazioni mai cambiate dopo il deployment iniziale, non ad attacchi sofisticati.
Le correzioni sono principalmente decisioni di configurazione una tantum: una direttiva USER, un cambio di port binding, una rete personalizzata, una pianificazione di rebuild. Nessuna di esse richiede nuovi strumenti nella maggior parte dei setup.
Configurare correttamente il container è il primo compito. L'infrastruttura su cui gira è il secondo. Entrambi contano, e nessuno sostituisce l'altro.