KCSA — Admission Controllers & Policy Engines

7 minute read

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:

  1. ConstraintTemplate — Defines the policy logic in Rego (the “how”)
  2. 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 failurePolicy on webhooks — Fail (deny if webhook unavailable) vs Ignore (allow if webhook unavailable)

Next: Image Assurance & Pipeline Security →

Updated: