Kubernetes v1.36 Introduces Manifest-Based Admission Control for Irremovable Policies
The Problem: Why Admission Policies Are Vulnerable
Enforcing security policies across a fleet of Kubernetes clusters often feels like a never‑ending game of cat and mouse. Admission policies are themselves API objects, meaning they don’t exist until someone creates them—and anyone with sufficient privileges can delete them at any time. This creates a frustrating chicken‑and‑egg scenario: you can’t protect your cluster with policies until the policies are created, but during that creation window, the cluster is essentially unprotected.
Even worse, there’s a significant gap during cluster bootstrap. The API server starts serving requests before your admission configurations are created and active. The same gap appears when restoring from a backup or recovering from an etcd failure, leaving a window where no policy enforcement exists.
Kubernetes also has a self‑protection blind spot: admission webhooks and policies cannot intercept operations on their own configuration resources. For example, Kubernetes deliberately skips invoking webhooks on ValidatingWebhookConfiguration objects to avoid circular dependencies. This means a sufficiently privileged user can delete critical admission policies without any intervention from the admission chain itself.
As a result, the Kubernetes SIG API Machinery recognized the need for a mechanism that says, “these policies are always on, full stop.”
Introducing Manifest-Based Admission Control (Alpha)
Kubernetes v1.36 ships an alpha feature that directly addresses this gap: manifest‑based admission control. The idea is simple: instead of relying solely on API objects, you define admission webhooks and CEL‑based policies as YAML files stored on disk. The API server loads these files at startup—before it begins serving any requests—ensuring that critical policies are always active from the moment the cluster is available.
How to Configure It
You enable the feature by adding a staticManifestsDir field to your existing AdmissionConfiguration file (the one you pass to the API server via the --admission-control-config-file flag). Point that field to a directory containing your policy YAML files, and the API server will load them during its initialization sequence.
apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: ValidatingAdmissionPolicy
configuration:
apiVersion: apiserver.config.k8s.io/v1
kind: ValidatingAdmissionPolicyConfiguration
staticManifestsDir: "/etc/kubernetes/admission/validating-policies/"
The manifest files themselves are standard Kubernetes resource definitions. The only extra requirement is that every object defined in these manifests must have a name ending with .static.k8s.io.
The .static.k8s.io Suffix Requirement
This reserved suffix serves two purposes. First, it prevents naming collisions between manifest‑based policies and API‑created ones. Second, it makes it easy to identify the source of an admission decision when examining metrics or audit logs—if you see a policy name ending in .static.k8s.io, you know it came from a disk‑based manifest and cannot be removed through the API.
A Concrete Example: Denying Privileged Pods
Here’s a complete example that denies privileged containers outside the kube-system namespace, using a CEL‑based ValidatingAdmissionPolicy:
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
name: "deny-privileged.static.k8s.io"
annotations:
kubernetes.io/description: "Deny launching privileged pods, anywhere this policy is applied"
spec:
matchConstraints:
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["pods"]
validations:
- expression: "!has(object.spec.containers) || object.spec.containers.all(c, !has(c.securityContext) || !has(c.securityContext.privileged) || c.securityContext.privileged != true) && (!has(object.spec.initContainers) || object.spec.initContainers.all(c, !has(c.securityContext) || !has(c.securityContext.privileged) || c.securityContext.privileged != true))"
message: "Privileged containers are not allowed outside kube-system."
- namespaceSelector:
matchExpressions:
- key: kubernetes.io/metadata.name
operator: NotIn
values: ["kube-system"]
When placed in the staticManifestsDir directory, this policy is loaded before the API server starts serving requests, ensuring that privileged pods cannot be created even during the initial bootstrap phase.
Benefits and Use Cases
Manifest‑based admission control closes the critical bootstrap gap. Whether you’re starting a brand‑new cluster, recovering from a disaster, or simply want to ensure that certain policies cannot be deleted accidentally or maliciously, this feature provides a reliable, always‑active layer of enforcement.
Key benefits include:
- Always‑on enforcement – Policies are loaded before any user request reaches the API server.
- Self‑protection – Policies loaded from disk cannot be altered or deleted through the Kubernetes API, eliminating the privileged‑user loophole.
- Clear auditability – The
.static.k8s.iosuffix makes it trivial to identify which policies are manifest‑based. - No circular dependency – Because these policies are loaded at startup, they avoid the chicken‑and‑egg problem that plagues API‑based admission configurations.
This feature is currently in alpha. The Kubernetes SIG API Machinery encourages you to test it in your own clusters and provide feedback through the usual Kubernetes channels.
Related Articles
- Go Team Launches 2025 Developer Survey, Seeks Global Input on Language Evolution
- How to Upgrade to Go 1.26 and Make the Most of Its New Features
- How to Build Type-Safe LLM Agents Using Pydantic AI: A Step-by-Step Guide
- Empowering Autonomous AI Agents on Cloudflare: A Step-by-Step Guide to Seamless Deployment
- Breaking: New Quiz Challenges Developers to Master Qt Designer for Rapid Python GUI Development
- Microsoft Opens a Historic Chapter: 86-DOS Source Code Released 45 Years Later
- Kubernetes v1.36 Delivers Declarative Validation: What It Means and How It Works
- How to Upgrade to Go 1.26 and Master Its New Features