KCSA — Admission Controllers & Policy Engines
The Admission Controller Pipeline
When a request reaches the Kubernetes API server, it passes through a multi-stage pipeline before the object is persisted to etcd. Admission controllers sit in the final stages of this pipeline.
Client Request
│
▼
┌─────────────────┐
│ Authentication │ ← Who are you? (certificates, tokens, OIDC)
└────────┬────────┘
│
▼
┌─────────────────┐
│ Authorization │ ← Are you allowed? (RBAC, ABAC, Webhook)
└────────┬────────┘
│
▼
┌─────────────────┐
│ Mutating │ ← Modify the request (inject sidecars, set defaults)
│ Admission │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Schema │ ← Validate object schema
│ Validation │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Validating │ ← Accept or reject (enforce policies)
│ Admission │
└────────┬────────┘
│
▼
┌─────────────────┐
│ Persist to etcd │
└─────────────────┘
Key point: Mutating admission runs before validating admission. This means a mutating webhook can modify a resource, and then a validating webhook checks the final result.
Built-in vs Webhook-based Admission Controllers
Kubernetes ships with many built-in admission controllers. These are compiled into the API server binary and enabled via the --enable-admission-plugins flag.
Common Built-in Admission Controllers
| Admission Controller | Type | What It Does |
|---|---|---|
| NamespaceLifecycle | Validating | Rejects requests in namespaces that are being deleted |
| LimitRanger | Mutating + Validating | Applies default resource limits from LimitRange objects |
| ServiceAccount | Mutating | Auto-mounts ServiceAccount tokens into pods |
| DefaultStorageClass | Mutating | Assigns default storage class to PVCs without one |
| ResourceQuota | Validating | Enforces resource quotas per namespace |
| PodSecurity | Validating | Enforces Pod Security Standards (PSA) |
| NodeRestriction | Validating | Limits what kubelets can modify (only their own node + pods) |
| AlwaysPullImages | Mutating | Forces imagePullPolicy: Always on all containers |
Webhook-based Admission Controllers
For custom policies beyond what built-in controllers provide, Kubernetes supports webhook-based admission controllers:
- MutatingAdmissionWebhook — Calls an external HTTP endpoint that can modify the request
- ValidatingAdmissionWebhook — Calls an external HTTP endpoint that accepts or rejects the request
Webhook-based controllers enable tools like OPA/Gatekeeper and Kyverno to enforce custom policies.
Webhook admission controllers add latency to every API request they intercept. A misconfigured webhook can block all API operations — always configure failurePolicy: Ignore or a timeout for non-critical webhooks.
OPA/Gatekeeper
Open Policy Agent (OPA) is a general-purpose policy engine. Gatekeeper is the Kubernetes-native integration of OPA — it runs as a validating admission webhook and evaluates policies written in OPA’s Rego language.
Gatekeeper Architecture
API Request → API Server → Gatekeeper Webhook → Evaluate Constraints → Allow/Deny
│
┌────────┴────────┐
│ Constraint │ ← What to enforce
│ Templates │ ← How to enforce (Rego)
└─────────────────┘
Constraint Templates and Constraints
Gatekeeper uses a two-layer model:
- ConstraintTemplate — Defines the policy logic in Rego (the “how”)
- Constraint — Applies the template with specific parameters (the “what” and “where”)
Example: Require All Pods to Have Labels
ConstraintTemplate:
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 := {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])
}
Constraint:
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
name: pods-must-have-owner
spec:
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
parameters:
labels:
- "app.kubernetes.io/name"
- "app.kubernetes.io/owner"
Common Gatekeeper Policies
| Policy | Description |
|---|---|
| Require labels | All resources must have specified labels |
| Block privileged containers | Deny pods with privileged: true |
| Require resource limits | All containers must specify CPU/memory limits |
| Restrict image registries | Only allow images from approved registries |
| Block hostPath volumes | Prevent mounting host filesystem paths |
| Require read-only root filesystem | All containers must set readOnlyRootFilesystem: true |
You do not need to write Rego for the KCSA exam. Know that Gatekeeper uses Rego, understand the ConstraintTemplate + Constraint model, and recognize common policy examples.
Gatekeeper Audit Mode
Gatekeeper can audit existing resources — not just incoming requests. This helps you identify violations in resources that were created before a policy was deployed.
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
name: pods-must-have-owner
spec:
enforcementAction: dryrun # Audit mode — log violations, don't block
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
parameters:
labels:
- "app.kubernetes.io/owner"
Kyverno
Kyverno is a Kubernetes-native policy engine where policies are written in YAML — no new language to learn. It runs as both a mutating and validating admission webhook.
Kyverno Architecture
API Request → API Server → Kyverno Webhook → Evaluate Policies → Allow/Deny/Mutate
│
┌────────┴────────┐
│ ClusterPolicy │ ← Cluster-wide
│ Policy │ ← Namespace-scoped
└─────────────────┘
Kyverno Policy Types
| Type | Description | Example |
|---|---|---|
| Validate | Accept or reject a resource based on conditions | Require labels, block privileged pods |
| Mutate | Modify a resource before it is created | Add default resource limits, inject labels |
| Generate | Automatically create companion resources | Create NetworkPolicy when a new namespace is created |
| VerifyImages | Validate image signatures and attestations | Only allow cosign-signed images |
Example: Require Labels (Kyverno)
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-labels
spec:
validationFailureAction: Enforce # or Audit
rules:
- name: check-for-labels
match:
any:
- resources:
kinds:
- Pod
validate:
message: "The label 'app.kubernetes.io/name' is required."
pattern:
metadata:
labels:
app.kubernetes.io/name: "?*"
Example: Auto-Generate NetworkPolicy
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: generate-default-networkpolicy
spec:
rules:
- name: default-deny
match:
any:
- resources:
kinds:
- Namespace
generate:
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
name: default-deny-all
namespace: ""
data:
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
Kyverno’s generate capability is unique among policy engines — it can create resources automatically when a trigger condition is met.
OPA/Gatekeeper vs Kyverno
This comparison is a high-probability exam topic. Know the differences.
| Feature | OPA/Gatekeeper | Kyverno |
|---|---|---|
| Policy language | Rego (domain-specific language) | YAML (Kubernetes-native) |
| Learning curve | Steep (Rego requires dedicated learning) | Low (YAML is familiar to K8s users) |
| Mutation support | Limited (added in later versions) | Native (first-class feature) |
| Resource generation | No | Yes (generate policies) |
| Image verification | External integration | Built-in (verifyImages rule type) |
| Audit mode | Yes (audit existing resources) | Yes (Audit enforcement action) |
| Scope | Cluster-wide (ConstraintTemplates are global) | Cluster-wide (ClusterPolicy) and namespace (Policy) |
| General-purpose | Yes (OPA works beyond Kubernetes) | No (Kubernetes-specific) |
| Community | CNCF Graduated | CNCF Incubating |
| Policy testing | opa test CLI |
kyverno test CLI |
When to Use Which
| Scenario | Recommended Engine |
|---|---|
| Team already knows Rego or uses OPA elsewhere | OPA/Gatekeeper |
| Need simple, readable YAML policies | Kyverno |
| Need to auto-generate resources (e.g., NetworkPolicy per namespace) | Kyverno |
| Need image signature verification built into the policy engine | Kyverno |
| Need policies across non-Kubernetes systems (Terraform, Envoy, etc.) | OPA |
| Enterprise with existing OPA investment | OPA/Gatekeeper |
ValidatingAdmissionPolicy (Kubernetes-Native)
Starting in Kubernetes 1.26 (beta in 1.28, GA in 1.30), Kubernetes introduced ValidatingAdmissionPolicy — a built-in admission controller that uses CEL (Common Expression Language) to define policies without external webhooks.
Why It Matters
- No external webhook — policy evaluation happens inside the API server
- No additional deployment — no Gatekeeper or Kyverno pods to manage
- Lower latency — no network call to a webhook
- CEL is simple and Kubernetes-native
Example: Block Privileged Containers
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
name: deny-privileged
spec:
failurePolicy: Fail
matchConstraints:
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["pods"]
validations:
- expression: "!object.spec.containers.exists(c, c.securityContext.privileged == true)"
message: "Privileged containers are not allowed."
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: deny-privileged-binding
spec:
policyName: deny-privileged
validationActions:
- Deny
matchResources:
namespaceSelector:
matchLabels:
environment: production
ValidatingAdmissionPolicy is the future direction for in-tree admission control. It is simpler than OPA/Gatekeeper or Kyverno for basic validations, but lacks mutation and generation capabilities.
Policy Engine Decision Table
Use this table to decide which tool fits a given scenario:
| Requirement | ValidatingAdmissionPolicy | OPA/Gatekeeper | Kyverno |
|---|---|---|---|
| Simple validation rules | ✅ Best fit | ✅ Overkill | ✅ Works |
| Complex policy logic | ❌ CEL limitations | ✅ Rego is powerful | ⚠️ YAML can get verbose |
| Mutation | ❌ Not supported | ⚠️ Limited | ✅ Native |
| Resource generation | ❌ Not supported | ❌ Not supported | ✅ Native |
| Image verification | ❌ Not supported | ❌ External | ✅ Built-in |
| No external dependencies | ✅ Built-in | ❌ Deploy Gatekeeper | ❌ Deploy Kyverno |
| Audit existing resources | ✅ (via audit annotations) | ✅ Native | ✅ Native |
| Multi-system policies | ❌ K8s only | ✅ OPA works everywhere | ❌ K8s only |
Key Takeaways for the Exam
| Concept | What to Remember |
|---|---|
| Admission pipeline | Authentication → Authorization → Mutating Admission → Schema Validation → Validating Admission → etcd |
| Mutating vs Validating | Mutating runs first (modifies resources), Validating runs second (accepts/rejects) |
| Built-in controllers | PodSecurity, NodeRestriction, LimitRanger, ResourceQuota, AlwaysPullImages |
| OPA/Gatekeeper | Rego language, ConstraintTemplate + Constraint model, CNCF Graduated |
| Kyverno | YAML policies, validate + mutate + generate, image verification built-in |
| ValidatingAdmissionPolicy | Kubernetes-native, CEL-based, no external webhooks, GA in K8s 1.30 |
| Webhook risks | Latency, availability (misconfigured webhook can block all API calls) |
- The admission controller pipeline order is a common exam question
- Know the Gatekeeper vs Kyverno trade-offs — language, mutation, generation
- ValidatingAdmissionPolicy is the Kubernetes-native alternative — simpler but less powerful
- Always consider
failurePolicyon webhooks —Fail(deny if webhook unavailable) vsIgnore(allow if webhook unavailable)