
Kubernetes DaemonSet to detect and remediate CVE-2026-31431 (GHSA-2274-3hgr-wxv6) — algif_aead LPE via modprobe blacklist
Severity: High (CVSS 7.8 — CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H)
CVE-2026-31431 is a local privilege escalation vulnerability in the Linux kernel's algif_aead module (AF_ALG AEAD crypto socket interface). A low-privileged local user can exploit incorrect in-place buffer operations to gain full root access. All AKS node pools running Linux kernels prior to the upstream patch are potentially affected.
The algif_aead module is not required for normal Kubernetes or AKS workloads. Blacklisting it eliminates the attack surface entirely.
One pod runs on every Linux node. On start-up and then every 60 seconds each pod:
Writes /etc/modprobe.d/modprobe-CIS.conf on the host with:
install algif_aead /bin/false
blacklist algif_aead
This prevents the module from loading on reboot or via modprobe.
Checks /proc/modules (the live host kernel module list — shared with all containers) for algif_aead.
Labels the Kubernetes node with the result so you can query across the cluster:
| Label value | Meaning |
|---|---|
mitigated | Module not loaded; blacklist config applied |
module-loaded | Module is currently loaded — investigate immediately |
| File | Purpose |
|---|---|
rbac.yaml | Namespace, ServiceAccount, ClusterRole (get/patch nodes), ClusterRoleBinding |
configmap.yaml | Shell remediation script mounted into every pod |
daemonset.yaml | DaemonSet definition — one pod per Linux node |
kubectl configured against the target AKS clusterApply the manifests in order:
kubectl apply -f rbac.yaml
kubectl apply -f configmap.yaml
kubectl apply -f daemonset.yaml
Wait for the rollout to complete on all nodes:
kubectl -n security-remediation rollout status daemonset/cve-2026-31431-remediator
kubectl get nodes -L vulnerability.aks.io/CVE-2026-31431
Expected output on a clean cluster:
NAME STATUS ROLES AGE VERSION CVE-2026-31431
aks-nodepool1-xxxxx-vmss000000 Ready <none> 4d v1.33.7 mitigated
aks-nodepool1-xxxxx-vmss000001 Ready <none> 4d v1.33.7 mitigated
# List pods and their assigned nodes
kubectl -n security-remediation get pods -o wide
# Tail a specific pod's logs
kubectl -n security-remediation logs <pod-name> -f
Expected log output:
2026-05-01T01:33:36Z [INFO ] CVE-2026-31431 remediator starting on node: aks-nodepool1-...
2026-05-01T01:33:36Z [INFO ] Blacklist config written → /host/etc/modprobe.d/modprobe-CIS.conf
2026-05-01T01:33:36Z [INFO ] Node aks-nodepool1-...: 'algif_aead' is NOT loaded — blacklist config applied, node is mitigated.
2026-05-01T01:33:36Z [INFO ] Labeled node aks-nodepool1-...: vulnerability.aks.io/CVE-2026-31431=mitigated
2026-05-01T01:33:36Z [INFO ] Entering monitoring loop (interval: 60s)
kubectl -n security-remediation exec <pod-name> -- cat /host/etc/modprobe.d/modprobe-CIS.conf
Expected content:
# CIS hardening – mitigate CVE-2026-31431 (GHSA-2274-3hgr-wxv6)
# Prevents algif_aead (AF_ALG AEAD) LPE from being loaded or exploited.
install algif_aead /bin/false
blacklist algif_aead
If a node shows module-loaded, the kernel module is currently active and the node may already be compromised. Cordon and drain the node immediately for forensic analysis:
# Find affected nodes
kubectl get nodes -l vulnerability.aks.io/CVE-2026-31431=module-loaded
# Cordon and drain for investigation
kubectl cordon <node-name>
kubectl drain <node-name> --ignore-daemonsets --delete-emptydir-data
Note: Removing the DaemonSet does not remove the
/etc/modprobe.d/modprobe-CIS.conffile from host nodes. That file is intentionally left in place as a persistent kernel hardening measure.
kubectl delete -f daemonset.yaml
kubectl delete -f configmap.yaml
kubectl delete -f rbac.yaml
This deletes the DaemonSet, all its pods, the ConfigMap, the ServiceAccount, the ClusterRole, the ClusterRoleBinding, and the namespace.
kubectl label nodes --all vulnerability.aks.io/CVE-2026-31431-
Only do this if you have an alternative mitigation (patched kernel). Run on each node or via a privileged pod:
rm -f /etc/modprobe.d/modprobe-CIS.conf
runAsUser: 0) — required to write to /etc/modprobe.d/ on the host.capabilities.drop: ["ALL"]). No kernel capabilities are needed.readOnlyRootFilesystem: true. Only the mounted hostPath and emptyDir volumes are writable./etc/modprobe.d/ is bind-mounted from the host — not /etc, /proc, or any broader path.curl binary and its shared libraries are staged by the init container into an emptyDir (/tools) so the main container can call the Kubernetes API without needing write access to rootfs.get and patch on nodes only.