DA MASER A MONFUMO | MOTO GUZZI V85TT | PURE SOUND POV 4K

🏍️ Il mio nuovo canale YouTube: giri in moto in POV, solo audio, tra le Dolomiti in 4K. Niente musica, niente parole — solo il motore e le Alpi. Vieni a fare un giro!

Iscriviti

I container hanno cambiato il modo in cui impacchettiamo e distribuiamo il software, ma non hanno riscritto le regole di sicurezza fondamentali. I confini di fiducia, i privilegi e la superficie d'attacco sono ancora tutti lì. Questa è una delle cose che ho imparato approfondendo la sicurezza dei container, in parte dal libro Container Security di Liz Rice e in parte passando tempo con i componenti Linux sottostanti.

Un container è davvero solo un processo Linux con un po' di isolamento intorno. Parla al kernel tramite syscall, gira come un certo utente, vede un certo filesystem, raggiunge una certa rete e ottiene certi limiti di risorse. Se quelle fondamenta sono deboli, anche il container è debole.

Questo è più ovvio nei sistemi reali, perché i sistemi reali sono condivisi ovunque. Cluster condivisi, nodi condivisi, macchine di build condivise, registry condivisi, segreti condivisi, errori condivisi. Quando un layer si rompe, un altro layer deve impedire al problema di espandersi. Per questo la sicurezza dei container non è una singola funzionalità, ma uno stack di controlli pratici (e noiosi).

La Sicurezza dei Container Segue Vecchie Regole

bash
docker run \
  --read-only \
  --cap-drop=ALL \
  --security-opt=no-new-privileges:true \
  --security-opt=seccomp=default.json \
  --pids-limit=100 \
  --memory=256m \
  --cpus=1 \
  --user=10001:10001 \
  --tmpfs /tmp:rw,noexec,nosuid,size=64m \
  --network=bridge \
  myapp@sha256:abc123

Il primo errore è trattare un container come un confine di sicurezza rigido. È solo un'unità di packaging con un po' di isolamento intorno, utile e veloce, ma anche facile da fidarsi troppo!

Il threat model inizia con il kernel condiviso. Se due container girano sullo stesso host, entrambi dipendono dallo stesso kernel. Questo significa che bug del kernel, mount incautamente configurati, capability troppo ampie, default del runtime deboli, o anche il semplice abuso delle risorse possono influenzare anche altri workload. Questo diventa molto più serio nei sistemi multitenant. Un servizio interno fidato e del codice cliente ostile non dovrebbero essere trattati come se avessero lo stesso livello di isolamento. Lo stesso vale per i namespace di Kubernetes: sono utili per l'organizzazione e le policy, ma non sono un muro rigido tra tenant come lo è una VM.

Niente di tutto questo è nuovo! Least privilege, defense in depth, riduzione della superficie d'attacco, limitazione del blast radius, separazione dei compiti: idee antiche che continuano a emergere perché risolvono problemi reali. Non vuoi che una sola persona, o una sola pipeline, costruisca, firmi, approvi e distribuisca tutto. Chiediti: se questo workload viene compromesso, cosa può raggiungere dopo? Se la risposta è l'intero nodo, il control plane, o altri tenant, allora l'isolamento è debole. Tristezza.

Le System Call, i Permessi e le Capability Sono la Porta d'Ingresso nel Kernel

c
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
 
int main() {
    printf("uid=%d gid=%d\n", getuid(), getgid());
    execl("/bin/sh", "/bin/sh", NULL);
    return 0;
}

I container non fanno niente di magico. Sono solo processi che eseguono syscall, e il kernel è quello che decide se la risposta è sì o no. Questa è la sintesi. Interiorizza questo, e il resto della sicurezza dei container inizia ad avere più senso.

Questo è anche il motivo per cui il normale modello di permessi Linux si applica anche all'interno dei container. La proprietà dei file, i mode bit, setuid e setgid non smettono improvvisamente di funzionare perché il processo è in un container. Se hai un helper setuid lì dentro, può diventare un percorso di privilege escalation quando il runtime lascia abilitate capability pericolose o monta qualcosa che non dovrebbe.

Le capability fanno parte dello stesso quadro. Dividere root in privilegi più piccoli è stata una buona idea, ma ha anche creato più modi per sbagliare la configurazione. Alcune capability, come CAP_NET_BIND_SERVICE, sono abbastanza ristrette e facili da ragionare. Altre, come CAP_SYS_ADMIN, sono fondamentalmente un martello cosmico di potere. Per questo "non girare come root" non è abbastanza. Un processo può essere non-root e avere ancora abbastanza privilegi per ispezionare altri processi, influenzare i mount, o raggiungere parti del kernel che non vuoi che raggiunga. Pensa a quali syscall può effettuare un processo, quali file può toccare, quali namespace influenza e quali funzionalità del kernel può raggiungere.

I Cgroup Impediscono che un Processo Difettoso Diventi il Problema di Tutti

bash
mkdir -p /sys/fs/cgroup/demo
echo $$ > /sys/fs/cgroup/demo/cgroup.procs
echo "200M" > /sys/fs/cgroup/demo/memory.max
echo "100000 50000" > /sys/fs/cgroup/demo/cpu.max

Nemmeno l'isolamento senza controllo delle risorse è sufficiente. Se un processo può consumare tutta la memoria, fare fork all'infinito, o congestionare la CPU, dargli il proprio hostname non protegge davvero nulla. Il processo può sembrare isolato, ma può comunque danneggiare tutto il resto sulla macchina.

Ecco a cosa servono i cgroup: sono il meccanismo Linux per mettere confini di risorse intorno ai processi. I cgroup v1 dividevano le cose su più gerarchie e diventavano disordinati in fretta. I cgroup v2 hanno sistemato questo usando una singola gerarchia unificata. Ma il modello di base è abbastanza semplice: crea un gruppo, imposta dei limiti e inserisci i processi in quel gruppo.

È semplice e utile. Anche un "batch job interno sicuro" può causare danni se non ha limiti di memoria o CPU e la piattaforma lo lascia comportarsi come se possedesse l'host. Docker e Kubernetes dipendono entrambi da cgroup sotto per i limiti di memoria, CPU, I/O e PID. Quindi questo è decisamente uno di quei controlli che vuoi usare intenzionalmente!

Isolamento dei Container con i Namespace Linux

bash
unshare --uts --pid --mount --net --ipc --fork bash
hostname demo
ps aux

Quando si parla di isolamento dei container, di solito si parla di namespace Linux più un po' di configurazione del filesystem. I namespace UTS danno a un container il proprio hostname, i namespace PID gli danno il proprio spazio di ID di processo, i namespace di mount gli danno la propria vista dei mount, e i namespace di rete separano le interfacce, le route e lo stato del firewall. I namespace IPC fanno lo stesso per la memoria condivisa e i semafori, mentre i namespace cgroup nascondono il layout cgroup dell'host. Queste funzionalità cambiano cosa un processo può vedere e quanto del sistema può osservare.

Se avvii una shell all'interno di un nuovo namespace PID, ottiene il suo albero di processi e può persino credere di essere PID 1. Ma dall'host, non è successo nulla di speciale. È solo un altro processo host con una visione diversa del mondo. I container non sono al di sopra del sistema operativo o in esecuzione accanto ad esso, sono processi regolari che vivono all'interno del kernel dell'host, con un po' di isolamento attentamente applicato intorno a loro.

Attenzione a chroot! chroot cambia la directory radice apparente, ma da solo non fornisce molto isolamento. È più vicino a un trucco del filesystem che a una funzionalità di sicurezza seria. I namespace utente sono più interessanti: ti permettono di mappare root all'interno del container a un utente non privilegiato sull'host, che è uno dei modi più puliti per limitare il blast radius quando qualcosa va storto. MA! I namespace possono nascondere parti dell'host, non fanno sparire l'host.

Rafforzare l'Isolamento Significa Più di una Linea di Difesa

json
{
  "defaultAction": "SCMP_ACT_ERRNO",
  "syscalls": [
    { "names": ["read", "write", "exit", "futex"], "action": "SCMP_ACT_ALLOW" }
  ]
}

La parte rischiosa dei container è il kernel condiviso, quindi la maggior parte del lavoro di hardening si riduce a ridurre quanto di quel kernel un workload può effettivamente toccare. Seccomp aiuta molto perché filtra le syscall, e le syscall sono la porta d'ingresso nel kernel. AppArmor e SELinux spingono le cose oltre aggiungendo il controllo di accesso obbligatorio intorno a file, capability, comportamento dei processi ed etichette. Non sono amichevoli come "esegui solo il container", ma possono impedire che una situazione brutta peggiori.

Una volta che si vuole un isolamento più forte, si inizia a guardare oltre il modello di container predefinito. Qualcosa come gVisor mette un kernel in user-space tra il workload e l'host, dando al processo un percorso più stretto e controllato nel kernel reale. Kata Containers adottano un approccio diverso e avvolgono i workload in VM leggere, mentre Firecracker va ancora più in quella direzione con microVM progettate per un avvio rapido e un'alta densità. Gli unikernels, invece, riducono le cose al minimo costruendo immagini specializzate con una superficie OS molto ridotta sin dall'inizio.

Questi strumenti esistono perché condividere un kernel è efficiente, ma crea anche un vero confine di rischio su cui devi riflettere. Per i servizi interni fidati, l'hardening di base è spesso sufficiente, ma per il codice non fidato, i workload dei clienti, o la multitenancy aggressiva, fermarsi ai namespace non sembra serio.

Le Macchine Virtuali Danno un Muro Più Duro

text
Container app -> Host kernel -> Hardware
VM app -> Guest kernel -> Hypervisor -> Hardware

Una VM cambia il confine di fiducia in un modo molto semplice: il guest ottiene il proprio kernel, mentre un container condivide il kernel dell'host. Questa è la differenza principale dal punto di vista della sicurezza.

Il modo in cui funziona è attraverso l'hypervisor, che si trova tra il guest e l'hardware. Gli hypervisor di tipo 1 girano vicino al metallo, mentre gli hypervisor di tipo 2 girano sopra un sistema operativo host. KVM è qualcosa di interessante che ha trasformato Linux in un hypervisor molto capace basandosi sul supporto alla virtualizzazione hardware e integrando quell'approccio in modo pulito nel kernel.

Il vecchio modello era trap-and-emulate: il guest cercava di eseguire un'istruzione sensibile, l'hypervisor la intercettava e poi la gestiva in modo sicuro. Le CPU moderne hanno migliorato tutto questo, e l'idea di base è ancora la stessa: con una VM, il kernel guest non è il kernel host. Questo è importante quando si esegue codice di cui non ci si fida, perché nella multitenancy ostile un confine VM è spesso una scelta più sicura rispetto a un kernel condiviso con una lunga lista di regole di hardening. Le VM hanno costi reali, poiché sono più pesanti, più lente da avviare e più costose da patchare e gestire, ma questo compromesso può essere ragionevole quando l'alternativa è mettere workload non fidati "a un bug del kernel di distanza" l'uno dall'altro. I container e le VM non sono strumenti in competizione, ma strumenti per diversi modelli di fiducia: i container si adattano bene ai workload fidati, e le VM possono spesso essere l'opzione migliore quando la fiducia è debole.

Immagini Container e Sicurezza della Supply Chain Software

Dockerfile
# Bad
FROM ubuntu:latest RUN apt-get update && apt-get install -y curl
 
# Better
FROM cgr.dev/chainguard/static:latest COPY app /app USER 65532:65532 ENTRYPOINT
["/app"]

Un'immagine container è davvero solo un root filesystem più un po' di configurazione, ma guarda quante mani la toccano prima che raggiunga la produzione! I developer la costruiscono, i sistemi CI la modificano, i registry la memorizzano, i controller di ammissione la ispezionano, i runtime la scaricano e i cluster infine la eseguono, quindi ognuno di questi passaggi fa parte della supply chain.

Gli standard OCI hanno aiutato molto perché hanno dato all'ecosistema formati comuni per immagini e distribuzione, e questo ha rimosso molta attrito. Ma la domanda più difficile è se l'immagine che stai per eseguire sia effettivamente quella che intendevi eseguire, costruita dalle sorgenti che ti aspettavi, con le dipendenze che pensavi di ottenere. I layer delle immagini sono ottimi per l'efficienza, ma tengono anche la storia in giro, quindi se un segreto viene copiato in un layer e rimosso in seguito, potrebbe essere ancora presente nel record dell'immagine. Se il tuo Dockerfile scarica qualunque repository di pacchetti venga servito durante la build, senza fissare le versioni o tracciare la provenienza, devi considerare l'intero sistema esterno nei tuoi calcoli di sicurezza.

C'è anche il processo di build stesso, e docker build ha un bel po' di bagagli: accesso ampio al daemon, contesti di build disordinati, fughe accidentali di segreti, edge case con symlink e più interazione con l'host di quanto si pensi. Le build senza daemon o rootless migliorano la situazione, così come contesti più piccoli, Dockerfile più puliti e build riproducibili. Un modello mentale utile: un Dockerfile non è solo configurazione, ma è la policy eseguibile della supply chain, quindi vale la pena trattarlo con la stessa cura del codice applicativo: usa immagini base minimali, fissa i digest invece dei tag, dividi le fasi di build e runtime, tieni i segreti fuori dal contesto di build.

La Sicurezza delle Immagini Non È Solo lo Scanning

bash
cosign sign my-registry.example.com/myapp@sha256:abc123
cosign verify my-registry.example.com/myapp@sha256:abc123

La sicurezza delle immagini ha due lati, build time e deploy time. Con questo in mente, le pipeline di build sembrano il posto su cui concentrarsi, ma l'immagine è sicura solo se ci si può fidare sia di come è stata prodotta che di cosa effettivamente gira nel cluster.

Al momento della build, la grande domanda è la provenienza: chi ha scritto il Dockerfile, da quale immagine base è partito, quali pacchetti o artifact sono stati inclusi e quale builder ha prodotto l'immagine finale. Se le cose vanno storte, la macchina di build stessa diventa la superficie d'attacco, e questa è una cattiva posizione in cui trovarsi perché un attaccante non ha bisogno di compromettere direttamente la produzione. Può compromettere il percorso di build, avvelenare la release del giorno dopo e poi aspettare che tu la distribuisca.

Una volta che l'applicazione è distribuita, la domanda è se il cluster sta eseguendo l'esatta immagine che hai approvato. Qui i tag diventano un problema, perché latest è solo un puntatore mobile, e anche i tag di versione che sembrano stabili possono essere retaggati e riutilizzati. I digest sono la vera identità OG, ecco perché il controllo di ammissione è la cosa più importante: se un deployment punta all'immagine sbagliata, a un'immagine non firmata o a un'immagine dal registry sbagliato, il cluster dovrebbe rifiutarla prima che qualsiasi cosa parta. GitOps aiuta rendendo lo stato desiderato visibile e revisionabile, ma non impone la sicurezza da solo, quindi un manifest difettoso o malevolo può comunque distribuire l'immagine sbagliata in modo molto pulito. Uno degli errori più comuni è firmare le immagini correttamente e poi distribuire comunque la cosa sbagliata perché il manifest usa un tag mutabile.

Lo Scanning delle Vulnerabilità Aiuta, ma...

bash
trivy image --severity HIGH,CRITICAL --ignore-unfixed myapp:latest
grype myapp:latest

Scansionare le immagini per le vulnerabilità è utile, ma gli strumenti non sono perfetti. Uno scanner guarda i pacchetti installati, le versioni e i metadata, poi cerca di mapparli ai database di advisory, il che ti dà un elenco di problemi noti. Questo è un segnale utile, ma non è la verità assoluta.

Molta confusione deriva da quanto siano disordinati i dati sottostanti. Le distribuzioni spesso applicano backport delle fix senza cambiare le versioni nel modo in cui uno scanner ingenuo si aspetta, alcuni CVE compaiono in sottopackage che non usi nemmeno, e alcuni problemi vengono marcati come won't-fix per ragioni (a volte discutibili). I nomi dei pacchetti differiscono tra le distribuzioni, i feed di advisory diventano obsoleti, gli scanner non sono d'accordo tra loro, e ogni tanto sono semplicemente sbagliati. Può capitare che uno scanner segnali un problema critico che la tua distribuzione ha già risolto tramite un backport che lo scanner non capisce.

Poi, lo scanning delle immagini copre solo una parte del rischio. Un'immagine statica piccola non è automaticamente sicura, perché l'applicazione ha ancora dipendenze, logica di parsing, codice di autenticazione e tutti i soliti modi in cui il software può fallire. L'approccio pratico è scansionare regolarmente, poiché nuovi advisory emergono dopo il momento della build, scansionare in CI/CD, ri-scansionare le immagini nei registry e bloccare le immagini ovviamente difettose dal raggiungere il deployment. Tuttavia, un report verde dallo scanner ti dice solo che lo strumento non ha trovato un problema noto in quel giorno.

La Maggior Parte delle Escape dei Container Inizia con Scelte Sbagliate

bash
docker run --privileged \
  -v /:/host \
  -v /var/run/docker.sock:/var/run/docker.sock \
  ubuntu

Le kernel escape sono interessanti, ma la maggior parte dei fallimenti di isolamento nel mondo reale sono più basilari. I container spesso girano come root per impostazione predefinita, che è una cattiva decisione, soprattutto quando l'applicazione non ha alcuna reale necessità di farlo. Poi qualcuno aggiunge --privileged perché qualcosa ha fallito e voleva la soluzione rapida, monta una directory host sensibile perché era comodo, e alla fine monta il socket Docker perché è "solo per l'automazione". Un altro elenco di cattive decisioni!

I container rootless sono utili, perché riducono i privilegi anche sul lato del runtime, non solo all'interno del container. Girare come non-root all'interno del container è anche un buon passo, ma da solo non è sufficiente. I namespace condivisi sono un altro modo facile per fare errori, poiché un container che condivide la rete host o il namespace PID ottiene molta più visibilità e portata di quanto le persone realizzino. I sidecar possono creare problemi simili, specialmente quando un debug sidecar che doveva essere temporaneo diventa un tunnel permanente nella produzione.

In molti casi, non hai bisogno di una sofisticata container escape perché l'ambiente ha già ceduto la maggior parte di quello che un attaccante vorrebbe. Se un container ha bisogno di --privileged, fermati e chiediti se una VM, un runtime diverso o un design applicativo diverso avrebbe più senso.

La Sicurezza di Rete Riguarda il Taglio dei Percorsi

bash
iptables -A FORWARD -s 10.42.1.0/24 -d 10.42.2.0/24 -j DROP

Molti cluster sono configurati come reti di ufficio piatte, e questo non va bene. I container ottengono indirizzi IP attraverso bridge, overlay o plugin CNI, ma una volta che possono parlarsi, quella comunicazione dovrebbe essere consentita in primo luogo?

Nelle piattaforme container, la sicurezza di rete riguarda principalmente il taglio dei percorsi che non devono esistere. I controlli di livello 3 e 4 gestiscono il routing e il filtraggio dei pacchetti, iptables è stato il meccanismo per lungo tempo e IPVS ha migliorato il load balancing in alcuni percorsi. Le network policy di Kubernetes hanno dato ai team un modo più pulito per descrivere il traffico consentito, ma scrivere una policy è solo metà del lavoro. La policy aiuta solo se il plugin di rete la supporta effettivamente e la applica nel modo in cui pensi.

Il consiglio pratico è: default deny, restringi sia ingress che egress, separa i workload per livello di fiducia e tratta DNS, endpoint di metadata e percorsi interni del control plane come sensibili. Assumi che se un servizio viene compromesso, cercherà di muoversi lateralmente il prima possibile. Le service mesh possono aiutare con l'identità, la policy del traffico e la crittografia, ma sono anche una seccatura da mantenere, quindi usale solo se ne hai davvero bisogno.

TLS Perché le Reti Interne Non Sono Sicure per Default

bash
openssl req -new -newkey rsa:2048 -nodes \
  -keyout server.key -out server.csr
 
openssl x509 -req -in server.csr \
  -CA ca.crt -CAkey ca.key -CAcreateserial \
  -out server.crt -days 365

È sbagliato pensare che il traffico all'interno del cluster sia fidato solo perché non lascia mai le reti interne. Le reti interne sono piene di errori, scorciatoie, accesso ampio e workload che potrebbero già essere compromessi.

TLS aiuta perché fornisce confidenzialità e integrità, ma la parte più interessante è l'identità. Un certificato lega una chiave pubblica a un soggetto, una CA firma quel legame, e la chiave privata deve rimanere privata altrimenti l'intero modello inizia a crollare. Una CSR è solo il modo in cui un servizio chiede quel certificato, e durante l'handshake TLS le due parti validano la catena di certificati, provano di controllare le chiavi giuste, negoziano la crittografia che useranno e derivano le chiavi di sessione per la connessione. Il mutual TLS porta questo un passo avanti facendo sì che entrambe le parti provino chi sono, che è utile nelle piattaforme container dove il traffico service-to-service è costante e le ipotesi di fiducia possono diffondersi rapidamente.

I certificati a breve scadenza e la rotazione affidabile di solito ti danno qualcosa di meglio della sola macchina di revoca, perché riducono la finestra del danno e tengono il processo sotto controllo. Ignorare la rotazione dei certificati fino alla settimana prima della scadenza non è un grande piano di sicurezza.

Gestione dei Segreti nei Container

bash
mkdir -p /run/secrets
chmod 0700 /run/secrets
printf '%s' "$DB_PASSWORD" > /run/secrets/db_password
chmod 0400 /run/secrets/db_password

I segreti non sono solo configurazione: hanno bisogno di confidenzialità, integrità, controllo degli accessi, rotazione e qualche modo di verificare chi li ha toccati e quando, perché una volta perso il controllo di un segreto, hai dei problemi.

Il posto peggiore dove mettere un segreto è all'interno dell'immagine, perché una volta che viene integrato, tende a essere copiato, messo in cache, scansionato, rispecchiato e trattenuto in più posti di quanti tu possa tracciare. La prossima cattiva opzione è spesso una variabile d'ambiente, che sembra comoda, ma quella comodità può trasformarsi in perdita attraverso log, crash dump, ispezione di processi o strumenti di debug. I file sono di solito più adatti, specialmente quando vivono su un filesystem in-memory con permessi stretti, e prelevare i segreti sulla rete è davvero l'opzione migliore quando il workload ha un'identità forte e la connessione è adeguatamente protetta.

I Kubernetes Secret sono utili, ma non risolvono tutto. Sono oggetti API che di solito finiscono in variabili d'ambiente o file, quindi hai ancora bisogno di proteggere etcd, abilitare la crittografia a riposo, bloccare RBAC e limitare chi può creare pod che montano segreti. Purtroppo, il root all'interno del container può solitamente leggere i segreti montati, quindi evitare root fa parte del mantenere l'esposizione dei segreti sotto controllo. In generale, non mettere segreti nelle variabili d'ambiente: potrebbero comparire nei log, nell'output dei crash o nei dati di debug.

Protezione del Runtime dei Container

yaml
- rule: Terminal shell in container
  condition: container and proc.name in (bash, sh, zsh)
  output: "Shell spawned in container %container.id"
  priority: WARNING

La protezione del runtime non riguarda il bloccare ogni attacco, ma il riuscire a distinguere tra un deploy normale e i primi minuti di un incidente.

Prima di tutto, hai bisogno di un quadro del comportamento normale. Di solito significa sapere da quale immagine è arrivato il container, quali eseguibili si prevede che lanci, quali file legge o scrive normalmente, con quale ID utente dovrebbe girare e con quali altri servizi dovrebbe comunicare. Una volta che hai quella baseline, il comportamento strano risalta più chiaramente. Un servizio web che genera bash è strano, un container che scrive nei percorsi di sistema è strano, e un workload che non ha mai parlato con internet che apre improvvisamente connessioni in uscita è strano anche questo. Lo stesso vale per un processo che si allontana da una stabile identità non-root e inizia a girare con privilegi diversi.

I rilevatori basati su eBPF, i monitor di syscall, i motori di policy e i profiler di comportamento stanno tutti cercando di aiutare a capire: questo container sta facendo quello che questo servizio dovrebbe fare, o si è spostato verso qualcos'altro? La prevenzione del drift aiuta anche, perché se l'immagine è destinata a essere immutabile, allora l'installazione di pacchetti a runtime o la comparsa improvvisa di nuovi binari dovrebbe sembrare subito sospetta.

I Container Non Ti Salvano dall'OWASP Top 10

python
query = f"SELECT * FROM users WHERE name = '{user_input}'"

La containerizzazione non risolve la sicurezza applicativa. I vecchi bug che hanno danneggiato i sistemi per anni sono ancora lì.

L'injection è ancora injection, l'autenticazione rotta ancora fa compromettere gli account, e l'esposizione di dati sensibili avviene ancora quando log, risposte o percorsi di archiviazione vengono gestiti male. XXE è ancora presente quando l'XML ostile viene analizzato male, il broken access control è ancora uno dei modi più rapidi per perdere dati reali, e il XSS colpisce ancora i browser esattamente come ha sempre fatto. La deserializzazione non sicura può ancora portare un'applicazione su percorsi di codice pericolosi, i componenti vulnerabili noti finiscono ancora in produzione quando i team li ignorano, e il logging e il monitoraggio deboli significano ancora che senti parlare dell'incidente dagli utenti o dagli attaccanti prima di sentirti dai tuoi sistemi.

Nelle piattaforme container, le misconfigurazioni tendono ad accumularsi invece di rimanere in un unico posto, e puoi sbagliare molte cose tutte insieme. Il punto chiave qui è che i container cambiano dove gira il software e quanto velocemente puoi sostituirlo, ma non cambiano se il software stesso è difettoso.

Cosa Correggere Prima se il Tuo Setup È Debole

Se la piattaforma è vulnerabile oggi, il posto migliore da cui iniziare sono i cambiamenti che migliorano la sicurezza senza aggiungere un'enorme quantità di complessità. Non hai bisogno di una grande revisione della sicurezza il primo giorno.

I passi pratici fanno molta strada: esegui i container come non-root, elimina le capability, imposta limiti di memoria e PID, smetti di integrare segreti nelle immagini, fissa i digest delle immagini invece dei tag, aggiungi controlli di ammissione per le immagini fidate, restringi i percorsi di rete tra i workload e abilita seccomp insieme a no-new-privileges. Questi sono il tipo di controlli che riducono il rischio senza costringere ogni team a diventare specialista della sicurezza dall'oggi al domani.

Questi passi di solito risolvono molti dei problemi che causano incidenti reali, e ti daranno anche un quadro migliore di cosa sta effettivamente girando nel cluster. Una volta che hai quello, puoi iniziare a guardare controlli più avanzati come gVisor, microVM o service mesh, ma le basi sono dove si trovano la maggior parte dei guadagni.

Fine!

L'idea principale è abbastanza semplice: i container sono ottimi strumenti di packaging, ma non sono un confine di sicurezza rigido out of the box. Probabilmente questa è la lezione più grande che ho tratto dall'esaminare la sicurezza dei container più seriamente.

Troppo privilegio, troppa fiducia in un kernel condiviso, troppa fede nei default e non abbastanza attenzione a cosa un workload può effettivamente raggiungere sono la causa di molti dei problemi. Una buona sicurezza dei container inizia come una ricetta semplice: gira come non-root, elimina le capability, stringi i controlli sulle immagini, restringi il networking, migliora l'isolamento, osserva il comportamento del runtime, gestisci i segreti correttamente e costruisci la fiducia nella supply chain.

I container sono ottimi perché sono veloci, portabili e molto bravi a trasformare il deployment in qualcosa di noioso in senso positivo. Ma non sono magici e non cancellano i confini di fiducia né rendono il kernel condiviso meno condiviso. Non correggono nemmeno i modelli di privilegio deboli, le reti piatte, la gestione sloppy dei segreti o il codice applicativo rotto solo perché tutto è ora avvolto in un'immagine e schedulato da un cluster.