KCSA — OPA & Kyverno — Policy Enforcement

6 minute read

OPA & Kyverno — Policy Enforcement

Scanning finds vulnerabilities. Runtime detection catches threats. But the most effective security control is prevention — stopping insecure resources from being created in the first place. Policy enforcement engines sit at the admission control layer and act as the last gate before a resource is persisted to etcd.


Policy Enforcement at Admission Time

When a user or controller submits a resource to the Kubernetes API server, the request passes through several stages:

Request → Authentication → Authorization (RBAC) → Admission Control → etcd
                                                        ↑
                                          Mutating → Validating webhooks
                                          (OPA/Kyverno intercept here)

Validating admission webhooks can reject requests that violate policy. Mutating admission webhooks can modify requests to add defaults or inject required fields.

Policy engines like OPA/Gatekeeper and Kyverno register as admission webhooks and evaluate every relevant API request against your defined policies.

Admission control is the most critical enforcement point — if a misconfigured pod is rejected here, it never runs. This is fundamentally different from runtime detection (Falco), which only detects threats after they occur.


OPA — Open Policy Agent

What Is OPA?

OPA (Open Policy Agent) is a CNCF Graduated project that provides a general-purpose policy engine. OPA is not Kubernetes-specific — it can enforce policy across microservices, CI/CD pipelines, API gateways, Terraform plans, and more.

Key characteristics:

  • CNCF Graduated — production-ready, widely adopted
  • General purpose — not limited to Kubernetes
  • Rego language — policies are written in Rego, a declarative query language
  • Decoupled — OPA evaluates policy; it doesn’t make decisions about what to do with the result

The Rego Language

OPA policies are written in Rego, a purpose-built, declarative query language:

package kubernetes.admission

deny[msg] {
  input.request.kind.kind == "Pod"
  container := input.request.object.spec.containers[_]
  not container.securityContext.runAsNonRoot
  msg := sprintf("Container '%v' must set runAsNonRoot to true", [container.name])
}

Rego is powerful but has a steep learning curve — this is one of the key distinctions between OPA and Kyverno on the exam.


OPA Gatekeeper

Gatekeeper is the Kubernetes-specific integration of OPA. It bridges OPA’s general-purpose engine with Kubernetes admission control using Custom Resource Definitions (CRDs).

Gatekeeper Architecture

API Request → Admission Webhook → Gatekeeper Controller
                                        ↓
                              Load ConstraintTemplate (Rego)
                                        ↓
                              Evaluate against Constraint (parameters)
                                        ↓
                              Allow or Deny

Two-Step Model: ConstraintTemplate + Constraint

Gatekeeper separates what to check (the Rego logic) from where to apply it (the parameters and scope):

Step 1 — ConstraintTemplate (defines the Rego logic):

apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata:
  name: k8srequiredlabels
spec:
  crd:
    spec:
      names:
        kind: K8sRequiredLabels
      validation:
        openAPIV3Schema:
          type: object
          properties:
            labels:
              type: array
              items:
                type: string
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package k8srequiredlabels
        violation[{"msg": msg}] {
          provided := input.review.object.metadata.labels
          required := input.parameters.labels[_]
          not provided[required]
          msg := sprintf("Missing required label: %v", [required])
        }

Step 2 — Constraint (applies the template with parameters):

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
  name: require-team-label
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Namespace"]
  parameters:
    labels:
      - "team"
      - "environment"

This two-step model allows platform teams to write reusable policy logic (ConstraintTemplate) while letting individual teams configure specific parameters (Constraint).

Gatekeeper Audit Mode

Gatekeeper can run in audit mode to detect violations in existing resources — not just new requests:

Mode Behavior
Enforce Reject admission requests that violate policy
Dry-run Log violations but allow the request (set enforcementAction: dryrun)
Audit Periodically scan existing resources and report violations

Audit mode is valuable for understanding your current violation landscape before enforcing policies that could break existing workloads.

# Check audit violations (conceptual)
kubectl get k8srequiredlabels require-team-label -o yaml
# status.violations shows existing resources that fail the policy

Kyverno

What Is Kyverno?

Kyverno is a Kubernetes-native policy engine and a CNCF Incubating project. Unlike OPA, Kyverno policies are written in YAML — no new language to learn.

Key characteristics:

  • CNCF Incubating — actively growing, production-ready
  • Kubernetes-native — policies are Kubernetes resources (CRDs)
  • YAML policies — no Rego, no new language
  • Four policy types — Validate, Mutate, Generate, VerifyImages
  • Lower learning curve — easier for teams already comfortable with Kubernetes YAML

Kyverno Policy Types

Type What It Does Example
Validate Reject resources that violate rules Require readOnlyRootFilesystem: true
Mutate Automatically modify resources Add default resource limits
Generate Create additional resources when a trigger fires Auto-create NetworkPolicy when a namespace is created
VerifyImages Verify container image signatures (cosign/sigstore) Only allow signed images from trusted registries

Kyverno Validate Policy Example

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-run-as-non-root
spec:
  validationFailureAction: Enforce
  rules:
    - name: check-run-as-non-root
      match:
        any:
          - resources:
              kinds:
                - Pod
      validate:
        message: "Containers must run as non-root"
        pattern:
          spec:
            containers:
              - securityContext:
                  runAsNonRoot: true

Kyverno Mutate Policy Example

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: add-default-resources
spec:
  rules:
    - name: add-resource-limits
      match:
        any:
          - resources:
              kinds:
                - Pod
      mutate:
        patchStrategicMerge:
          spec:
            containers:
              - (name): "*"
                resources:
                  limits:
                    memory: "256Mi"
                    cpu: "500m"

Kyverno Generate Policy Example

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: generate-networkpolicy
spec:
  rules:
    - name: default-deny-on-namespace
      match:
        any:
          - resources:
              kinds:
                - Namespace
      generate:
        kind: NetworkPolicy
        apiVersion: networking.k8s.io/v1
        name: default-deny
        namespace: ""
        data:
          spec:
            podSelector: {}
            policyTypes:
              - Ingress
              - Egress

ClusterPolicy vs Policy

Scope Resource Applies To
Cluster-wide ClusterPolicy All namespaces (or specific namespaces via match/exclude)
Namespace-scoped Policy Only the namespace where it is created

Use ClusterPolicy for organization-wide rules and Policy for team-specific rules within a namespace.


Side-by-Side Comparison

This is one of the most exam-relevant tables in this chapter:

Feature OPA / Gatekeeper Kyverno
CNCF Status Graduated Incubating
Policy Language Rego (custom DSL) YAML (Kubernetes-native)
Learning Curve Steep — Rego is a new language Gentle — YAML is familiar
Validation Yes (via Rego logic) Yes (via validate rules)
Mutation Yes (template-based) Yes (native mutate rules)
Resource Generation No Yes (generate rules)
Image Verification Via custom Rego Built-in (verifyImages with cosign)
Background Scan Yes (audit mode) Yes (background scan of existing resources)
General Purpose Yes — works beyond Kubernetes No — Kubernetes only
Policy Model ConstraintTemplate → Constraint (two-step) Single ClusterPolicy/Policy resource
Ecosystem Large library of community ConstraintTemplates Large library of community policies

For the KCSA exam, the most commonly tested distinctions are: Rego vs YAML, Graduated vs Incubating, and Kyverno’s generate + verifyImages capabilities that OPA/Gatekeeper lacks natively.


ValidatingAdmissionPolicy — Kubernetes Native

Starting with Kubernetes 1.26 (beta in 1.28, GA in 1.30), Kubernetes itself provides a built-in policy mechanism without requiring external webhooks:

ValidatingAdmissionPolicy uses CEL (Common Expression Language) expressions:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: require-non-root
spec:
  matchConstraints:
    resourceRules:
      - apiGroups: [""]
        apiVersions: ["v1"]
        operations: ["CREATE"]
        resources: ["pods"]
  validations:
    - expression: "object.spec.containers.all(c, c.securityContext.runAsNonRoot == true)"
      message: "All containers must run as non-root"
Aspect ValidatingAdmissionPolicy OPA/Kyverno
Built-in Yes (no webhook needed) No (requires external deployment)
Language CEL expressions Rego / YAML
Feature richness Basic validation only Validate, mutate, generate, verify
Maturity GA in 1.30 Mature (years of production use)
Performance No webhook latency Webhook round-trip overhead

ValidatingAdmissionPolicy is simpler and faster but currently limited to validation — it cannot mutate, generate resources, or verify images.


When to Choose Each Tool

Scenario Recommended Tool
Simple validation only, performance critical ValidatingAdmissionPolicy (CEL)
Organization already uses OPA for non-K8s policies OPA / Gatekeeper (unified policy engine)
Team wants YAML-only, no new language Kyverno
Need to auto-generate resources (NetworkPolicy, defaults) Kyverno
Need image signature verification Kyverno (built-in cosign support)
Complex cross-resource policy logic OPA / Gatekeeper (Rego is more expressive)
Want both mutation and validation Either (both support it)

Key Takeaways for the Exam

  1. Admission control = prevention — policy engines stop bad resources before they are created
  2. OPA is CNCF Graduated, general-purpose — uses Rego language, works beyond Kubernetes
  3. Gatekeeper = OPA + Kubernetes — uses ConstraintTemplate (Rego logic) + Constraint (parameters)
  4. Kyverno is CNCF Incubating, Kubernetes-native — uses YAML policies, no new language
  5. Kyverno has four policy types — Validate, Mutate, Generate, VerifyImages
  6. Rego vs YAML is the key distinction — Rego is powerful but has a steep learning curve
  7. Both support audit/background scanning — detect violations in existing resources
  8. Kyverno uniquely supports resource generation — auto-create NetworkPolicies, defaults
  9. ValidatingAdmissionPolicy (CEL) is Kubernetes-native, no webhook — but limited to validation
  10. Policy engines complement Trivy and Falco — Trivy finds issues, policy engines enforce rules, Falco detects runtime threats

Next: Service Mesh Security

Updated: