
Mitigação do Docker para CVE-2026-31431 ('Copy Fail'). Inclui também modelos Kubernetes.
Script idempotente para bloquear a criação de sockets AF_ALG para todos os contêineres Docker
em um host como mitigação para CVE-2026-31431 ("Copy Fail").
A CVE-2026-31431 é uma escalada de privilégio local no template criptográfico
authencesn do kernel Linux, presente em kernels compilados entre 2017 e
a disponibilidade da correção upstream (commit mainline a664bf3d603d). Um
usuário sem privilégios pode encadear uma operação de socket AF_ALG com splice() para
realizar uma escrita controlada de 4 bytes no cache de páginas de qualquer arquivo legível,
visando um binário setuid para obter um shell root. Uma prova de conceito em Python de 732 bytes
explora isso de forma confiável, sem corridas ou offsets por distribuição, em todas as
principais distribuições Linux que possuem um kernel afetado.
O primeiro passo obrigatório do exploit é abrir um socket AF_ALG
(socket(AF_ALG, SOCK_SEQPACKET, 0)). Bloquear essa syscall via seccomp
impede a exploração mesmo em kernels sem correção. O perfil seccomp padrão integrado do Docker
não bloqueia AF_ALG, e RuntimeDefault não é
suficiente — clusters testados mostraram que pods admitidos sob PSS Restricted
ainda podiam abrir sockets AF_ALG.
Consulte o advisory do pesquisador original em https://copy.fail e o advisory da CERT-EU em https://cert.europa.eu/publications/security-advisories/2026-005/ para detalhes técnicos completos e disponibilidade de correção por distribuição.
Este script cobre a configuração global do daemon do Docker Engine. Para
Kubernetes, consulte a seção Kubernetes abaixo. Para cargas de trabalho
bare-metal ou VM (não containerizadas), desative o módulo do kernel algif_aead
em vez disso:
echo "install algif_aead /bin/false" > /etc/modprobe.d/disable-algif.conf
rmmod algif_aead 2>/dev/null || true
Essa abordagem não tem impacto em dm-crypt/LUKS, kTLS, IPsec/XFRM, OpenSSL, GnuTLS, NSS ou SSH.
Extrai o perfil seccomp padrão integrado ativo do Docker inspecionando o
HostConfig.SecurityOpt de um contêiner de curta duração. Isso evita qualquer
dependência de uma URL remota e garante que o perfil base corresponda à
versão do Docker realmente instalada. Uma busca no GitHub em
moby/profiles é usada como fallback
apenas se a inspeção do contêiner não retornar nada.
Corrige o perfil removendo socket da entrada de allowlist do Docker
e o adicionando novamente com um filtro de argumento que permite todas as famílias de endereços
exceto AF_ALG (valor 38) usando SCMP_CMP_NE. Todo o resto do comportamento
seccomp padrão do Docker é preservado.
Grava o perfil corrigido atomicamente em
/etc/seccomp/docker-block-af-alg.json (arquivo temporário + rename). Ignorado se
o conteúdo no disco já for idêntico.
Atualiza /etc/docker/daemon.json para definir "seccomp-profile" como o
caminho do perfil corrigido. O arquivo original é copiado para daemon.json.bak
na primeira modificação. Ignorado se já estiver configurado corretamente.
Recarrega o dockerd via systemctl reload docker (SIGHUP — sem necessidade de reinicialização).
Ignorado se nenhum dos arquivos foi alterado.
Verifica se o bloqueio está ativo executando uma sonda dentro de um contêiner, independentemente de quaisquer alterações feitas nas etapas acima.
O script é idempotente: executá-lo várias vezes produz o mesmo resultado e só recarrega o Docker quando algo realmente mudou.
Nota: Contêineres
--privilegedignoram todos os perfis seccomp, independentemente desta configuração. Audite seus arquivos Compose e comandos de execução para contêineres privilegiados separadamente.
docker no PATHcurl no PATH (apenas fallback)systemctl (host systemd)/etc/seccomp e /etc/docker, e para
systemctl reload docker# Aplicar a mitigação e verificar (uso normal)
sudo python3 harden-docker-seccomp.py
# Mostrar o que mudaria sem gravar nada ou recarregar o Docker
sudo python3 harden-docker-seccomp.py --dry-run
# Re-executar apenas a verificação do contêiner (sem alterações de configuração)
python3 harden-docker-seccomp.py --verify-only
# Saída detalhada
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.
| Código | Significado |
|---|---|
0 | Sucesso — a mitigação está ativa |
1 | Script não executado como root (ao corrigir), ou erro irrecuperável |
2 | Falha na verificação — AF_ALG não está bloqueado |
Os pods do Kubernetes compartilham o kernel do host, portanto, o mesmo primitivo de socket AF_ALG
é acessível a partir de qualquer pod em um nó afetado. O seccomp RuntimeDefault não é
suficiente — clusters testados mostraram que pods admitidos sob PSS Restricted
ainda podiam abrir sockets AF_ALG. Um perfil Localhost com uma regra de negação explícita
é necessário.
A mitigação requer duas coisas: o JSON do perfil presente no sistema de arquivos de cada nó, e cada spec de pod referenciando-o. As seções abaixo cobrem ambos, incluindo como injetar o perfil globalmente sem modificar specs de pods individuais.
O kubelet resolve perfis seccomp Localhost em relação à sua raiz seccomp,
que por padrão é /var/lib/kubelet/seccomp. O perfil deve existir nesse
caminho em cada nó que possa agendar uma carga de trabalho.
Aplique o ConfigMap e o DaemonSet deste repositório:
kubectl apply -f templates/configmap-seccomp-profile.yaml
kubectl apply -f templates/daemonset-distribute-profile.yaml
O DaemonSet executa um contêiner init que copia o perfil do ConfigMap para a raiz seccomp do kubelet do nó e, em seguida, estaciona um contêiner pause mínimo para que o pod permaneça visível para monitoramento de saúde. Ele tolera todos os taints para que também seja executado em nós do plano de controle.
Raiz seccomp do kubelet não padrão: RKE2 usa
/var/lib/rancher/rke2/agent/kubelet/seccomp. Substitua o caminho definindoNODE_SECCOMP_ROOTno env do contêiner init do DaemonSet antes de aplicar.
Verifique se o arquivo está presente em um nó:
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
Em vez de modificar specs de pods individuais ou Helm charts, use um
webhook de admissão mutante para injetar seccompProfile automaticamente no momento da admissão.
Duas opções são fornecidas: Kyverno e OPA Gatekeeper.
Ambas as abordagens só injetam o perfil quando um pod ainda não declara um, portanto, pods com perfis explícitos são deixados intactos.