Kubernetes Policy

Kubernetes has three distinct policy enforcement mechanisms. They sit at the same point in the request lifecycle — the admission controller — but differ in language, capability, and operational complexity.

KyvernoGatekeeper (OPA)ValidatingAdmissionPolicy
LanguageYAML/JMESPathRegoCEL
Native to K8sNo (CRD)No (CRD)Yes (built-in)
ValidateYesYesYes
MutateYesLimitedYes (1.32+)
GenerateYesNoNo
Image verifyYesNoNo
GA sinceK8s 1.30
Good forFull-featured, K8s-native feelRego-first teams, policy-as-dataSimple rules, no extra install

ValidatingAdmissionPolicy (VAP)

Added in Kubernetes 1.26, GA in 1.30. Policies are built into the API server — no admission controller to deploy or maintain. Policy is written in CEL (Common Expression Language), a simple expression language also used in Kubernetes’ x-kubernetes-validations CRD validation.

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: "require-run-as-non-root"
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
      - apiGroups: ["apps"]
        apiVersions: ["v1"]
        operations: ["CREATE", "UPDATE"]
        resources: ["deployments"]
  validations:
    - expression: >
        object.spec.template.spec.securityContext.runAsNonRoot == true
      message: "Pods must run as non-root"
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: "require-run-as-non-root-binding"
spec:
  policyName: "require-run-as-non-root"
  validationActions: [Deny]
  matchResources:
    namespaceSelector:
      matchLabels:
        enforce-policy: "true"

The ValidatingAdmissionPolicyBinding scopes where a policy applies — cluster-wide, specific namespaces, or by label selector.

CEL basics

CEL expressions have access to object (the incoming resource), oldObject (for updates), request (metadata, user, etc.), and params (a referenced ConfigMap or CRD for parameterisation).

# Simple field check
object.spec.replicas <= 10

# Nested optional field (use ?. for optional traversal)
object.spec.template.spec.?securityContext.?runAsNonRoot == optional.of(true)

# List comprehension — all containers must have limits
object.spec.template.spec.containers.all(c,
  has(c.resources) && has(c.resources.limits)
)

# String operations
object.metadata.name.startsWith("prod-")

MutatingAdmissionPolicy

Added in Kubernetes 1.32 (alpha). Brings CEL-based mutation — set defaults, inject labels, patch fields — without Kyverno or a webhook. Still early; not production-ready yet.

Gatekeeper

OPA running as a Kubernetes admission controller. Policies are written in Rego and stored as ConstraintTemplate CRDs. The separation between template (the Rego logic) and constraint (the enforcement configuration + parameters) is the key design pattern.

apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8srequiredlabels
spec:
  crd:
    spec:
      names:
        kind: K8sRequiredLabels
      validation:
        openAPIV3Schema:
          properties:
            labels:
              type: array
              items: {type: string}
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8srequiredlabels

        violation[{"msg": msg}] {
            provided := {label | input.review.object.metadata.labels[label]}
            required := {label | label := input.parameters.labels[_]}
            missing := required - provided
            count(missing) > 0
            msg := sprintf("missing required labels: %v", [missing])
        }
---
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
  name: require-team-label
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Namespace"]
  parameters:
    labels: ["team", "environment"]

Gatekeeper also supports audit mode — it continuously evaluates existing resources against policies and surfaces violations without blocking. Useful for measuring compliance against policies you’re not yet ready to enforce.

Gatekeeper vs Kyverno

Kyverno is better if your team does not know Rego and wants policies that look like Kubernetes manifests. Gatekeeper is better if you are already invested in OPA/Rego and want a single policy language across K8s and non-K8s surfaces (via Conftest).

The K8s-native VAP is the right default for simple validation rules on new clusters — no extra install, but it does not cover mutation (until 1.32+), generation, or image verification.

Resources

Built with Hugo
Theme Stack designed by Jimmy