
Mitigazione Docker per CVE-2026-31431 ('Copy Fail'). Include anche template Kubernetes.
Script idempotente per bloccare la creazione di socket AF_ALG per tutti i container Docker
su un host come mitigazione per CVE-2026-31431 ("Copy Fail").
CVE-2026-31431 è un'escalation di privilegi locale nel template crittografico
authencesn del kernel Linux, presente nei kernel compilati tra il 2017 e
la disponibilità della correzione upstream (commit mainline a664bf3d603d). Un
utente non privilegiato può concatenare un'operazione su socket AF_ALG con splice() per
eseguire una scrittura controllata di 4 byte nella cache delle pagine di qualsiasi file leggibile,
prendendo di mira un binario setuid per ottenere una shell di root. Una proof of concept
Python di 732 byte sfrutta questa vulnerabilità in modo affidabile, senza race condition o offset
per distribuzione, su ogni principale distribuzione Linux che include un kernel vulnerabile.
Il primo passo obbligatorio dell'exploit è l'apertura di un socket AF_ALG
(socket(AF_ALG, SOCK_SEQPACKET, 0)). Bloccare quella syscall tramite seccomp
previene lo sfruttamento anche su kernel non patchati. Il profilo seccomp predefinito
integrato di Docker non blocca AF_ALG, e RuntimeDefault non è
sufficiente — i cluster testati hanno mostrato che i pod ammessi sotto PSS Restricted
potevano comunque aprire socket AF_ALG.
Vedi l'advisory del ricercatore originale su https://copy.fail e l'advisory CERT-EU su https://cert.europa.eu/publications/security-advisories/2026-005/ per i dettagli tecnici completi e la disponibilità delle patch per distribuzione.
Questo script copre la configurazione globale del daemon Docker Engine. Per
Kubernetes, vedi la sezione Kubernetes qui sotto. Per carichi di lavoro
bare-metal o VM (non containerizzati), disabilita invece il modulo del kernel algif_aead:
echo "install algif_aead /bin/false" > /etc/modprobe.d/disable-algif.conf
rmmod algif_aead 2>/dev/null || true
Questo approccio non ha alcun impatto su dm-crypt/LUKS, kTLS, IPsec/XFRM, OpenSSL, GnuTLS, NSS o SSH.
Estrae il profilo seccomp predefinito attivo di Docker ispezionando il
HostConfig.SecurityOpt di un container di breve durata. Questo evita qualsiasi
dipendenza da un URL remoto e garantisce che il profilo di base corrisponda alla
versione di Docker effettivamente installata. Un fetch da GitHub da
moby/profiles viene usato come fallback
solo se l'ispezione del container non produce risultati.
Applica una patch al profilo rimuovendo socket dalla voce allowlist di Docker
e riaggiungendolo con un filtro sugli argomenti che consente tutte le famiglie di
indirizzi tranne AF_ALG (valore 38) usando SCMP_CMP_NE. Tutto il resto del
comportamento seccomp predefinito di Docker viene preservato.
Scrive il profilo patchato atomicamente in
/etc/seccomp/docker-block-af-alg.json (file temporaneo + rinomina). Viene saltato se
il contenuto su disco è già identico.
Aggiorna /etc/docker/daemon.json per impostare "seccomp-profile" sul
percorso del profilo patchato. Il file originale viene salvato come backup in daemon.json.bak
alla prima modifica. Viene saltato se già configurato correttamente.
Ricarica dockerd tramite systemctl reload docker (SIGHUP — nessun riavvio
richiesto). Viene saltato se nessuno dei due file è cambiato.
Verifica che il blocco sia attivo eseguendo una sonda all'interno di un container, indipendentemente dal fatto che siano state apportate modifiche nei passaggi precedenti.
Lo script è idempotente: eseguirlo più volte produce lo stesso risultato e ricarica Docker solo quando qualcosa è effettivamente cambiato.
Nota: i container
--privilegedbypassano tutti i profili seccomp indipendentemente da questa configurazione. Controlla separatamente i tuoi file Compose e i comandi di esecuzione per i container privilegiati.
docker nel PATHcurl nel PATH (solo fallback)systemctl (host systemd)/etc/seccomp e /etc/docker, e per
systemctl reload docker# Applica la mitigazione e verifica (utilizzo normale)
sudo python3 harden-docker-seccomp.py
# Mostra cosa cambierebbe senza scrivere nulla o ricaricare Docker
sudo python3 harden-docker-seccomp.py --dry-run
# Esegui solo la verifica del container (nessuna modifica alla configurazione)
python3 harden-docker-seccomp.py --verify-only
# Output verboso
sudo python3 harden-docker-seccomp.py --verbose
INFO Written: /etc/seccomp/docker-block-af-alg.json
INFO Updated: /etc/docker/daemon.json
INFO Reloading Docker daemon (SIGHUP)…
INFO Docker daemon reloaded.
INFO Verifying AF_ALG is blocked inside a container…
OK: AF_ALG blocked — [Errno 1] Operation not permitted
INFO All done. CVE-2026-31431 (Copy Fail) mitigation is active.
INFO Profile unchanged: /etc/seccomp/docker-block-af-alg.json
INFO daemon.json already configured correctly.
INFO No changes — Docker daemon reload not required.
INFO Verifying AF_ALG is blocked inside a container…
OK: AF_ALG blocked — [Errno 1] Operation not permitted
INFO All done. CVE-2026-31431 (Copy Fail) mitigation is active.
| Codice | Significato |
|---|---|
0 | Successo — la mitigazione è attiva |
1 | Script non eseguito come root (durante la patch), o errore irreversibile |
2 | Verifica fallita — AF_ALG non è bloccato |
I pod Kubernetes condividono il kernel dell'host, quindi la stessa primitiva del socket AF_ALG
è raggiungibile da qualsiasi pod su un nodo vulnerabile. Il seccomp RuntimeDefault non è
sufficiente — i cluster testati hanno mostrato che i pod ammessi sotto PSS Restricted
potevano comunque aprire socket AF_ALG. È richiesto un profilo Localhost con una regola di
deny esplicita.
La mitigazione richiede due cose: il JSON del profilo presente sul filesystem di ogni nodo, e ogni spec di pod che lo referenzi. Le sezioni seguenti coprono entrambe, incluso come iniettare il profilo globalmente senza modificare gli spec dei singoli pod.
Il kubelet risolve i profili seccomp Localhost relativamente alla sua root seccomp,
che per impostazione predefinita è /var/lib/kubelet/seccomp. Il profilo deve esistere a
quel percorso su ogni nodo che può pianificare un carico di lavoro.
Applica il ConfigMap e il DaemonSet da questo repository:
kubectl apply -f templates/configmap-seccomp-profile.yaml
kubectl apply -f templates/daemonset-distribute-profile.yaml
Il DaemonSet esegue un container init che copia il profilo dal ConfigMap alla root seccomp del kubelet del nodo, poi parcheggia un container pause minimale così il pod rimane visibile per il monitoraggio della salute. Tollera tutti i taint così viene eseguito anche sui nodi control-plane.
Root seccomp kubelet non standard: RKE2 usa
/var/lib/rancher/rke2/agent/kubelet/seccomp. Sovrascrivi il percorso impostandoNODE_SECCOMP_ROOTnell'env del container init del DaemonSet prima di applicare.
Verifica che il file sia presente su un nodo:
kubectl -n kube-system exec -it \
$(kubectl -n kube-system get pod -l app.kubernetes.io/name=distribute-af-alg-seccomp \
-o jsonpath='{.items[0].metadata.name}') -- \
cat /var/lib/kubelet/seccomp/block-af-alg.json