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