10 Key Facts About Kubernetes v1.36’s Immutable Admission Policies
Kubernetes v1.36 introduces a groundbreaking feature that changes how admission policies are managed: manifest-based admission control. For years, administrators have struggled with the bootstrap gap and self-protection issues inherent in API-driven policies. This alpha feature addresses both by loading policies directly from disk at API server startup, making them truly immutable and always active. Here are 10 things you need to know about this game-changing enhancement.
1. The Bootstrap Gap
When a Kubernetes cluster starts, admission policies like ValidatingAdmissionPolicy must be created via the API. This creates a window—from when the API server begins serving to when policies are applied—where no enforcement exists. During recovery from etcd failures or backup restores, this gap can last minutes. Malicious actors could exploit this by creating privileged pods before policies take effect. Manifest-based policies eliminate this entirely by loading at startup, before any requests are processed.
2. The Self-Protection Problem
Admission webhooks cannot intercept operations on their own configuration objects (e.g., ValidatingWebhookConfiguration) due to circular dependency avoidance. This means any user with delete permissions on those resources can remove critical security policies. In v1.36, static manifests are not API objects, so they cannot be deleted or modified via the API. They are truly immutable, closing the self-protection loophole once and for all.
3. How It Works: staticManifestsDir
The feature uses a new field staticManifestsDir in the AdmissionConfiguration file passed via --admission-control-config-file. You point this directory at a folder containing your policy YAML files. The API server reads these files during initialization, before it starts its listener. All policies defined there become active immediately and persist even if the files are removed later (the loaded policies remain for the lifetime of the API server process).
4. Naming Convention Requirement
All policy objects defined in static manifest files must have names ending with .static.k8s.io. This reserved suffix prevents collisions with API-created policies of the same name. For example, a ValidatingAdmissionPolicy must be named deny-privileged.static.k8s.io. This naming hint also appears in audit logs and metrics, making it easy to identify static policies versus API-driven ones.
5. Supported Policy Types
Currently, the alpha supports two types of admission configurations: ValidatingAdmissionPolicy (with CEL expressions) and ValidatingAdmissionPolicyBinding. These are the same resources you create via the API, but loaded from disk. Mutating policies are not yet supported in this alpha. Webhook configurations (ValidatingWebhookConfiguration and MutatingWebhookConfiguration) cannot be loaded from static manifests at this time.
6. Example Policy: Deny Privileged Pods
Here’s a complete static policy that blocks privileged containers outside kube-system:
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"]
operations: ["CREATE", "UPDATE"]
resources: ["pods"]
validations:
- expression: "!(has(object.spec.containers) && object.spec.containers.all(c, has(c.securityContext) && c.securityContext.privileged))"
message: "Privileged containers are not allowed in this namespace."
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: "deny-privileged-binding.static.k8s.io"
spec:
policyName: "deny-privileged.static.k8s.io"
matchResources:
namespaceSelector:
matchExpressions:
- key: kubernetes.io/metadata.name
operator: NotIn
values: ["kube-system"]
7. Combining with API-Based Policies
Static manifests do not replace API-based policies; they complement them. You can have both static and API-created policies active simultaneously. However, static policies take precedence in case of conflicts because they load first. If a static policy and an API policy have the same name (using the reserved suffix), the static one wins. This allows you to enforce baseline rules that cannot be overridden, while still giving flexibility for dynamic policies.
8. Operations and Lifecycle
Static policies are loaded once at API server startup and remain until the process restarts. They cannot be created, updated, or deleted via the API. To change a static policy, you must modify the manifest files and restart the API server. This trade-off is intentional: it guarantees immutability and security at the cost of requiring a rollout for updates. For high-availability clusters, all API servers must have identical static manifests to ensure consistency.
9. Monitoring and Troubleshooting
Because static policies use the reserved suffix .static.k8s.io, you can easily filter them in metrics and audit logs. The API server includes a new metric apiserver_admission_webhook_static_manifest_count and logs which static policies are loaded. If a manifest file contains invalid YAML or a policy that fails validation, the API server will fail to start, preventing misconfigurations from silently slipping in. This provides a strong safety net.
10. Alpha Status and Next Steps
This feature is alpha in Kubernetes v1.36, meaning it is disabled by default. To enable it, set the feature gate AdmissionWebhookMatchConditions (the actual feature gate name may vary—consult the release notes). The Kubernetes SIG API Machinery is gathering feedback for future enhancements, including support for mutating policies and webhook configurations. Test it in non-production clusters to validate your security baseline.
Manifest-based admission control closes long-standing gaps in Kubernetes policy enforcement. By loading policies from disk at startup, it ensures critical rules are always active and impossible to delete via the API. While still alpha, this feature represents a significant step toward a more secure and predictable cluster environment. Start experimenting with it today to prepare for its eventual stabilization.
Related Articles
- How to Implement Safe Configuration Rollouts at Scale: A Step-by-Step Guide
- The Complete Guide to Go 1.26: 10 Key Updates You Should Know
- Microsoft Replaces C++ Node.js Addons with C# and .NET Native AOT in C# Dev Kit
- Python 3.15.0 Alpha 6 Launches with Major Performance Boosts and New Profiler
- When Observability Becomes Dependency: Hyrum's Law, Restartable Sequences, and the TCMalloc Dilemma
- GitHub Launches Declarative Security Modeling in CodeQL for Faster, Custom Analysis
- 10 Surprising Ways a $30 USB Drive Can Rescue Your PC from Costly Repairs
- Microsoft Opens DOS Vault: Earliest Source Code Released for 45th Anniversary