Blog

Difendersi dalle Fughe di Container: Una Guida Pratica all'Isolamento Docker per l'Hosting Multi-Tenant

Scopri come proteggere i container Docker dalle vulnerabilità di fuga e dai fallimenti di isolamento negli ambienti multi-tenant con passaggi concreti ed esempi.

Riepilogo

I container Docker condividono il kernel host, rendendo l'isolamento critico, specialmente nell'hosting multi-tenant dove una singola fuga di container può compromettere tutti gli tenant. Molti sviluppatori presumono che i container siano macchine virtuali perfettamente isolate, ma la realtà è diversa. Questo articolo spiega le funzionalità del kernel Linux alla base dell'isolamento Docker (namespace, cgroup) e i vettori di attacco che li minacciano. Imparerai passaggi pratici per rafforzare la tua configurazione Docker: limitare i privilegi, utilizzare runtime sicuri, scansionare le immagini e implementare la segmentazione di rete. Seguendo un esempio reale di un provider di hosting WordPress multi-tenant, vedrai come applicare queste difese. Copriamo anche avvertenze come compromessi di prestazioni e l'uso di seccomp/AppArmor. L'obiettivo è fornirti una strategia di isolamento robusta che prevenga le fughe di container e mantenga al sicuro i tuoi tenant.

Introduzione

Se gestisci una piattaforma di hosting multi-tenant, che si tratti di hosting WordPress condiviso, un'applicazione SaaS o un servizio di ambiente di sviluppo, la fuga di container è lo scenario da incubo. Una vulnerabilità nel kernel o una configurazione errata può consentire a un tenant di uscire dal proprio container e accedere ai dati di altri tenant o all'host stesso. L'isolamento di Docker si basa su funzionalità del kernel Linux come namespace e cgroup, ma le configurazioni predefinite sono spesso insufficienti per una sicurezza robusta. Questo articolo ti guiderà attraverso i vettori di attacco e fornirà passaggi attuabili per bloccare i tuoi container Docker, illustrati con un esempio reale di WordPress multi-tenant. Per una visione più ampia dell'orchestrazione di produzione, consulta la nostra guida su Orchestrazione di Applicazioni Containerizzate Pronte per la Produzione.

Comprendere l'Isolamento Docker

I container Docker utilizzano i namespace Linux per fornire isolamento a livello di processo: i namespace PID isolano gli alberi dei processi, i namespace di rete separano le interfacce di rete, i namespace di mount isolano i mount del filesystem e i namespace utente consentono il mapping dell'utente root del container a un utente host non privilegiato. I Control Group (cgroup) limitano l'utilizzo delle risorse come CPU, memoria e I/O del disco. Queste funzionalità creano insieme una "sandbox" attorno a ciascun container. Tuttavia, a differenza di una macchina virtuale che esegue un kernel separato, i container condividono il kernel host. Ciò significa che una vulnerabilità nel kernel (ad esempio, CVE-2022-0492) può essere sfruttata per uscire dall'isolamento del namespace del container. Inoltre, configurazioni errate come l'esecuzione di container come root all'interno del container, la concessione di tutte le capacità al container o la mancata revoca delle capacità Linux non necessarie possono ampliare la superficie di attacco.

Vettori di Attacco

I vettori di attacco comuni includono:

  • Exploit del kernel: Sfruttare un bug nel kernel host per ottenere l'accesso all'host.
  • Container privilegiati: L'esecuzione con --privileged concede tutte le capacità e aggira la maggior parte dell'isolamento.
  • Abuso di capacità: Anche senza la modalità privilegiata completa, un container con capacità pericolose come CAP_SYS_ADMIN o CAP_NET_ADMIN può montare filesystem o manipolare le impostazioni di rete.
  • Pratiche di immagine insicure: Utilizzo di immagini base con vulnerabilità note o inclusione di strumenti non necessari come compilatori o interpreti di shell.
  • Namespace di mount condivisi: Montare directory host nei container può consentire la fuga se non sono in sola lettura.

Passaggi Pratici di Sicurezza

1. Esegui i Container come Utente Non Root

Per impostazione predefinita, Docker esegue i container come root all'interno del container. Se un attaccante ottiene i privilegi di root all'interno del container, ha più leva. Crea un utente nel tuo Dockerfile e utilizza la direttiva USER. Evita anche di utilizzare il flag --user in Docker Compose per mappare a un utente host arbitrario, se possibile.

2. Revoca Tutte le Capacità e Aggiungi Solo Quelle Necessarie

Le capacità Linux suddividono i privilegi di superutente in unità più piccole. In Docker Compose, usa cap_drop: ALL e poi cap_add solo quelle richieste (ad esempio, NET_BIND_SERVICE). Evita capacità pericolose come SYS_ADMIN, NET_ADMIN, SYS_PTRACE.

3. Usa il Filesystem Root in Sola Lettura

Imposta read_only: true nella definizione del tuo container. Ciò impedisce agli attaccanti di scrivere sul filesystem del container. Se la tua app necessita di scrivere file temporanei, monta un volume tmpfs in quella posizione.

4. Abilita la Rimappatura dei Namespace Utente

La rimappatura dei namespace utente mappa l'utente root del container a un utente host non root. Ciò aggiunge un livello di isolamento, poiché anche se un utente root del container dovesse fuggire, avrebbe i privilegi dell'utente rimappato. Abilitala in /etc/docker/daemon.json con "userns-remap": "default". Tieni presente che ciò può complicare i permessi dei volumi. Per maggiori dettagli, consulta Mastering Docker Isolation for Secure and Efficient Web Hosting.

5. Applica Profili Seccomp e AppArmor/AppArmor

Seccomp limita le chiamate di sistema che un container può effettuare. Docker fornisce un profilo seccomp predefinito che blocca le syscall pericolose. Puoi anche creare profili personalizzati. Allo stesso modo, AppArmor (o SELinux) fornisce il controllo di accesso obbligatorio. Utilizza AppArmor per confinare il tuo container a un set minimo di operazioni consentite. Il profilo di sicurezza può essere impostato tramite security_opt in Docker Compose.

6. Usa Immagini Base Minimali e Scansiona le Vulnerabilità

Scegli immagini piccole come Alpine o Distroless che hanno una superficie di attacco ridotta. Scansiona regolarmente le immagini con strumenti come Docker Scout, Trivy o Clair. Integra la scansione nella tua pipeline CI/CD per impedire la distribuzione di immagini vulnerabili.

7. Segmentazione di Rete con Reti Bridge Personalizzate

Crea reti bridge separate per ciascun tenant o livello applicativo. Ciò limita il traffico est-ovest. In Docker Compose, definisci le reti e isola i servizi. Usa internal: true se un servizio non necessita di accesso a Internet in uscita. Le regole del firewall sull'host limitano ulteriormente il traffico inter-container.

8. Limita le Risorse con Cgroup

Imposta limiti di CPU e memoria in Docker Compose utilizzando deploy.resources.limits. Ciò impedisce a un container compromesso di lanciare un attacco di esaurimento delle risorse. Inoltre, imposta kernel_memory e memory_reservation per un controllo più preciso.

Esempio Reale: Hosting WordPress Multi-Tenant con Docker Compose

Considera uno scenario in cui ospiti più siti WordPress per diversi clienti, ciascuno nel proprio container Docker. Una configurazione insicura potrebbe apparire così:

version: '3'
services:
  wordpress:
    image: wordpress:latest
    ports:
      - "8080:80"
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: exampleuser
      WORDPRESS_DB_PASSWORD: examplepass
      WORDPRESS_DB_NAME: exampledb
    volumes:
      - ./wp-content:/var/www/html/wp-content
  db:
    image: mysql:5.7
    environment:
      MYSQL_DATABASE: exampledb
      MYSQL_USER: exampleuser
      MYSQL_PASSWORD: examplepass
      MYSQL_ROOT_PASSWORD: somewordpress
    volumes:
      - db_data:/var/lib/mysql
volumes:
  db_data:

Questa configurazione è vulnerabile: il container WordPress viene eseguito come root all'interno, ha tutte le capacità (poiché nessuna è stata revocata), monta una directory host con accesso in scrittura e ha accesso illimitato alla rete.

Ora rafforziamola:

version: '3'
services:
  wordpress:
    image: wordpress:latest
    user: www-data
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE
    read_only: true
    tmpfs:
      - /var/www/html/wp-content/plugins
    security_opt:
      - seccomp=seccomp-profile.json
      - apparmor=wordpress-profile
    networks:
      - frontend
    ports:
      - "8080:80"
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: exampleuser
      WORDPRESS_DB_PASSWORD: examplepass
      WORDPRESS_DB_NAME: exampledb
    volumes:
      - wp-uploads:/var/www/html/wp-content/uploads
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 256M
  db:
    image: mysql:5.7
    user: mysql
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE
    networks:
      - backend
    environment:
      MYSQL_DATABASE: exampledb
      MYSQL_USER: exampleuser
      MYSQL_PASSWORD: examplepass
      MYSQL_ROOT_PASSWORD: somewordpress
    volumes:
      - db_data:/var/lib/mysql
    deploy:
      resources:
        limits:
          cpus: '0.25'
          memory: 128M
networks:
  frontend:
    driver: bridge
    internal: false
  backend:
    driver: bridge
    internal: true
volumes:
  wp-uploads:
  db_data:

Miglioramenti chiave:

  • Entrambi i container vengono eseguiti come utenti non root (www-data e mysql).
  • Tutte le capacità sono state revocate, solo NET_BIND_SERVICE è stata aggiunta.
  • Il filesystem di WordPress è in sola lettura tranne per un mount tmpfs e un volume di upload.
  • Sono stati applicati profili Seccomp e AppArmor (sarebbe necessario fornire profili personalizzati).
  • Reti separate isolano il web dal database, con la rete del database interna.
  • Limiti di risorse prevengono l'esaurimento delle risorse.

Per saperne di più sul rafforzamento specifico di Docker per WordPress, consulta Docker per WordPress: Perché i Container Isolati Cambiano Tutto.

Avvertenze

  • Rimappatura Namespace Utente: Sebbene potente, interrompe il montaggio dei volumi perché l'UID host rimappato non è lo stesso dell'UID del container. Potrebbe essere necessario pre-creare directory con i permessi corretti o utilizzare volumi Docker con supporto alla rimappatura.
  • Profili Seccomp/AppArmor: I profili personalizzati richiedono la comprensione delle syscall della tua applicazione e dei pattern di accesso ai file. Profili eccessivamente restrittivi possono interrompere la funzionalità. Testa a fondo.
  • Prestazioni: Strati di sicurezza aggiuntivi come seccomp e AppArmor hanno un overhead minimo, ma i limiti di risorse e i filesystem in sola lettura potrebbero influire sulle applicazioni con molte scritture.
  • Complessità dell'Orchestrazione: In un ambiente multi-tenant, la gestione dei file Docker Compose per tenant può diventare complessa. Considera l'utilizzo di uno strumento di orchestrazione di livello superiore come Kubernetes, ma ciò introduce le proprie considerazioni sulla sicurezza.

Conclusione

La fuga di container è una minaccia reale nell'hosting Docker multi-tenant, ma è prevenibile. Comprendendo i meccanismi di isolamento e applicando la difesa in profondità: revoca delle capacità, esecuzione come non root, abilitazione dei namespace utente, seccomp, AppArmor, segmentazione di rete e scansione regolare delle immagini, puoi ridurre drasticamente il rischio. Ricorda che le impostazioni predefinite di Docker non sono pronte per la produzione per carichi di lavoro multi-tenant. Implementa questi passaggi oggi stesso per proteggere i tuoi tenant e la tua infrastruttura. Per una panoramica completa delle migliori pratiche di sicurezza Docker, fai riferimento a Proteggere le Tue Applicazioni Web con Docker: Una Guida Pratica all'Isolamento e alle Best Practice.

Sources (5)