콘텐츠로 이동

Kubernetes 1.36 Security Deep Dive: User Namespaces GA, DRA, and the End of Webhook Ceremony

· 13 min · automation
kubernetescloud-securitycontainer-securitydevops

Kubernetes 1.36, codenamed "Haru," landed on April 22, 2026 after a 15-week release cycle. It ships 70 enhancements total: 18 graduating to stable, 25 moving to beta, and 25 arriving in alpha. The sheer volume of promotions to GA makes this one of the most consequential releases in recent memory, especially for teams focused on security hardening and resource management.

This post breaks down the security-relevant changes you need to understand, with practical configuration examples and a migration checklist so you can plan your upgrade with confidence. For the full changelog, see the official release notes.

User Namespaces Graduate to GA: Root Without the Risk

The single biggest security milestone in 1.36 is the graduation of User Namespaces to stable. This feature fundamentally changes the security posture of containerized workloads by allowing a process to run as UID 0 inside the container while being mapped to an unprivileged UID on the host.

Why does this matter? Container escapes that exploit root privileges become significantly less damaging when the "root" user inside the container is actually nobody on the host kernel. Historically, achieving this required complex runtime configurations and was never consistently supported across the ecosystem. Now it is a first-class, stable API.

Requirements and Prerequisites

User Namespaces require:

  • Linux kernel 6.3 or later (for idmap mounts support)
  • containerd 1.7+ or CRI-O 1.25+
  • The UserNamespacesSupport feature gate (enabled by default in 1.36)

Enabling User Namespaces on a Pod

The configuration is straightforward. Add hostUsers: false to your pod spec:

apiVersion: v1
kind: Pod
metadata:
  name: secure-app
spec:
  hostUsers: false
  containers:
    - name: app
      image: myregistry/app:latest
      securityContext:
        runAsUser: 0
        runAsGroup: 0

With hostUsers: false, the container sees itself running as root, but the host kernel maps that UID to an unprivileged range (typically starting at 65536). You can verify this after deployment:

# Inside the container
$ id
uid=0(root) gid=0(root)

# On the host node
$ ps aux | grep app
65536    12345  0.1  0.2  ... /app

Combining with Pod Security Standards

For defense in depth, pair User Namespaces with Pod Security Admission. Even with user namespace isolation, enforcing the restricted profile catches misconfigurations early:

kubectl label namespace production \
  pod-security.kubernetes.io/enforce=restricted \
  pod-security.kubernetes.io/warn=restricted

As Sysdig's analysis of 1.36 security features notes, User Namespaces close one of the longest-standing gaps in container isolation and should be a priority adoption target for any security-conscious team.

Dynamic Resource Allocation Reaches GA

Dynamic Resource Allocation (DRA) has been in development across multiple release cycles and finally reaches stable in 1.36. DRA provides a standardized, vendor-neutral API for requesting and allocating hardware devices like GPUs, FPGAs, and other specialized accelerators.

What Changed in GA

Three key capabilities solidify in this release:

  • Multi-allocatable devices: A single physical device can be partitioned and allocated to multiple pods simultaneously.
  • Shared GPU access: Critical for AI/ML workloads where multiple inference containers share a single GPU with defined memory and compute slices.
  • AdminAccess mode: Cluster administrators can grant debugging access to devices without modifying workload configurations.

Practical DRA Configuration

Here is an example requesting a GPU slice through DRA:

apiVersion: resource.k8s.io/v1
kind: ResourceClaim
metadata:
  name: gpu-slice
spec:
  devices:
    requests:
      - name: gpu
        deviceClassName: gpu.example.com
        count: 1
        selectors:
          - cel:
              expression: "device.attributes['memory'].quantity >= quantity('4Gi')"
---
apiVersion: v1
kind: Pod
metadata:
  name: ml-inference
spec:
  containers:
    - name: model
      image: myregistry/inference:v2
      resources:
        claims:
          - name: gpu
  resourceClaims:
    - name: gpu
      resourceClaimName: gpu-slice

The AdminAccess feature is particularly useful for debugging device issues without disrupting workloads:

kubectl debug node/gpu-node-01 \
  --image=busybox \
  --admin-access=gpu-slice

Security Implications of DRA

From a security perspective, DRA is a significant improvement over the device plugin model. Device plugins often required containers to run in privileged mode or with broad device access (/dev/* mounts) to interact with hardware. DRA provides fine-grained, API-level access control. You can use standard Kubernetes RBAC to control which namespaces and service accounts are permitted to create ResourceClaim objects for specific device classes, keeping the blast radius of any compromise contained.

DRA replaces the older device plugin API for new hardware integrations. If you maintain custom device plugins, review the upstream migration documentation to plan your transition.

Mutating Admission Policies: The End of Webhook Ceremony

Perhaps the most operationally impactful security feature in 1.36 is the GA graduation of Mutating Admission Policies. This feature lets you define mutation rules directly in Kubernetes manifests using CEL (Common Expression Language), eliminating the need to deploy, maintain, and monitor external webhook servers.

If you have ever spent an afternoon debugging why a webhook was timing out, or traced a production incident back to a webhook server that went down and took pod scheduling with it, this feature is for you.

Before and After

Before (webhook-based mutation): You needed a running HTTP server, a TLS certificate, a MutatingWebhookConfiguration, monitoring, high-availability deployment, and careful failure-mode configuration. A single webhook outage could block all pod creation cluster-wide.

After (policy-based mutation): Everything lives in YAML. The API server evaluates CEL expressions inline. No external dependencies.

Example: Injecting Default Labels

The Kubernetes blog post on manifest-based admission control provides an excellent walkthrough. Here is a practical example that injects a cost-center label on every new pod:

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingAdmissionPolicy
metadata:
  name: inject-cost-center
spec:
  matchConstraints:
    resourceRules:
      - apiGroups: [""]
        apiVersions: ["v1"]
        operations: ["CREATE"]
        resources: ["pods"]
  mutations:
    - patchType: ApplyConfiguration
      applyConfiguration:
        expression: >
          Object{
            metadata: Object.metadata{
              labels: {"cost-center": "engineering"}
            }
          }

Bind it with a MutatingAdmissionPolicyBinding:

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingAdmissionPolicyBinding
metadata:
  name: inject-cost-center-binding
spec:
  policyName: inject-cost-center
  matchResources:
    namespaceSelector:
      matchLabels:
        environment: production

Migration Strategy

Do not rip out your webhooks on day one. Run both in parallel, compare outputs with audit logging, then cut over:

# Enable audit logging for admission mutations
kubectl patch configmap audit-policy -n kube-system --type merge \
  -p '{"data":{"policy.yaml":"rules:\n- level: RequestResponse\n  resources:\n  - group: \"\"\n    resources: [\"pods\"]\n  stages: [\"Mutating\"]"}}'

This approach lets you verify that CEL policies produce identical mutations before decommissioning webhook infrastructure.

Security Benefits Beyond Convenience

The security argument for Mutating Admission Policies goes beyond operational simplicity. Webhook servers are attack surface: they require network connectivity from the API server, they hold TLS certificates that must be rotated, and a compromised webhook server can silently inject malicious mutations into every pod created in the cluster. By moving mutation logic into declarative CEL expressions evaluated by the API server itself, you eliminate an entire category of supply chain and lateral movement risk.

Additionally, CEL policies are auditable by design. Every policy is a Kubernetes object that shows up in kubectl get, in audit logs, and in GitOps repositories. Contrast this with webhook servers where the actual mutation logic lives in application code that may or may not be version-controlled alongside your cluster configuration.

SELinux Label Management Goes Stable

SELinux mount-option handling has been a persistent pain point for teams running RHEL, CentOS, or Fedora-based nodes. Previously, the kubelet would recursively relabel every file in a volume at pod startup, a process that could take minutes for volumes with millions of files.

In 1.36, SELinux label management graduates to GA with mount-option-based labeling. Instead of relabeling files after mount, the kubelet passes the correct SELinux context as a mount option, and the kernel applies it atomically.

Impact on Pod Startup Times

For a volume with 1 million files, the difference is dramatic:

Approach Startup Time
Recursive relabel (old) 30-90 seconds
Mount-option label (new) < 1 second

Configuration

Set the SELinux options in your pod's securityContext:

apiVersion: v1
kind: Pod
metadata:
  name: data-processor
spec:
  securityContext:
    seLinuxOptions:
      level: "s0:c123,c456"
  containers:
    - name: processor
      image: myregistry/processor:latest
      volumeMounts:
        - name: data-vol
          mountPath: /data
  volumes:
    - name: data-vol
      persistentVolumeClaim:
        claimName: large-dataset

The kubelet now handles the mount-level labeling automatically. No feature gate is needed in 1.36 since the feature is stable and enabled by default.

VolumeGroupSnapshot Reaches Stable

Crash-consistent backups across multiple volumes have been a long-requested enterprise feature. VolumeGroupSnapshot graduates to stable in 1.36, enabling atomic point-in-time snapshots of multiple PersistentVolumeClaims simultaneously.

This is essential for stateful applications like databases where data and write-ahead logs live on separate volumes. Snapshotting them independently risks inconsistency.

apiVersion: groupsnapshot.storage.k8s.io/v1
kind: VolumeGroupSnapshot
metadata:
  name: db-consistent-backup
spec:
  source:
    selector:
      matchLabels:
        app: postgres
        role: data
  volumeGroupSnapshotClassName: csi-groupsnap-class

The snapshot is atomic: either all volumes are captured at the same point in time, or the operation fails entirely. No partial snapshots.

For disaster recovery workflows, you can automate group snapshots on a schedule using a CronJob that creates VolumeGroupSnapshot resources, giving you consistent point-in-time recovery across your entire stateful application stack. This is a substantial improvement over the previous approach of coordinating individual VolumeSnapshot objects and hoping the timing was close enough for consistency.

Your CSI driver must support the GroupSnapshot capability. Check with your storage vendor: most major providers including AWS EBS CSI, GCE PD CSI, and several enterprise storage backends have added support in their latest releases.

CRI Streaming: Lighter List Operations

High-density nodes running hundreds of pods have long suffered from the memory overhead of monolithic List RPC calls through the Container Runtime Interface. CRI streaming replaces these with server-side streaming RPCs, sending results incrementally rather than buffering the entire response in memory.

For nodes running 200+ pods, this can reduce kubelet memory usage during list operations by an order of magnitude. The change is transparent to operators: upgrade your container runtime to a version supporting CRI v1.36, and the kubelet negotiates streaming automatically.

This improvement is especially relevant for security monitoring workloads. Security agents and log collectors deployed as DaemonSets need to enumerate containers on each node frequently. With the old monolithic List approach, these queries could cause memory pressure spikes that impacted co-located workloads. Streaming eliminates that concern.

Verify streaming is active on a node:

kubectl get --raw "/api/v1/nodes/<node-name>/proxy/configz" | \
  jq '.kubeletconfig.featureGates.CRIStreamingList'

If you are running a managed Kubernetes service (EKS, GKE, AKS), streaming support depends on when your provider upgrades to a compatible container runtime. Check your provider's 1.36 compatibility matrix before assuming the feature is available.

Notable Deprecations and Removals

kubernetes/ingress-nginx Retired

The kubernetes/ingress-nginx project was officially retired on March 24, 2026. It is no longer receiving security patches. If you are still running it, migration to an actively maintained ingress controller is urgent. Alternatives include:

  • Gateway API with implementations like Envoy Gateway, Istio, or Cilium
  • Traefik with native Gateway API support
  • HAProxy Ingress for teams needing traditional reverse proxy semantics

externalIPs Deprecated

The externalIPs field on Service objects is deprecated due to well-documented security risks (IP spoofing, traffic interception). Replace with LoadBalancer type Services or Gateway API routes. If you must continue using externalIPs during migration, monitor the kubernetes_feature_deprecation metric for timeline updates.

The externalIPs field has been a known vector for CVE-2020-8554 style man-in-the-middle attacks, where a malicious actor with Service creation permissions could intercept traffic destined for any external IP. Its deprecation formalizes what security teams have been recommending for years: do not use this field.

Check your cluster for affected services:

kubectl get svc --all-namespaces -o json | \
  jq -r '.items[] | select(.spec.externalIPs != null) |
  "\(.metadata.namespace)/\(.metadata.name): \(.spec.externalIPs)"'

Storage Improvements: In-Place PV Resize

An alpha feature worth watching: in-place PersistentVolume resize for AWS EBS volumes. Previously, resizing a PV required deleting and recreating the volume or relying on CSI driver-specific behavior. The new alpha API allows dynamic IOPS and throughput adjustment on running volumes without pod disruption.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: high-perf-storage
  annotations:
    ebs.csi.aws.com/iops: "10000"
    ebs.csi.aws.com/throughput: "500"
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 500Gi
  storageClassName: gp3-dynamic

Since this is alpha, enable it explicitly with the InPlacePodVerticalScaling feature gate and test thoroughly in non-production environments first. The ability to adjust IOPS and throughput without pod restarts is particularly valuable for databases and analytics workloads that experience variable load patterns. Instead of over-provisioning storage performance for peak load, you can start conservative and scale dynamically.

Note that this alpha feature currently only supports AWS EBS gp3 and io2 volume types. Support for other cloud providers and volume types is expected in future releases as the API stabilizes.

Upgrade Considerations and Migration Checklist

Upgrading to 1.36 requires planning, especially given the number of features moving to stable and the deprecations taking effect. Based on guidance from VMware's enterprise platform analysis and MetalBear's practical breakdown, here is a structured checklist.

Pre-Upgrade

  1. Verify kernel version: User Namespaces require Linux 6.3+. Check with uname -r on all nodes.
  2. Audit container runtime versions: Ensure containerd >= 1.7 or CRI-O >= 1.25.
  3. Scan for deprecated APIs: Run kubectl deprecations or use tools like pluto to find deprecated API usage.
  4. Inventory ingress-nginx deployments: Plan migration before security patches stop entirely.
  5. Review externalIPs usage: Identify all services using the deprecated field.
# Quick pre-upgrade audit
echo "=== Kernel Version ==="
kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.nodeInfo.kernelVersion}{"\n"}{end}'

echo "=== Container Runtime ==="
kubectl get nodes -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.nodeInfo.containerRuntimeVersion}{"\n"}{end}'

echo "=== External IPs in Use ==="
kubectl get svc -A -o json | jq -r '.items[] | select(.spec.externalIPs) | .metadata.namespace + "/" + .metadata.name'

During Upgrade

  1. Upgrade control plane first, then worker nodes, following the standard version skew policy.
  2. Test User Namespaces on a canary node before rolling out cluster-wide.
  3. Deploy Mutating Admission Policies alongside existing webhooks in shadow mode.
  4. Monitor API server latency: CEL policy evaluation adds marginal overhead during the transition.

Post-Upgrade

  1. Enable hostUsers: false on workloads starting with the most security-sensitive namespaces.
  2. Validate SELinux mount behavior on RHEL/CentOS nodes with large volumes.
  3. Test VolumeGroupSnapshot with your CSI driver to confirm atomic behavior.
  4. Benchmark CRI streaming on high-density nodes to quantify memory savings.
  5. Set up DRA ResourceClaims for GPU and accelerator workloads, replacing legacy device plugins.
# Post-upgrade validation
kubectl get nodes -o wide  # Confirm all nodes are Ready and on v1.36.x

# Verify User Namespaces support
kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: userns-test
  namespace: default
spec:
  hostUsers: false
  containers:
    - name: test
      image: busybox
      command: ["id"]
  restartPolicy: Never
EOF

kubectl logs userns-test  # Should show uid=0(root)

What This Means for Your Security Posture

Kubernetes 1.36 represents a maturation of the platform's security model. User Namespaces eliminate an entire class of container escape vulnerabilities. Mutating Admission Policies remove the operational risk of webhook-based mutation infrastructure. SELinux improvements make mandatory access controls practical at scale. DRA provides secure, fine-grained hardware access without privileged containers.

The common thread across these features is a shift from "security requires complex external tooling" to "security is built into the platform." Teams that adopt these GA features will reduce their attack surface while simultaneously reducing operational complexity.

It is worth noting what is not in this release. Network Policy v2, which would bring more expressive traffic rules, remains in discussion. Service mesh integration at the kubelet level continues to evolve in alpha. And while Gateway API continues to mature, its full feature set has not yet reached stable parity with all Ingress use cases. These are areas to watch in 1.37 and beyond.

For a comprehensive overview of every enhancement in 1.36, including the alpha features not covered here, see Google Cloud's complete breakdown and the official Kubernetes 1.36 release announcement.

Start your upgrade planning today. The security improvements alone justify the effort, and the 18 GA promotions mean you are adopting battle-tested features, not bleeding-edge experiments.