Use KubeFleet to rollout Kernel LPE CVE mitigations

This post shows how to use KubeFleet to simplify the safe rollout of mitigations for CVE-2026-31431 (“Copy Fail”) and CVE-2026-43284 / CVE-2026-43500 (“DirtyFrag”) and CVE-2026-46300 (“Fragnesia”) across multiple Kubernetes clusters. This vulnerability allows a container to escalate to root on the node and impacts Linux nodes until mitigations are applied. Existing nodes require either a node image upgrade or the self-service DaemonSet mitigation shown in this post.

The DaemonSet used in this post originates from the Azure Kubernetes Service (AKS) advisory and detailed mitigation guide for per-cluster application which can be found in GitHub Issue 5753.

Before you begin

Before following the instructions in this post you must first configure your clusters as members of a fleet, which requires one cluster to act as the hub cluster.

  • Configure a cluster as your KubeFleet hub cluster (instructions) and make sure you have the kubeconfig downloaded.

  • Add remaining Kubernetes clusters as member clusters (instructions).

  • Add labels to member clusters that can be used for rollout grouping. In this example we label the three clusters using dev, test, prod as the stages.

kubectl label membercluster my-member-cluster-01 upgroup=dev
kubectl label membercluster my-member-cluster-02 upgroup=test
kubectl label membercluster my-member-cluster-03 upgroup=prod

Apply mitigation

If you are unable to patch your nodes to an OS release that fixes the CVEs you can apply a DaemonSet that disables the vulnerable kernel modules.

The DaemonSet is deployed into its own Namespace on each cluster, making it easy to discern intent and easier to distribute across clusters.

Given how DaemonSets function, the protection is applied to the node, even though we don’t use the kube-system Namespace as shown in the original AKS mitigation guide.

For the rollout we’ll use KubeFleet’s resource placement features:

Step 1: Create the mitigation Namespace and DaemonSet

Create the 01-kernel-lpe-mitigate.yaml file with the following contents. The DaemonSet blocks vulnerable modules covered by the CVEs.

apiVersion: v1
kind: Namespace
metadata:
  name: kernel-lpe-cve-mitigate-ns
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: kernel-lpe-mitigate
  namespace: kernel-lpe-cve-mitigate-ns
  labels:
    app: kernel-lpe-mitigate
    purpose: security-mitigation
spec:
  selector:
    matchLabels:
      app: kernel-lpe-mitigate
  template:
    metadata:
      labels:
        app: kernel-lpe-mitigate
    spec:
      hostPID: true
      priorityClassName: system-node-critical
      nodeSelector:
        kubernetes.io/os: linux
      tolerations:
        - operator: Exists
      containers:
        - name: mitigate
          image: mcr.microsoft.com/cbl-mariner/busybox:2.0
          command:
            - /bin/sh
            - -c
            - |
              echo "=== Kernel LPE Module Mitigation ==="
              echo "Covers: CVE-2026-31431 (algif_aead), DirtyFrag (esp4/esp6/rxrpc)"

              MODULES="algif_aead esp4 esp6 rxrpc"

              for mod in $MODULES; do
                if ! grep -qs "install ${mod} /bin/false" /host/etc/modprobe.d/*.conf 2>/dev/null; then
                  printf "install %s /bin/false\nblacklist %s\n" "$mod" "$mod" >> /host/etc/modprobe.d/disable-kernel-lpe.conf
                  echo "Blocked ${mod}"
                else
                  echo "${mod} already blocked"
                fi

                if chroot /host grep -q "^${mod} " /proc/modules 2>/dev/null; then
                  if chroot /host modprobe -r "$mod" 2>/dev/null; then
                    echo "Unloaded ${mod}"
                  else
                    echo "WARNING: Could not unload ${mod} (in use). Reboot node."
                  fi
                fi
              done

              echo "=== Mitigation complete. Sleeping ==="
              sleep infinity
          resources:
            requests:
              cpu: 10m
              memory: 16Mi
            limits:
              memory: 32Mi
          securityContext:
            privileged: true
          volumeMounts:
            - name: host-root
              mountPath: /host
      volumes:
        - name: host-root
          hostPath:
            path: /
            type: Directory

Stage the mitigation namespace and DaemonSet on the KubeFleet hub cluster.

kubectl apply -f 01-kernel-lpe-mitigate.yaml

Step 2: Create a ClusterResourcePlacement

Create a placement manifest 02-fleet-pick-clusters-mitigation.yaml that selects all KubeFleet member clusters (PickAll) as suitable to receive the Namespace and its DaemonSet. We set the strategy to External so we can define and control the rollout order for clusters, which we will do in following steps.

apiVersion: placement.kubernetes-fleet.io/v1
kind: ClusterResourcePlacement
metadata:
  name: kernel-lpe-cve-mit-ns-place
spec:
  resourceSelectors:
  - group: ""
    version: v1
    kind: Namespace
    name: kernel-lpe-cve-mitigate-ns
  policy:
    placementType: PickAll
  strategy:
    type: External

Apply the placement on KubeFleet hub cluster.

kubectl apply -f 02-fleet-pick-clusters-mitigation.yaml

You can validate how many member clusters will receive the mitigation using this command.

kubectl get clusterresourceplacement kernel-lpe-cve-mit-ns-place -o jsonpath="{.status.conditions[?(@.type=='ClusterResourcePlacementScheduled')].message}"

The message in the response shows how many clusters were selected.

found all cluster needed as specified by the scheduling policy, found 3 cluster(s)

Step 3: Create a ClusterStagedUpdateStrategy

A strategy is used to define the rollout order and soak duration per rollout stage. Note that all selected clusters must be in a stage. This is determined by the labels set of the cluster when it was added to the fleet.

In this sample 03-fleet-mitigation-rollout-strategy.yaml we have three stages, with a 4 hour soak time between each stage. You can also add approvals if required.

apiVersion: placement.kubernetes-fleet.io/v1
kind: ClusterStagedUpdateStrategy
metadata:
  name: kernel-lpe-cve-mit-ds-strategy
spec:
  stages:
    - name: stage1
      labelSelector:
        matchLabels:
          upgroup: dev
      afterStageTasks:
        - type: TimedWait
          waitTime: 4h
      maxConcurrency: 1
    - name: stage2
      labelSelector:
        matchLabels:
          upgroup: test
      afterStageTasks:
        - type: TimedWait
          waitTime: 4h
      maxConcurrency: 1
    - name: stage3
      labelSelector:
        matchLabels:
          upgroup: prod

Apply the strategy on KubeFleet hub cluster.

kubectl apply -f 03-fleet-mitigation-rollout-strategy.yaml

Step 4: Start rollout

Finally, create a ClusterStagedUpdateRun 04-fleet-mitigation-rollout-run.yaml to begin the rollout.

NOTE: To start the rollout later, set state to Initialize. You can patch the resource to Run to begin rollout (see below).

apiVersion: placement.kubernetes-fleet.io/v1
kind: ClusterStagedUpdateRun
metadata:
  name: kernel-lpe-cve-mit-ds-rollout
spec:
  placementName: kernel-lpe-cve-mit-ns-place
  stagedRolloutStrategyName: kernel-lpe-cve-mit-ds-strategy
  state: Run

Apply the update run on KubeFleet hub cluster. If you have Run as the state, the rollout begins.

kubectl apply -f 04-fleet-mitigation-rollout-run.yaml

Step 5: Monitor and control rollout

Use the following command to retrieve the status of the rollout. Inspect the conditions and stageStatuses arrays to review progress.

kubectl get clusterstagedupdaterun kernel-lpe-cve-mit-ds-rollout -o yaml

NOTE: You can also use the preview version of KubeFleet’s Headlamp plugin to help monitor and control the rollout. Connect it to the KubeFleet hub cluster to use.

If you want to stop the rollout, you can patch the update run:

kubectl patch clusterstagedupdaterun kernel-lpe-cve-mit-ds-rollout --type merge -p '{"spec":{"state":"Stop"}}'

Restart the rollout by patching the update run:

kubectl patch clusterstagedupdaterun kernel-lpe-cve-mit-ds-rollout --type merge -p '{"spec":{"state":"Run"}}'

Step 6: Validate mitigation is applied

Select clusters in your fleet that have received the mitigation and run the following check.

kubectl logs -n kernel-lpe-cve-mitigate-ns -l app=kernel-lpe-mitigate

If the mitigation is applied successfully you should see.

=== Kernel LPE Module Mitigation ===
Covers: CVE-2026-31431 (algif_aead), DirtyFrag (esp4/esp6/rxrpc)
Blocked algif_aead
Blocked esp4 
Blocked esp6
Blocked rxrpc
=== Mitigation complete. Sleeping ===

NOTE: In some cases where the module is in use, the node will require a reboot. The log message will indicate this is required.

Step 7: Roll back or remove

If issues occur, or the DaemonSet is no longer required as the node image has been updated to a permanently patched release, you can remove the mitigation DaemonSet by deleting the ClusterResourcePlacement on the KubeFleet hub cluster.

kubectl delete clusterresourceplacement kernel-lpe-cve-mit-ns-place

Summary

In this post we’ve seen how you can use KubeFleet to:

  • Apply an immediate mitigation across all clusters via DaemonSet.
  • Use a controlled rollout to reduce risk, allowing testing on clusters in batches.
  • No node reboot required when module not in use.

If you’re interested to learn more about KubeFleet, consider joining a KubeFleet community call.