How to Implement Immutable Admission Policies in Kubernetes v1.36 Using Manifest Files

From Moocchen, the free encyclopedia of technology

Introduction

Securing a Kubernetes cluster often means enforcing admission policies that prevent misconfigurations or malicious actions. But traditional admission policies have a fundamental flaw: they are API objects, meaning they can be deleted by anyone with sufficient permissions, and they don't exist until created—leaving a window of vulnerability during cluster bootstrap. Kubernetes v1.36 addresses this with an alpha feature called manifest-based admission control. This guide will walk you through the process of defining admission policies as files on disk that the API server loads before serving any requests—policies that literally cannot be deleted or bypassed.

How to Implement Immutable Admission Policies in Kubernetes v1.36 Using Manifest Files

What You Need

  • A Kubernetes cluster running v1.36 or later with the ValidatingAdmissionPolicy feature gate enabled.
  • Access to the API server configuration file (usually passed via --admission-control-config-file).
  • Permissions to modify API server startup parameters and add files to the server's filesystem.
  • Basic familiarity with YAML syntax and Kubernetes admission resources (ValidatingAdmissionPolicy, ValidatingAdmissionPolicyBinding).
  • Optional: kubectl and etcdctl for testing and verification.

Step-by-Step Guide

Step 1: Prepare the Admission Configuration File

The API server already uses an AdmissionConfiguration file to configure plugins. If you don't have one, create it. This file will be referenced later by the --admission-control-config-file flag.

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/"

Save this as admission-config.yaml. The staticManifestsDir field points to a directory where you'll drop your policy files.

Step 2: Create the Policy Manifest Directory

On each API server node, create the directory specified in the configuration. Ensure the API server process (usually running under a user like kube-apiserver) has read permissions.

sudo mkdir -p /etc/kubernetes/admission/validating-policies/

Make sure the directory is secure—only root or the API server user should be able to write to it.

Step 3: Write Your Policy Manifest Files

Create YAML files with standard Kubernetes resource definitions—any admission policy type supported by the plugin (e.g., ValidatingAdmissionPolicy, ValidatingAdmissionPolicyBinding, or custom webhook configurations). The only requirement: each resource's name must end with .static.k8s.io. This suffix prevents name collisions with API-object-based policies and helps identify static policies in metrics and audit logs.

Example: deny-privileged.static.k8s.io

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) || !has(c.securityContext.privileged) || c.securityContext.privileged != true)"
    message: "Privileged containers are not allowed"

Save this as deny-privileged.yaml in the directory created earlier.

Step 4: Configure the API Server to Load Static Manifests

Ensure your API server is started with the --admission-control-config-file flag pointing to the configuration file from Step 1. If you're using kubeadm or a managed control plane, adjust accordingly. For self-managed clusters, modify the API server manifest (e.g., /etc/kubernetes/manifests/kube-apiserver.yaml for static pods) to include:

--admission-control-config-file=/etc/kubernetes/admission-config.yaml

Restart the API server to pick up the changes. The API server will load all YAML files from the staticManifestsDir before it starts serving requests.

Step 5: Verify the Policies Are Active

Check that the policy is listed as a static resource. You can't delete it via the API, but you can see it in the list of policies using kubectl get validatingadmissionpolicy. The name will include the .static.k8s.io suffix.

kubectl get validatingadmissionpolicy
NAME                                  AGE
deny-privileged.static.k8s.io         5m

Test the policy by attempting to create a privileged pod:

kubectl run test --image=nginx --privileged
Error from server: admission webhook "deny-privileged.static.k8s.io" denied the request: Privileged containers are not allowed

Step 6: Confirm the Policy Cannot Be Deleted

Try to delete the static policy via kubectl:

kubectl delete validatingadmissionpolicy deny-privileged.static.k8s.io
Error from server (Forbidden): ...

The API server will reject the deletion because the resource is loaded from disk and is considered immutable. Even a cluster admin cannot remove it through the API. To remove or modify a static policy, you must edit the file on disk and restart the API server.

Tips

  • Use a version-controlled directory: Store your policy manifests in a Git repository and deploy them to the staticManifestsDir via a configuration management tool (e.g., Ansible, Puppet, or a Kubernetes operator). This ensures consistency across your fleet.
  • Test policies in a non-production cluster first: Since static policies cannot be easily removed, validate them in a staging environment to avoid locking yourself out.
  • Monitor with audit logs: The .static.k8s.io suffix appears in audit entries, making it easy to distinguish static decisions from API-driven ones. Use this for compliance reporting.
  • Combine with dynamic policies: Static policies are great for baseline security; use API-based policies for dynamic rules that need frequent updates.
  • Watch for circular dependencies: Static policies can be applied to API server configuration resources, but be cautious – you could accidentally block access to the very resources that manage the cluster.
  • Plan for upgrades: When upgrading Kubernetes, ensure the manifest directory still exists and contains valid YAML. The API server will fail to start if any manifest is invalid.

With manifest-based admission control, you can close the bootstrap gap and prevent privileged users from removing critical policies. This feature marks a significant step toward truly immutable cluster security.