Sicurezza dei Container Linux 2026: Dal Malware VoidLink alla Difesa in Profondità

Analisi del malware VoidLink e guida pratica all'hardening dei container Linux: profili seccomp, AppArmor, runtime sandboxed con gVisor e Kata Containers, monitoraggio con Falco e Tetragon. Checklist operativa inclusa.

Introduzione: Perché la Sicurezza dei Container È Diventata Critica nel 2026

Se gestite infrastrutture Linux in produzione, è praticamente certo che Docker e Kubernetes facciano parte del vostro stack. I container hanno cambiato radicalmente il modo in cui facciamo deploy delle applicazioni — su questo siamo tutti d'accordo. Ma hanno anche aperto una superficie d'attacco completamente nuova, e nel 2026 gli attaccanti la stanno sfruttando come mai prima d'ora.

Il caso che ha fatto più rumore? VoidLink.

Si tratta di un framework malware sofisticato, scoperto a dicembre 2025, progettato specificamente per ambienti cloud e container Linux. Scritto in Zig, utilizza rootkit basati su eBPF e LD_PRELOAD, un sistema modulare con oltre 37 plugin, e la capacità di rilevare automaticamente il cloud provider su cui gira — AWS, GCP, Azure, Alibaba o Tencent. La cosa inquietante è che VoidLink non si limita a eludere gli strumenti di sicurezza: li profila attivamente e adatta il proprio comportamento di conseguenza.

E non è un caso isolato. Nel 2025, oltre il 65% delle violazioni di sicurezza dei container ha sfruttato privilegi root all'interno dei container stessi. Gli attacchi di container escape — dove l'attaccante esce dal container per compromettere l'host — sono diventati una minaccia concreta, con CVE critiche che dimostrano quanto sia sottile il confine tra isolamento containerizzato e sistema host.

In questo articolo andiamo a fondo sulle minacce attuali alla sicurezza dei container Linux e, cosa più importante, sulle strategie di hardening concrete che potete applicare oggi. Dalla configurazione di profili seccomp personalizzati all'implementazione di runtime sandboxed come gVisor e Kata Containers, passando per il monitoraggio runtime con strumenti eBPF — c'è tutto quello che serve per costruire una difesa in profondità che funzioni davvero.

VoidLink: Anatomia di un Malware Cloud-Native

Prima di parlare di difese, bisogna capire cosa stiamo affrontando. VoidLink rappresenta una nuova generazione di malware pensato specificamente per gli ambienti containerizzati e cloud-native. E, onestamente, è impressionante dal punto di vista tecnico (purtroppo).

Architettura e Meccanismo di Distribuzione

VoidLink utilizza un meccanismo di distribuzione a tre stadi, progettato per minimizzare la sua impronta su disco e sfuggire all'analisi statica:

  • Stadio 1 — Loader iniziale: un binario minimale che stabilisce la persistenza e scarica il componente successivo in memoria
  • Stadio 2 — Implant core: il cuore del framework, scritto in Zig, che gestisce la comunicazione C2 e il caricamento dei plugin
  • Stadio 3 — Rootkit e plugin: moduli caricati dinamicamente per evasione, persistenza, movimento laterale e esfiltrazione dati

Il Sistema di Rootkit Adattivo

Una delle caratteristiche più insidiose di VoidLink è la selezione automatica del rootkit in base alla versione del kernel. In pratica, si adatta a qualsiasi ambiente trovi:

# Logica di selezione del rootkit (semplificata)
# VoidLink determina automaticamente quale tecnica di evasione utilizzare

Kernel < 4.0  →  LD_PRELOAD hooking
  - Intercetta le chiamate a funzioni di libreria (libc)
  - Nasconde processi, file e connessioni di rete
  - Funziona a livello userspace

Kernel 4.0 - 5.4  →  Loadable Kernel Module (LKM)
  - Modulo kernel compilato on-demand dal server C2
  - Il server C2 compila il modulo per la versione kernel specifica del target
  - Opera a livello kernel con pieno accesso

Kernel ≥ 5.5 con supporto eBPF  →  eBPF rootkit
  - Attacca programmi eBPF ai tracepoint del kernel
  - Filtra e manipola le informazioni visibili agli strumenti di monitoraggio
  - Estremamente difficile da rilevare

La compilazione lato server dei moduli kernel è la parte che preoccupa di più: il server C2 riceve le informazioni sul kernel del target e compila rootkit personalizzati al volo. Non esiste una singola firma statica da cercare — ogni deployment è unico. Questo rende l'approccio tradizionale basato su firme sostanzialmente inutile.

Rilevamento dell'Ambiente Cloud e Container

VoidLink è progettato per essere consapevole del contesto in cui opera. Ecco come rileva l'ambiente circostante:

# Tecniche di rilevamento cloud utilizzate da VoidLink

# 1. Rilevamento cloud provider via metadata endpoint
curl -s http://169.254.169.254/latest/meta-data/  # AWS
curl -s http://metadata.google.internal/             # GCP
curl -s http://169.254.169.254/metadata/instance     # Azure

# 2. Rilevamento container Docker
# Controlla l'esistenza di /.dockerenv
test -f /.dockerenv

# Controlla cgroup per indicatori Docker
grep -q "docker\|containerd" /proc/1/cgroup

# 3. Rilevamento Kubernetes
# Controlla variabili d'ambiente del service account
env | grep KUBERNETES_SERVICE

# Controlla mount del token
test -f /var/run/secrets/kubernetes.io/serviceaccount/token

Quando VoidLink rileva di trovarsi dentro un container Kubernetes, attiva automaticamente plugin specifici per la discovery dei servizi nel cluster, l'escalation dei privilegi tramite service account, e il movimento laterale verso altri pod. Insomma, sa esattamente dove si trova e cosa fare.

Evasione Attiva dei Prodotti di Sicurezza

VoidLink non si limita a evitare il rilevamento in modo passivo. Il malware profila attivamente i prodotti CDR, EDR e XDR installati — a livello di processo e di percorso — e adatta il proprio comportamento. Quando rileva strumenti di detection and response, modifica il timing del beacon C2 per ridurre la probabilità di essere individuato tramite analisi dei pattern. In parole povere: sa chi lo sta cercando e si nasconde di conseguenza.

Container Escape: Come gli Attaccanti Fuggono dall'Isolamento

L'isolamento dei container Linux si basa su tre meccanismi fondamentali del kernel: namespace, cgroup e capability. Quando uno di questi viene compromesso, l'attaccante può uscire dal container e prendere il controllo dell'host.

Vediamo i vettori di escape più comuni documentati nel 2025-2026.

1. Container Privilegiati

Eseguire un container con --privileged è, di fatto, equivalente a dare all'attaccante accesso diretto all'host. Eppure lo vedo ancora fare in produzione più spesso di quanto si vorrebbe ammettere:

# PERICOLOSO: container privilegiato
docker run --privileged -it ubuntu bash

# Un attaccante all'interno può montare il filesystem dell'host
mkdir /mnt/host
mount /dev/sda1 /mnt/host

# Accesso completo al filesystem dell'host
ls /mnt/host/etc/shadow
cat /mnt/host/etc/ssh/sshd_config

# Oppure caricare moduli kernel
insmod /path/to/malicious_module.ko

2. Socket Docker Esposto

Montare il socket Docker all'interno di un container è una pratica comune (soprattutto nei pipeline CI/CD) ma estremamente pericolosa:

# PERICOLOSO: montare il socket Docker
docker run -v /var/run/docker.sock:/var/run/docker.sock ubuntu

# Un attaccante può creare un container privilegiato dall'interno
docker run --privileged --pid=host -v /:/host ubuntu chroot /host

3. Capability Eccessive

Alcune capability Linux possono essere sfruttate per l'escape. La regola d'oro è: se non sapete esattamente perché vi serve una capability, non aggiungetela.

# CAP_SYS_ADMIN — la più pericolosa
# Permette mount, umount, e molte altre operazioni privilegiate
docker run --cap-add SYS_ADMIN -it ubuntu bash

# CAP_SYS_PTRACE — debug di processi
# Può essere usata per iniettare codice in processi dell'host
docker run --cap-add SYS_PTRACE --pid=host -it ubuntu bash

# CAP_NET_ADMIN — configurazione di rete
# Può manipolare regole di routing e firewall
docker run --cap-add NET_ADMIN --network=host -it ubuntu bash

4. Vulnerabilità del Kernel

Questo è il punto più critico, e spesso il meno considerato. Poiché tutti i container condividono lo stesso kernel dell'host, una vulnerabilità del kernel può essere sfruttata dall'interno di qualsiasi container per compromettere l'intero sistema. CVE critiche nel 2025 hanno dimostrato exploit di escape tramite vulnerabilità in namespace, cgroup e nel filesystem OverlayFS.

Hardening con Profili Seccomp: Filtrare le System Call

Seccomp (Secure Computing Mode) è un meccanismo del kernel Linux che restringe le system call disponibili per un processo. È la prima linea di difesa contro gli attacchi di container escape e l'esecuzione di codice malevolo — e, a mio avviso, una delle più sottovalutate.

Il Profilo Seccomp di Default di Docker

Docker include un profilo seccomp di default che blocca circa 44 system call su oltre 300 disponibili. Copre le syscall più pericolose, ma la superficie d'attacco rimane significativa:

# Verificare se seccomp è attivo nel container
docker run --rm alpine grep Seccomp /proc/1/status
# Output atteso:
# Seccomp:     2
# Seccomp_filters:  1

# ATTENZIONE: Kubernetes NON abilita seccomp di default!
# Dovete configurarlo esplicitamente

Quel dettaglio su Kubernetes è fondamentale e prende alla sprovvista molti team. Se state migrando da Docker a Kubernetes e pensate che seccomp sia "già attivo", vi sbagliate.

Creare un Profilo Seccomp Personalizzato

Il gold standard per la produzione è creare profili seccomp su misura per ogni workload. Il processo non è complicato, ma richiede attenzione.

Passo 1: Profilare le system call necessarie

# Usare strace per identificare le syscall utilizzate dalla vostra applicazione
strace -c -f -p $(pgrep my-app) -e trace=all 2>&1 | tail -30

# Oppure utilizzare il profilo seccomp di logging di Docker
# per registrare tutte le syscall utilizzate
docker run --security-opt seccomp=audit.json my-image

# Esempio con sysdig per monitorare le syscall di un container
sysdig -p "%evt.type" container.name=my-container | sort | uniq -c | sort -rn

Passo 2: Creare il profilo personalizzato

{
  "defaultAction": "SCMP_ACT_ERRNO",
  "defaultErrnoRet": 1,
  "archMap": [
    {
      "architecture": "SCMP_ARCH_X86_64",
      "subArchitectures": ["SCMP_ARCH_X86", "SCMP_ARCH_X32"]
    }
  ],
  "syscalls": [
    {
      "names": [
        "read", "write", "close", "fstat", "lseek",
        "mmap", "mprotect", "munmap", "brk",
        "rt_sigaction", "rt_sigprocmask",
        "ioctl", "access", "pipe", "select",
        "sched_yield", "mremap", "msync",
        "clone", "execve", "exit", "wait4",
        "kill", "uname", "fcntl", "flock",
        "fsync", "fdatasync", "getcwd",
        "openat", "newfstatat", "readlinkat",
        "socket", "connect", "accept",
        "sendto", "recvfrom", "bind", "listen",
        "epoll_create1", "epoll_ctl", "epoll_wait",
        "getrandom", "futex", "clock_gettime",
        "nanosleep", "exit_group"
      ],
      "action": "SCMP_ACT_ALLOW"
    },
    {
      "names": [
        "mount", "umount2", "pivot_root",
        "ptrace", "kexec_load", "reboot",
        "init_module", "finit_module", "delete_module",
        "io_uring_setup", "io_uring_enter", "io_uring_register",
        "bpf", "perf_event_open",
        "unshare", "setns"
      ],
      "action": "SCMP_ACT_ERRNO",
      "errnoRet": 1,
      "comment": "System call pericolose: bloccate esplicitamente"
    }
  ]
}

Notate come questo profilo blocchi esplicitamente io_uring_setup, bpf e ptrace — system call utilizzate sia dal rootkit Curing (trattato nel nostro articolo precedente sulla sicurezza di io_uring) sia dal framework VoidLink.

Passo 3: Applicare il profilo

# Docker
docker run --security-opt seccomp=my-seccomp-profile.json my-image

# Docker Compose
services:
  my-app:
    image: my-image
    security_opt:
      - seccomp:my-seccomp-profile.json

# Kubernetes — SecurityContext del Pod
apiVersion: v1
kind: Pod
metadata:
  name: my-secure-pod
spec:
  securityContext:
    seccompProfile:
      type: Localhost
      localhostProfile: profiles/my-seccomp-profile.json
  containers:
  - name: my-container
    image: my-image

AppArmor: Controllo d'Accesso Obbligatorio per Container

AppArmor è un modulo di sicurezza del kernel Linux che implementa il Mandatory Access Control (MAC). Mentre seccomp filtra le system call, AppArmor controlla l'accesso a file, directory, capability e risorse di rete a livello di path. Sono complementari, e usarli entrambi è fortemente consigliato.

Il Profilo Docker-Default

Docker genera automaticamente un profilo AppArmor chiamato docker-default per ogni container. Questo profilo fa il suo lavoro base:

  • Impedisce la scrittura in /proc e /sys (con alcune eccezioni)
  • Impedisce il mount di filesystem
  • Limita l'accesso a /proc/sysrq-trigger
  • Blocca la modifica dei cgroup

Ma per la produzione, serve qualcosa di più mirato.

Creare un Profilo AppArmor Personalizzato per Container

# /etc/apparmor.d/containers/my-secure-container

#include <tunables/global>

profile my-secure-container flags=(attach_disconnected,mediate_deleted) {
  #include <abstractions/base>

  # Nega accesso a informazioni sensibili dell'host
  deny /etc/shadow r,
  deny /etc/passwd r,
  deny /etc/ssh/** rwklx,
  deny /root/.ssh/** rwklx,

  # Nega accesso ai metadata cloud
  deny network inet stream peer=(addr=169.254.169.254),

  # Nega accesso al socket Docker
  deny /var/run/docker.sock rw,

  # Permetti accesso in lettura alle librerie di sistema
  /lib/** r,
  /usr/lib/** r,
  /usr/local/lib/** r,

  # Permetti accesso alla directory dell'applicazione
  /app/** r,
  /app/data/** rw,
  /app/logs/** rw,

  # Permetti operazioni di rete necessarie
  network inet tcp,
  network inet udp,
  network inet6 tcp,
  network inet6 udp,

  # Nega mount di filesystem
  deny mount,
  deny umount,
  deny pivot_root,

  # Nega accesso a ptrace
  deny ptrace (read, trace, attach),

  # Nega caricamento di moduli kernel
  deny capability sys_module,
  deny capability sys_rawio,
  deny capability sys_admin,

  # Nega accesso ai dispositivi
  deny /dev/** rw,
  /dev/null rw,
  /dev/zero r,
  /dev/urandom r,
  /dev/random r,
  /dev/tty rw,
}

Per caricare e applicare il profilo:

# Caricare il profilo AppArmor
sudo apparmor_parser -r -W /etc/apparmor.d/containers/my-secure-container

# Verificare che il profilo sia caricato
sudo aa-status | grep my-secure-container

# Eseguire il container con il profilo personalizzato
docker run --security-opt apparmor=my-secure-container my-image

# In Kubernetes (se il nodo supporta AppArmor)
apiVersion: v1
kind: Pod
metadata:
  name: my-secure-pod
  annotations:
    container.apparmor.security.beta.kubernetes.io/my-container: localhost/my-secure-container
spec:
  containers:
  - name: my-container
    image: my-image

Un dettaglio cruciale che molti trascurano: il profilo blocca l'accesso all'endpoint dei metadata cloud (169.254.169.254). Questo è esattamente il vettore che VoidLink utilizza per rilevare il cloud provider e adattare il suo comportamento. Bloccandolo, eliminate un tassello fondamentale della fase di ricognizione del malware.

Hardening delle Capability Linux

Le capability Linux suddividono i tradizionali privilegi root in unità più granulari. Per i container, gestirle correttamente è essenziale per prevenire gli escape. E la buona notizia è che il principio è semplice: togli tutto, aggiungi solo il minimo indispensabile.

Configurazione di Sicurezza Minimale

# Docker: drop di TUTTE le capability, aggiunta solo di quelle necessarie
docker run \
  --cap-drop ALL \
  --cap-add NET_BIND_SERVICE \
  --cap-add CHOWN \
  --cap-add SETUID \
  --cap-add SETGID \
  --read-only \
  --tmpfs /tmp:rw,noexec,nosuid \
  --security-opt no-new-privileges:true \
  my-image

# Kubernetes: SecurityContext completo
apiVersion: v1
kind: Pod
metadata:
  name: hardened-pod
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    runAsGroup: 1000
    fsGroup: 1000
    seccompProfile:
      type: RuntimeDefault
  containers:
  - name: app
    image: my-image
    securityContext:
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      capabilities:
        drop:
          - ALL
        add:
          - NET_BIND_SERVICE
      privileged: false
    resources:
      limits:
        cpu: "500m"
        memory: "256Mi"
      requests:
        cpu: "100m"
        memory: "128Mi"
    volumeMounts:
    - name: tmp
      mountPath: /tmp
    - name: app-data
      mountPath: /app/data
  volumes:
  - name: tmp
    emptyDir:
      medium: Memory
      sizeLimit: "64Mi"
  - name: app-data
    emptyDir: {}

La flag no-new-privileges merita un'attenzione speciale: impedisce ai processi dentro il container di acquisire nuovi privilegi tramite binari setuid o setgid. È una di quelle opzioni che costa zero in termini di funzionalità ma blocca una delle tecniche di escalation più comuni.

Runtime Sandboxed: gVisor e Kata Containers

Tutte le misure di hardening che abbiamo visto finora — seccomp, AppArmor, capability — operano sullo stesso kernel dell'host. Se l'attaccante trova una vulnerabilità nel kernel, tutte queste protezioni possono essere aggirate simultaneamente. È un po' come mettere tre serrature sulla stessa porta.

Per questo, nel 2026 le organizzazioni che prendono sul serio la sicurezza stanno adottando runtime sandboxed che aggiungono un vero strato di isolamento aggiuntivo.

gVisor: Un Kernel in Userspace

gVisor implementa un kernel Linux in userspace chiamato Sentry che intercetta tutte le system call del container. Invece di passarle direttamente al kernel dell'host, Sentry le processa internamente, utilizzando solo circa 20 system call verso il kernel host (rispetto alle oltre 300 normalmente disponibili). La riduzione della superficie d'attacco è enorme.

# Installazione di gVisor (runsc)
# Scaricare e installare il runtime
curl -fsSL https://gvisor.dev/archive.key | sudo gpg --dearmor -o /usr/share/keyrings/gvisor-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/gvisor-archive-keyring.gpg] https://storage.googleapis.com/gvisor/releases release main" | sudo tee /etc/apt/sources.list.d/gvisor.list
sudo apt-get update && sudo apt-get install -y runsc

# Configurare Docker per utilizzare gVisor
cat <<EOF | sudo tee /etc/docker/daemon.json
{
  "runtimes": {
    "runsc": {
      "path": "/usr/bin/runsc",
      "runtimeArgs": [
        "--platform=systrap",
        "--network=sandbox"
      ]
    }
  }
}
EOF

sudo systemctl restart docker

# Eseguire un container con gVisor
docker run --runtime=runsc -it ubuntu bash

# Verificare che gVisor è attivo
docker run --runtime=runsc alpine dmesg | head -5
# Output: "Starting gVisor..."

Vantaggi di gVisor:

  • Riduce drasticamente la superficie d'attacco del kernel (da 300+ syscall a ~20)
  • Non richiede hardware di virtualizzazione
  • Avvio rapido, paragonabile a un container standard
  • Protegge anche contro vulnerabilità kernel zero-day

Limitazioni da tenere a mente:

  • Non supporta il 100% delle system call Linux (circa il 70% sono supportate)
  • Overhead di prestazioni del 5-15% per workload I/O-intensive
  • Alcune applicazioni che usano syscall esotiche potrebbero non funzionare correttamente

Kata Containers: MicroVM per Ogni Container

Kata Containers porta l'isolamento a un livello ancora superiore: ogni container viene eseguito all'interno della propria macchina virtuale leggera con un kernel dedicato. Sì, avete letto bene — un kernel separato per ogni container.

# Installazione di Kata Containers
# (esempio per Ubuntu 22.04+)
sudo snap install kata-containers --classic

# Configurare come runtime Docker
cat <<EOF | sudo tee /etc/docker/daemon.json
{
  "runtimes": {
    "kata-runtime": {
      "path": "/snap/bin/kata-containers.runtime"
    }
  }
}
EOF

sudo systemctl restart docker

# Eseguire un container con Kata
docker run --runtime=kata-runtime -it ubuntu bash

# Verificare l'isolamento — il container ha il proprio kernel
docker run --runtime=kata-runtime alpine uname -r
# Output: kernel della microVM, diverso dall'host

# In Kubernetes con RuntimeClass
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  name: kata
handler: kata-runtime
---
apiVersion: v1
kind: Pod
metadata:
  name: kata-isolated-pod
spec:
  runtimeClassName: kata
  containers:
  - name: app
    image: my-sensitive-app

Vantaggi di Kata Containers:

  • Isolamento hardware-enforced tramite virtualizzazione
  • Anche se l'attaccante sfugge dal container, resta intrappolato nella microVM
  • Kernel separato per ogni container — una vulnerabilità kernel nel container non compromette l'host
  • Perfetto per workload multi-tenant e ambienti ad alta sicurezza

Limitazioni:

  • Richiede hardware con supporto alla virtualizzazione (VT-x/AMD-V)
  • Overhead di memoria più elevato (~5 MiB per microVM con Firecracker)
  • Tempo di avvio leggermente superiore (~125ms con Firecracker, comunque molto veloce)

Quando Usare Quale Runtime

Per semplificare la scelta, ecco una matrice di confronto rapido:

# Matrice decisionale per la scelta del runtime

┌─────────────────────────────┬──────────┬──────────┬──────────────┐
│ Criterio                    │  runc    │  gVisor  │  Kata/       │
│                             │(default) │ (runsc)  │  Firecracker │
├─────────────────────────────┼──────────┼──────────┼──────────────┤
│ Isolamento                  │  Basso   │  Alto    │  Molto Alto  │
│ Overhead prestazioni        │  Minimo  │  5-15%   │  2-5%        │
│ Overhead memoria            │  Minimo  │  Basso   │  ~5 MiB/VM   │
│ Tempo avvio                 │  <100ms  │  <200ms  │  ~125ms      │
│ Compatibilità syscall       │  100%    │  ~70%    │  100%        │
│ Protezione kernel zero-day  │  No      │  Sì      │  Sì          │
│ Requisiti hardware          │  Nessuno │  Nessuno │  VT-x/AMD-V  │
├─────────────────────────────┼──────────┼──────────┼──────────────┤
│ Caso d'uso ideale           │ Dev/Test │ Prod     │ Multi-tenant │
│                             │          │ generale │ Alta sicur.  │
└─────────────────────────────┴──────────┴──────────┴──────────────┘

Isolamento di Rete e Protezione degli Endpoint Metadata

Un aspetto che viene sottovalutato troppo spesso nella sicurezza dei container è l'isolamento di rete. Per default, i container in un cluster Kubernetes possono comunicare liberamente tra loro e, potenzialmente, accedere a servizi esterni sensibili come gli endpoint metadata dei cloud provider. È praticamente un invito a nozze per il movimento laterale.

Network Policy Kubernetes

Le Network Policy di Kubernetes permettono di definire regole di firewall a livello di pod. Senza di esse, il cluster opera con una politica "allow-all" — esattamente ciò che un attaccante desidera.

# Politica di default: negare tutto il traffico ingress ed egress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

---
# Permettere solo il traffico necessario per l'applicazione
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-app-traffic
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: web-frontend
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          name: ingress-nginx
    ports:
    - protocol: TCP
      port: 8080
  egress:
  - to:
    - podSelector:
        matchLabels:
          app: api-backend
    ports:
    - protocol: TCP
      port: 3000
  - to:  # Permettere DNS
    - namespaceSelector: {}
      podSelector:
        matchLabels:
          k8s-app: kube-dns
    ports:
    - protocol: UDP
      port: 53

---
# CRITICO: Bloccare accesso ai metadata cloud
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: block-cloud-metadata
  namespace: production
spec:
  podSelector: {}
  policyTypes:
  - Egress
  egress:
  - to:
    - ipBlock:
        cidr: 0.0.0.0/0
        except:
        - 169.254.169.254/32  # Endpoint metadata AWS/Azure/GCP

Questa configurazione applica il principio del minimo privilegio alla rete. Ogni pod può comunicare solo con i servizi strettamente necessari, e l'accesso all'endpoint metadata cloud è bloccato — una difesa cruciale contro la fase di ricognizione di malware come VoidLink.

Egress Filtering per Contrastare le Comunicazioni C2

VoidLink supporta molteplici protocolli per le comunicazioni C2: HTTP/1.1, HTTP/2, WebSocket, DNS e ICMP. Per contrastare efficacemente queste comunicazioni, serve un filtraggio dell'egress davvero rigoroso:

# Esempio con Cilium Network Policy per egress filtering avanzato
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: strict-egress
  namespace: production
spec:
  endpointSelector:
    matchLabels:
      app: my-app
  egress:
  # Permettere solo HTTPS verso domini specifici
  - toFQDNs:
    - matchName: "api.example.com"
    - matchName: "registry.example.com"
    toPorts:
    - ports:
      - port: "443"
        protocol: TCP
  # Permettere DNS solo verso il DNS interno del cluster
  - toEndpoints:
    - matchLabels:
        k8s-app: kube-dns
    toPorts:
    - ports:
      - port: "53"
        protocol: UDP
  # Bloccare ICMP (usato da VoidLink per C2)
  # Tutto il resto è negato implicitamente

L'uso di CiliumNetworkPolicy con toFQDNs è particolarmente efficace: permette di specificare i domini di destinazione consentiti anziché semplici IP. Questo impedisce agli attaccanti di usare domini dinamici per le comunicazioni C2 — una tecnica che framework come VoidLink impiegano regolarmente.

Monitoraggio Runtime con eBPF: Falco e Tetragon

L'hardening preventivo è fondamentale, ma da solo non basta. Servono strumenti di monitoraggio runtime capaci di rilevare e rispondere ad attività sospette in tempo reale. Nel 2026, la tecnologia eBPF è diventata lo standard de facto per questo tipo di monitoraggio — e per buone ragioni.

Falco: Rilevamento delle Anomalie

Falco, progetto CNCF ormai maturo, utilizza eBPF per monitorare le system call e rilevare comportamenti anomali in tempo reale. Ecco come installarlo e configurare regole specifiche per le minacce che abbiamo analizzato:

# Installazione di Falco
curl -fsSL https://falco.org/repo/falcosecurity-packages.asc | \
  sudo gpg --dearmor -o /usr/share/keyrings/falco-archive-keyring.gpg

echo "deb [signed-by=/usr/share/keyrings/falco-archive-keyring.gpg] https://download.falco.org/packages/deb stable main" | \
  sudo tee /etc/apt/sources.list.d/falcosecurity.list

sudo apt-get update
sudo apt-get install -y falco

# Esempio di regole personalizzate per rilevare VoidLink
# /etc/falco/rules.d/voidlink-detection.yaml

- rule: Accesso al Metadata Cloud da Container
  desc: Rileva tentativi di accesso all'endpoint metadata del cloud provider
  condition: >
    evt.type in (connect, sendto) and
    container.id != host and
    fd.sip = "169.254.169.254"
  output: >
    Tentativo di accesso al metadata cloud da container
    (user=%user.name command=%proc.cmdline
    container=%container.name image=%container.image.repository
    connection=%fd.name)
  priority: WARNING
  tags: [network, container, cloud, mitre_discovery]

- rule: Caricamento Modulo Kernel da Container
  desc: Rileva tentativi di caricare moduli kernel dall'interno di un container
  condition: >
    evt.type in (init_module, finit_module) and
    container.id != host
  output: >
    Tentativo di caricamento modulo kernel da container
    (user=%user.name command=%proc.cmdline
    container=%container.name image=%container.image.repository)
  priority: CRITICAL
  tags: [process, container, mitre_persistence]

- rule: Uso di eBPF da Container Non Autorizzato
  desc: Rileva l'uso della syscall bpf da container non autorizzati
  condition: >
    evt.type = bpf and
    container.id != host and
    not container.image.repository in (falco, tetragon, cilium)
  output: >
    Chiamata bpf da container non autorizzato
    (user=%user.name command=%proc.cmdline
    container=%container.name image=%container.image.repository)
  priority: CRITICAL
  tags: [process, container, mitre_defense_evasion]

- rule: Rilevamento LD_PRELOAD Sospetto
  desc: Rileva l'impostazione di LD_PRELOAD che potrebbe indicare hooking
  condition: >
    evt.type = execve and
    container.id != host and
    evt.arg.environment contains "LD_PRELOAD"
  output: >
    Variabile LD_PRELOAD rilevata in esecuzione container
    (user=%user.name command=%proc.cmdline
    container=%container.name
    env=%evt.arg.environment)
  priority: WARNING
  tags: [process, container, mitre_defense_evasion]

Tetragon: Enforcement in Tempo Reale

Falco è eccellente per il rilevamento, ma Tetragon (di Cilium/Isovalent) fa un passo in più: aggiunge la capacità di enforcement, bloccando le azioni sospette direttamente nel kernel senza latenza percepibile. Se Falco è l'allarme, Tetragon è la porta blindata che si chiude automaticamente.

# Installazione di Tetragon in Kubernetes via Helm
helm repo add cilium https://helm.cilium.io
helm repo update
helm install tetragon cilium/tetragon -n kube-system

# TracingPolicy per bloccare container escape
apiVersion: cilium.io/v1alpha1
kind: TracingPolicy
metadata:
  name: block-container-escape
spec:
  kprobes:
  # Blocca tentativi di mount da container
  - call: "__x64_sys_mount"
    syscall: true
    selectors:
    - matchNamespaces:
      - namespace: Mnt
        operator: NotIn
        values:
        - "host_mnt_ns"
      matchActions:
      - action: Sigkill
        argError: -1

  # Blocca caricamento moduli kernel da container
  - call: "__x64_sys_init_module"
    syscall: true
    selectors:
    - matchNamespaces:
      - namespace: Pid
        operator: NotIn
        values:
        - "host_pid_ns"
      matchActions:
      - action: Sigkill

  # Blocca uso di io_uring da container
  - call: "__x64_sys_io_uring_setup"
    syscall: true
    selectors:
    - matchNamespaces:
      - namespace: Pid
        operator: NotIn
        values:
        - "host_pid_ns"
      matchActions:
      - action: Sigkill

  # Monitora uso di bpf da container
  - call: "__x64_sys_bpf"
    syscall: true
    selectors:
    - matchNamespaces:
      - namespace: Pid
        operator: NotIn
        values:
        - "host_pid_ns"
      matchActions:
      - action: Post
        rateLimit: "1m"

La combinazione Falco + Tetragon è, a mio parere, la scelta migliore al momento: Falco per la rilevazione con regole flessibili e alerting granulare, Tetragon per l'enforcement in tempo reale con overhead sotto l'1%. Insieme coprono sia la detection che la response.

Difesa in Profondità: Checklist Operativa Completa

Bene, abbiamo coperto parecchio terreno. Ecco una checklist che riassume tutte le misure di hardening trattate. Usatela come riferimento pratico per valutare e migliorare la sicurezza dei vostri ambienti containerizzati.

1. Configurazione Base del Container

  • Mai utilizzare container privilegiati (--privileged) in produzione
  • Drop di tutte le capability con --cap-drop ALL, aggiungere solo quelle necessarie
  • Eseguire come utente non-root (runAsNonRoot: true)
  • Filesystem root in sola lettura (readOnlyRootFilesystem: true)
  • Bloccare l'escalation dei privilegi (allowPrivilegeEscalation: false)
  • Mai montare il socket Docker all'interno dei container
  • Impostare limiti di risorse (CPU, memoria) per prevenire DoS

2. Profili di Sicurezza

  • Applicare profili seccomp personalizzati per ogni workload
  • Configurare profili AppArmor o SELinux dedicati
  • Bloccare esplicitamente io_uring, bpf, ptrace, mount nei profili seccomp
  • In Kubernetes, abilitare seccomp esplicitamente (non è attivo di default!)

3. Isolamento di Rete

  • Utilizzare Network Policy in Kubernetes per segmentare il traffico tra pod
  • Bloccare l'accesso agli endpoint metadata cloud (169.254.169.254) da container non autorizzati
  • Non utilizzare --network=host salvo necessità assoluta
  • Implementare service mesh (Istio, Cilium) per mTLS tra servizi

4. Immagini e Supply Chain

  • Utilizzare immagini base minimali (distroless, scratch, Alpine)
  • Scansione delle vulnerabilità con Trivy, Grype o Snyk
  • Firmare le immagini con Cosign/Sigstore
  • Implementare policy di ammissione (OPA Gatekeeper, Kyverno) per impedire il deploy di immagini non verificate

5. Runtime e Monitoraggio

  • Valutare gVisor o Kata Containers per workload ad alto rischio
  • Deployare Falco per il rilevamento delle anomalie runtime
  • Deployare Tetragon per l'enforcement in tempo reale
  • Configurare alerting per tentativi di container escape
  • Monitorare l'uso di LD_PRELOAD, BPF e io_uring all'interno dei container

6. Difese Specifiche Anti-VoidLink

  • Bloccare l'accesso ai metadata endpoint cloud tramite AppArmor e Network Policy
  • Monitorare la creazione di programmi eBPF con Falco/Tetragon
  • Proibire il caricamento di moduli kernel da container
  • Rilevare e bloccare tentativi di LD_PRELOAD injection
  • Implementare egress filtering rigoroso per limitare le comunicazioni C2
  • Monitorare pattern di beacon anomali nel traffico di rete in uscita

Conclusioni: La Sicurezza dei Container È un Processo, Non un Progetto

La scoperta di VoidLink a dicembre 2025 ha reso evidente una realtà scomoda: gli attaccanti si stanno specializzando negli ambienti cloud-native con la stessa velocità con cui le organizzazioni li adottano. Il malware moderno non si limita a sfruttare vulnerabilità singole — è progettato per operare nativamente nell'ecosistema container, con capacità di rilevamento ambientale, evasione adattiva e modularità che ricordano i framework offensivi professionali.

La buona notizia? Abbiamo gli strumenti per difenderci, e sono maturi.

La combinazione di seccomp personalizzato, AppArmor, capability minimali, runtime sandboxed e monitoraggio eBPF crea una difesa in profondità che rende la vita estremamente difficile a qualsiasi attaccante — anche uno sofisticato come VoidLink.

Il punto chiave è non affidarsi mai a un singolo strato di protezione. Come abbiamo visto nel nostro articolo precedente sulle vulnerabilità di io_uring, gli attaccanti trovano costantemente modi per aggirare le difese individuali. La vera sicurezza sta nella sovrapposizione di livelli indipendenti, dove la compromissione di uno non invalida gli altri.

Il mio consiglio? Iniziate oggi. Revisionate i vostri deployment con la checklist che abbiamo visto, implementate subito le misure più critiche (seccomp, capability minimali, utente non-root) e pianificate l'adozione di runtime sandboxed e monitoraggio eBPF per i workload più sensibili. La sicurezza dei container non è un progetto con una data di fine — è un processo continuo, e il momento migliore per iniziare è adesso.

Sull'Autore Editorial Team

Our team of expert writers and editors.