Share
## https://sploitus.com/exploit?id=92A6859C-BEA4-590A-B207-77F394842034
# CVE-2026-31431 Mitigation for Deckhouse Kubernetes Platform

**Language:** English | [Русский](README_RU.md)

## About the vulnerability

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.

## Files in this repository

This repository contains everything needed to mitigate CVE-2026-31431 on a
Deckhouse-managed cluster:

| 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. |

### `ngc-cve-2026-31431.yaml` β€” node-side mitigation

The `NodeGroupConfiguration` runs through `bashible` on every node and performs
four steps:

1. **Probe** β€” opens an `AF_ALG` socket and binds it to `aead/gcm(aes)` to
   determine whether the vulnerable interface is reachable on the node.
2. **Block** β€” writes `/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.
3. **Unload** β€” runs `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).
4. **Verify** β€” re-runs the probe and fails the bashible step (non-zero exit)
   if the AEAD interface is still reachable, so the bashible status surfaces
   the node as not-mitigated.

Idempotent: re-running the configuration on an already-mitigated node is a
no-op.

### `far-cve-2026-31431.yaml` β€” runtime detection

The `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:** `FalcoAuditRules` is reconciled by the
> `runtime-audit-engine` module, which is shipped only in Deckhouse
> **Enterprise Edition (EE)** and **Certified Security Edition (CSE)**. On
> Community Edition the resource will not be processed; the
> `NodeGroupConfiguration` from this repository still applies and is the
> primary mitigation.

## Applying

```shell
kubectl apply -f ngc-cve-2026-31431.yaml
# EE / CSE only:
kubectl apply -f far-cve-2026-31431.yaml
```

### Waiting for convergence

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:

```shell
kubectl get nodegroups
```

```text
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:

```shell
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.

## Rollback

Once the upstream kernel package containing the official fix is installed on
every node, remove both resources:

```shell
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.

## Manual verification

Cluster-wide:

```shell
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/` + `chroot /host`):

```shell
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`.