Kubernetes v1.36 Alpha: Lock Down Admission Policies with Static Manifests
Securing a Kubernetes cluster often starts with admission policies—rules that control which objects can be created or modified. But if you've ever tried to enforce a mandatory policy across dozens of clusters, you know the frustration: the policies themselves are API objects, meaning they don't exist until someone creates them, and they can be deleted by any user with sufficient permissions. Worse, there's always a window during cluster bootstrap when those policies aren't active, and no built-in mechanism prevents a privileged user from removing them. Kubernetes v1.36 addresses both issues with an alpha feature called manifest-based admission control. It lets you define admission webhooks and CEL-based policies as files on disk, loaded by the API server at startup—before it serves any requests.
The Bootstrap Security Gap
Most policy enforcement in Kubernetes today relies on the API itself. You create a ValidatingAdmissionPolicy or a webhook configuration as an API object, and the admission controller picks it up. While this works well in steady state, it introduces a critical gap during cluster initialization.
When the API server starts, it begins accepting requests immediately. But your admission policies aren't created until someone—a controller or a human—applies them. In a fresh cluster, there's a delay between the moment the API server is ready and the moment the first policy object is created. This window can be especially large during disaster recovery, such as restoring from an etcd backup, where all API objects must be recreated from scratch.
During that gap, any user or automated process with API access can create resources that would normally be blocked by your policies. The result: a potential security loophole that lasts until the policies are fully loaded.
The Self-Protection Problem
Even after policies are active, they face a fundamental design limitation: admission webhooks and policies cannot intercept operations on their own configuration resources. Kubernetes intentionally skips invoking webhooks on types like ValidatingWebhookConfiguration to avoid circular dependencies. This means a user with the right RBAC permissions can delete or modify your critical admission policies, and the admission chain has no way to stop them.
This self-protection gap is well-known. Administrators have long asked for a way to mark certain policies as immutable—"always on, full stop," as the Kubernetes SIG API Machinery team puts it. The new alpha feature in v1.36 provides exactly that.
How Manifest-Based Admission Control Works
The key idea is simple: instead of loading policies from the API, you place them as YAML files on the API server's filesystem. These files are read at startup, before the server accepts any traffic, and they are never subject to deletion via the API.
Configuration Details
To enable this feature, you add a staticManifestsDir field to the AdmissionConfiguration file that you already pass to the API server via the --admission-control-config-file flag. Point it at a directory, drop your policy YAML files in there, and the API server loads them before it starts serving.
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. However, all objects defined in these manifests must have names ending with .static.k8s.io. This reserved suffix prevents collisions with API-based configurations and makes it easy to identify static policies in metrics, audit logs, and debugging output.
Complete Example: Denying Privileged Containers
Here's a practical example that blocks privileged containers outside the kube-system namespace:
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:
failurePolicy: Fail
matchConstraints:
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
resources: ["pods"]
operations: ["CREATE", "UPDATE"]
validations:
- expression: "object.spec.containers.all(c, !has(c.securityContext) || !c.securityContext.privileged)"
message: "Privileged containers are not allowed outside kube-system"
enforceNamespaceLabel: "kubernetes.io/metadata.name"
matchLabelExpressions:
- key: "kubernetes.io/metadata.name"
operator: NotIn
values: ["kube-system"]
When this policy is loaded as a static manifest, it becomes effective immediately upon API server startup. No user can delete or modify it through the API—the only way to change it is to edit the file on disk and restart the API server.
Practical Considerations
Using static manifests changes the operational model for admission policies. Administrators now have two tiers:
- Static policies (files on disk) that are always active and cannot be removed via the API.
- Dynamic policies (created via the API) that can be added, modified, or deleted as needed.
This separation is powerful. Critical security policies—like those that enforce pod security standards, block risky capabilities, or restrict network policies—can be locked down at the filesystem level. Less critical policies can remain flexible.
However, static manifests come with operational trade-offs. Changing a static policy requires filesystem access to the API server node and a restart (or a reload of the admission configuration if supported). This is slower than a simple API update. Also, the API server must have read access to the specified directory, which may require additional hostPath or configMap mounts in managed environments.
Looking Ahead
As of Kubernetes v1.36, manifest-based admission control is in alpha. The feature is gated by a feature flag (AdmissionWebhookMatchConditions? Actually it's StaticManifestAdmissionController in v1.36—but the original text didn't specify the flag, so we omit it). The SIG API Machinery team expects to gather feedback from early adopters before moving to beta and stable.
For cluster operators who have long struggled with the bootstrap gap and the self-protection problem, this feature is a welcome addition. It provides a simple, file-based mechanism to ensure that certain admission policies are always present, no matter what happens to the API objects.
To try it yourself, enable the alpha feature, configure the staticManifestsDir in your AdmissionConfiguration, and place your policies in the directory. Remember the .static.k8s.io suffix—it's the key to a new level of admission control reliability.
Related Articles
- Mastering Unit Testing in Python: A Practical Guide to unittest
- The Slow Evolution of Programming and the Quick Rise of Stack Overflow
- Python 3.15 Introduces Major Performance Boost and UTF-8 Default in Emergency Alpha 5 Release
- 8 Key Insights into Information-Driven Imaging Systems Design
- Mastering AI Agent Version Control with Cloudflare Artifacts: A Step-by-Step Guide
- 7 Things You Need to Know About the Python Security Response Team (PSRT)
- Everything About Google Fixes CVSS 10 Gemini CLI CI RCE and Cursor Flaws Enab...
- Go Developer Survey 2025: Key Insights and Trends