# Supply Chain Security: Policy Enforcement and Admission Controllers

> Chapter 6 of the Courseiva CNCF-CKS curriculum — https://courseiva.com/learn/cncf-cks/supply-chain-policy-enforcement

**Official objective:** 2.2 — Implement admission controllers and policy engines (e.g., OPA/Gatekeeper, Kyverno)

## Introduction

Supply chain security is the practice of making sure every piece of software and configuration that enters your Kubernetes cluster is safe and follows your organisation's rules. Policy enforcement and admission controllers are the automated bouncers that check each request before it is allowed in, preventing dangerous or non-compliant code from ever running. For the CKS exam, you must understand how to set up and use these tools to protect the cluster from the moment a developer submits a change until it is running in production.

## The Nightclub Bouncer Analogy

Before anyone can enter a high-end nightclub, a bouncer checks every person against a strict list of rules. First, the club owner publishes policies: no one under 21, no glass bottles, no gang colours, and everyone must show valid ID. This policy set is the equivalent of a Kubernetes admission control policy. The bouncer stands at the door, and as each guest arrives, they must stop and submit to inspection. The bouncer does not check the guest's entire life story — only the things on the list. If a guest tries to sneak in a flask of whiskey, the bouncer spots it and turns them away before they ever set foot on the dance floor. This is exactly how an admission controller works: it intercepts every request to create or modify a resource in a Kubernetes cluster, checks it against the rules you have defined, and either allows it or rejects it on the spot. The bouncer never lets a banned person inside to cause trouble later. In the same way, policy engines like OPA/Gatekeeper or Kyverno act as that security guard, ensuring only compliant containers, images, and configurations ever enter the cluster. The party inside stays safe because the door is locked tight against anything that breaks the rules.

## Core explanation

Kubernetes is a platform that runs containerised applications. A container is a lightweight, standalone package that includes everything needed to run a piece of software: the code, runtime, system tools, libraries, and settings. When a developer or a CI/CD pipeline sends a request to create a Pod (the smallest deployable unit in Kubernetes), that request goes to the Kubernetes API server. The API server is the front door of the cluster — every operation goes through it.

An admission controller is a piece of code that intercepts requests to the API server after authentication and authorisation have happened, but before the object is persisted (saved to the cluster's database, called etcd). This is the perfect place to enforce security policies because you can inspect and modify the request before it becomes permanent. There are two types of admission controllers: mutating and validating. A mutating admission controller can change the request — for example, adding a default security context that prevents the container from running as root. A validating admission controller can only accept or reject the request — it cannot modify it. Both types can be chained together, so a request goes through mutating controllers first, then validating controllers.

Policy engines are tools that let you define rules in a high-level language and then plug them into the admission controller framework. The two most important ones for the CKS exam are Open Policy Agent (OPA) with Gatekeeper, and Kyverno. OPA is a general-purpose policy engine that uses a language called Rego to write rules. Gatekeeper is a Kubernetes-specific implementation of OPA that works as an admission controller. You write policies in Rego, and Gatekeeper enforces them on every API request. Kyverno is a Kubernetes-native policy engine that uses YAML (the same format as Kubernetes resources) to define policies, making it easier for Kubernetes administrators to learn and write.

Why does this matter for supply chain security? Because a modern software supply chain is complex. A developer might pull a container image from a public registry like Docker Hub, use it in their application, and then deploy it to the cluster. That image could contain vulnerabilities, be configured to run as root, or include a backdoor. Supply chain attacks exploit the chain of dependencies — libraries, base images, configuration files — that make up a deployed application. By using admission controllers with policy engines, you can enforce rules such as:

- Only allow container images from approved registries (e.g., your internal registry, not random public ones)
- Require that images have been signed (using tools like cosign) and the signature is verified
- Prevent containers from running with root privileges or from mounting sensitive host paths
- Ensure that every Pod has resource limits (CPU and memory) to prevent runaway resource consumption
- Mandate that all images have a specific label or annotation for auditing

Before admission controllers, these checks had to be done manually by reviewers or enforced at the CI/CD pipeline level. The problem was that if a developer bypassed the pipeline (for example, by running kubectl apply directly), the cluster would accept the dangerous configuration. Admission controllers close that gap because they run inside the cluster and cannot be bypassed — every request goes through them.

In practice, you install a policy engine like Gatekeeper or Kyverno into your cluster as a set of Pods and Custom Resource Definitions (CRDs). CRDs are extensions to the Kubernetes API that let you define your own resource types. You then write policy definitions as CRDs. When a request arrives, the policy engine checks the request against all active policies. If any policy is violated, the request is rejected with a clear error message telling the developer what rule they broke. This is immediate feedback, so developers can fix their manifests before those manifests ever become live objects in the cluster.

The CKS exam will test your ability to understand these concepts, interpret policy definitions, and identify the correct steps to enforce supply chain security policies. You do not need to become an expert in writing Rego policies, but you must know what the tools do, how they fit together, and what best practices look like.

## Real-world context

Let us walk through a typical day for an IT professional who manages Kubernetes security at a medium-sized company. This person, call them Sam, is responsible for ensuring that every container running in the company's cluster is safe and follows internal policies.

Sam starts the morning by checking the latest security advisories. They learn about a critical vulnerability in a popular Node.js base image that attackers are actively exploiting. The vulnerability allows remote code execution if the container runs as root. Sam knows that some development teams have been using this base image for their microservices. They need to act fast to prevent any new deployments from using the vulnerable image.

The company already has Kyverno installed as a policy engine. Sam logs into the cluster using kubectl, the command-line tool for interacting with Kubernetes. They navigate to the policy definitions stored in a Git repository, which acts as the single source of truth. Sam updates an existing policy that restricts which registries images can be pulled from. They add a specific rule: any image from the vulnerable registry (or a specific tag) must be rejected unless it has been signed by the security team and the signature is verified. The policy also mutates any new Pod that tries to run as root by adding a security context that forces it to run as a non-root user.

Sam applies the updated policy by running kubectl apply -f policy.yaml. The Kyverno controller picks up the change and now enforces the new rule on every subsequent request. A few minutes later, a developer in the payments team submits a new deployment manifest that uses the vulnerable base image and does not specify a security context. The developer runs kubectl apply and immediately sees an error message: "Policy 'block-vulnerable-images' rejected the request. Image 'docker.io/library/node:latest' is from a restricted registry and runs as root. Use approved image from registry.internal.company.com/node:latest-signed." The developer is confused because the pipeline did not catch this earlier, but Sam has trained the teams to understand that admission controllers are the final safety net.

The developer updates their Dockerfile to use the approved image, builds a new container, signs it with cosign (a tool for signing container images), and submits again. This time the request passes Kyverno's checks, and the Pod is created successfully. Sam then reviews the audit logs to confirm that the blocked requests were legitimate attempts and not an attacker probing the cluster. They also set up alerts for any policy violation so they can respond quickly if a pattern of blocked requests indicates malicious activity.

Later that week, Sam needs to audit all running Pods to ensure none have slipped through before the policy was updated. They use a tool like kubectl audit or kube-bench to check the cluster's existing state, because admission controllers only intercept new requests — they do not retroactively apply to running objects. Sam finds two old Pods that are still using the vulnerable image and schedules them for replacement. They also add a cron job that periodically scans the cluster for policy violations and reports them to the security dashboard.

This scenario shows that the IT professional's job is not just to install a policy engine. It involves:

- Writing and maintaining policies that balance security and developer productivity
- Testing policies in a non-production cluster before rolling them out to production
- Educating developers about why a request was blocked and how to fix it
- Auditing existing resources to ensure compliance
- Monitoring policy violations for signs of attack

All of these tasks are core to the role of a Kubernetes security specialist, and the CKS exam will test your understanding of them.

## Exam focus

The CKS exam is a hands-on, performance-based test. You will not be asked multiple-choice questions about admission controllers — you will have to actually configure them in a live cluster. The exam presents you with a scenario, a cluster, and a set of tasks. Your job is to complete those tasks correctly and efficiently. The exam environment uses real terminal windows, and you type real commands.

For the objective 'Implement admission controllers and policy engines (e.g., OPA/Gatekeeper, Kyverno)', the exam expects you to know the following specific things:

- The difference between mutating and validating admission controllers, and in what order they execute (mutating first, then validating). This is a common trap: a mutating policy might fix a problem, and then the validating policy passes. If you apply them in the wrong order, the validating policy might reject a request that could have been fixed.
- How to write a basic Kyverno policy in YAML. You need to understand the structure: apiVersion, kind (ClusterPolicy or Policy), metadata, spec with rules, match, validate or mutate patterns. A common exam task is to create a ClusterPolicy that rejects any Pod running as root.
- How to use OPA Gatekeeper with Constraint Templates and Constraints. A Constraint Template defines the logic (in Rego) for a type of policy, and a Constraint applies that logic to specific resources. You need to know how to install Gatekeeper, create a ConstraintTemplate, and then create a Constraint that enforces a rule like 'all images must come from a specific registry'.
- The concept of 'dry run' policies. Before enforcing a policy, you can run it in dry-run mode to see what it would affect without actually blocking anything. The exam might ask you to test a policy in dry-run mode first, then switch it to enforcement mode.
- How to verify that a policy is working. This means submitting a compliant request and a non-compliant request, and checking that the non-compliant one is rejected. You might be asked to capture the error message in a file as evidence.

Common traps the exam sets:

- They give you a policy with a syntax error. You must identify the error and fix it. For example, a missing field in the match block or a Rego rule that references a non-existent variable.
- They ask you to enforce a policy but the policy engine is not installed. You need to install it first (often via a Helm chart or a kubectl apply command on the release manifest).
- They use obscure policy parameters. For example, requiring that a Pod has a specific label or annotation, or that the container image includes a digest (the hash) rather than just a tag. The exam expects you to know how to check for these in the policy definition.
- They present a scenario where you need to use a dynamic admission controller (like Kyverno) to mutate a request. For example, adding a default seccomp profile to every Pod that does not already specify one.

Key definitions to memorise:

- Admission controller: intercepts API requests after auth, before persistence.
- Mutating admission controller: modifies the request.
- Validating admission controller: rejects or allows the request without modification.
- OPA: Open Policy Agent, a general-purpose policy engine using Rego.
- Gatekeeper: Kubernetes-specific implementation of OPA as an admission controller.
- Kyverno: Kubernetes-native policy engine using YAML.
- ConstraintTemplate: OPA Gatekeeper resource defining the policy logic and schema.
- Constraint: OPA Gatekeeper resource applying the template to specific resources.
- ClusterPolicy: Kyverno resource that applies to the entire cluster.
- Policy (namespaced): Kyverno resource scoped to a single namespace.

You should also be comfortable with kubectl commands: kubectl apply, kubectl get constrainttemplates, kubectl get constraints, kubectl describe clusterpolicy, and kubectl logs to check the policy engine's Pod logs for errors.

## Step by step

1. **Identify Policy Requirements** — Review the security requirements for the cluster. For supply chain security, typical requirements include: only images from approved registries, images must be signed, containers must not run as root, and every Pod must have resource limits. Document these rules before writing any code.
2. **Install the Policy Engine** — Deploy the chosen policy engine (Kyverno or OPA/Gatekeeper) into the cluster. This is typically done using Helm charts or kubectl apply on the release manifest. Verify the installation by checking that the policy engine's Pods are running in the designated namespace (often gatekeeper-system or kyverno).
3. **Write or Obtain Policy Definitions** — Create the policy resource. For Kyverno, this is a ClusterPolicy YAML file. For Gatekeeper, you create a ConstraintTemplate and then a Constraint. The policy specifies what to match (e.g., all Pods) and what to validate (e.g., the container image must start with 'registry.internal.company.com/'). Use a text editor or the kubectl command line.
4. **Test the Policy in Dry-Run Mode** — Apply the policy with dry-run enabled so it logs violations without blocking requests. This lets you see if the policy is too restrictive or has mistakes. Submit a compliant request and a non-compliant request, then check the policy engine's logs to confirm the correct behaviour.
5. **Enable Enforcement Mode** — Once testing confirms the policy works as intended, switch it from dry-run to enforcement mode (often just removing the dry-run annotation or setting a field to true). Now the policy will actively block non-compliant requests.
6. **Audit Existing Resources** — Run a manual or automated audit of all existing Pods and other relevant resources to identify any that violate the new policy. Use kubectl get pods --all-namespaces and check each one against the policy rules. Schedule replacements for non-compliant resources.
7. **Monitor and Update Policies** — Continuously monitor policy violations via logs and metrics. Update policies as new vulnerabilities are discovered or as business requirements change. Keep policies in a version-controlled repository and use a CI/CD pipeline to deploy them.

## Comparisons

### Kyverno vs OPA Gatekeeper

**Kyverno:**
- Policies written in YAML, same format as Kubernetes resources
- Kubernetes-native, developed by Nirmata
- Supports both mutating and validating policies natively

**OPA Gatekeeper:**
- Policies written in Rego, a custom declarative language
- General-purpose policy engine that works beyond Kubernetes
- Requires separate ConstraintTemplate and Constraint resources

### Mutating Admission Controller vs Validating Admission Controller

**Mutating Admission Controller:**
- Can modify the request before persistence
- Runs before validating controllers
- Example: adding a default security context to a Pod

**Validating Admission Controller:**
- Can only accept or reject the request
- Runs after mutating controllers
- Example: checking that an image comes from an approved registry

### Policy Engine (Kyverno/Gatekeeper) vs Network Policy

**Policy Engine (Kyverno/Gatekeeper):**
- Operates at the Kubernetes API level
- Controls what resources can be created or modified
- Used for supply chain security and configuration compliance

**Network Policy:**
- Operates at the network layer (L3/L4)
- Controls traffic flow between Pods
- Used for micro-segmentation and network security

### Admission Controller vs CI/CD Pipeline Security Scan

**Admission Controller:**
- Runs inside the cluster, intercepts every API request
- Cannot be bypassed if a developer runs kubectl direct
- Catches issues at deployment time

**CI/CD Pipeline Security Scan:**
- Runs outside the cluster, in the build/CI pipeline
- Can be bypassed if a developer runs commands outside the pipeline
- Catches issues at build time

## Diagram

_The flow of a Kubernetes API request through authentication, mutating admission controllers, validating admission controllers, and either persistence or rejection._

```mermaid
flowchart TD
    A[Developer or CI/CD submits request] --> B[Kubernetes API Server]
    B --> C[Authentication and Authorisation]
    C --> D[Mutating Admission Controllers (e.g., Kyverno, Gatekeeper)]
    D --> E[Validate Admission Controllers (e.g., Kyverno, Gatekeeper)]
    E --> F{Policy Check Passes?}
    F -- Yes --> G[Request persisted to etcd]
    F -- No --> H[Request rejected with error message]
    D --> I[Mutation: adds defaults like non-root user]
    E --> J[Validation: checks image registry, signing, etc.]
```

## Common misconceptions

- **Misconception:** Admission controllers are the same as network policies or firewall rules. **Reality:** Admission controllers inspect API requests before resources are created or updated, while network policies control traffic between Pods. They operate at completely different layers of the stack. (The word 'policy' appears in both, so beginners assume they do the same thing. One works at the API level, the other at the network level.)
- **Misconception:** Once you set an admission controller policy, it automatically applies to all existing resources in the cluster. **Reality:** Admission controllers only intercept new or modified requests. Existing resources that were created before the policy was enabled are not affected and must be audited separately. (People expect policies to be retroactive because that is how many other security tools (like antivirus) work. Kubernetes does not re-evaluate existing objects when a new policy is added.)
- **Misconception:** You need to write Rego code for all policy engines, including Kyverno. **Reality:** Rego is specific to OPA/Gatekeeper. Kyverno uses YAML that mirrors Kubernetes resource definitions, making it easier to learn and maintain. (Because OPA is more famous and has been around longer, beginners assume all policy engines use the same language. Kyverno's YAML approach is a major selling point but is less known.)
- **Misconception:** A mutating admission controller is dangerous because it can change any part of the request, so it should be avoided. **Reality:** Mutating controllers are extremely useful for adding default security settings (like non-root user, resource limits, labels) and are safe when properly configured. They are a best practice for ensuring baseline security without relying on developers. (The word 'mutate' sounds like something harmful or out of control, but in Kubernetes it is simply a controlled modification. Beginners fear it without understanding its value.)
- **Misconception:** The CKS exam expects you to memorise all Rego syntax and write complex policies from scratch. **Reality:** The exam tests your ability to understand and modify existing policies, not write complex ones from scratch. You need to know the structure and common keywords, but you will not be asked to write a full Rego library. (Many official CKS study guides go deep into Rego, which makes beginners panic. The actual exam is more about practical application of pre-written templates.)

## Key takeaways

- Admission controllers intercept all API requests to the Kubernetes API server after authentication and authorisation, and before the object is persisted to etcd.
- Mutating admission controllers can modify a request (e.g., add default security settings), while validating admission controllers can only accept or reject a request.
- Kyverno uses YAML policy definitions that look like Kubernetes resources, making it more accessible than OPA/Gatekeeper which requires Rego language.
- OPA Gatekeeper uses ConstraintTemplates (defining the policy logic) and Constraints (applying the logic to specific resources).
- Existing resources created before a policy is enabled are not automatically re-evaluated; you must audit them separately.
- For supply chain security, admission controllers can enforce rules like 'only allow signed images from approved registries' and 'prevent containers from running as root'.
- The CKS exam tests practical skills: you install the policy engine, write or modify a policy, test it with dry-run mode, and enforce it on the cluster.
- A common exam trap is a policy with syntax errors; you must be able to read logs and correct the YAML or Rego code.
- Policy engines themselves run as Pods inside the Kubernetes cluster and can be deployed using Helm charts or kubectl commands.
- Admission controllers do not replace CI/CD pipeline security; they are a complementary final layer that prevents bypasses.

## FAQ

**What is the difference between Kyverno and OPA Gatekeeper?**

Kyverno uses YAML policies that look like Kubernetes resources, which is easier for beginners. OPA Gatekeeper uses Rego, a more powerful but steeper language. Both do the same job: enforce policies as admission controllers.

**Do admission controllers work retroactively on existing resources?**

No, admission controllers only intercept new or updated API requests. Existing resources that were created before the policy was enabled are not affected and must be audited and updated separately.

**Can I run both Kyverno and Gatekeeper in the same cluster?**

Yes, you can run multiple admission controllers in the same cluster. They are independent webhooks, so they will each evaluate every request. This is useful for layered security but adds complexity.

**How do I see which admission controllers are installed in my cluster?**

Use the command kubectl get mutatingwebhookconfigurations and kubectl get validatingwebhookconfigurations. This lists all the admission controllers registered with the API server.

**Why would I use a mutating admission controller instead of making developers set the fields themselves?**

A mutating admission controller ensures that security settings are applied automatically and consistently, even if a developer forgets or makes a mistake. It prevents human error and enforces organisational policies without requiring developer compliance.

**What does 'supply chain security' mean in the context of Kubernetes?**

Supply chain security means ensuring that every component that goes into your deployed application — container images, libraries, configuration files — is trusted, verified, and free from vulnerabilities. Admission controllers enforce this by blocking untrusted or non-compliant resources.

## Check your understanding

1. **What is the order of execution for mutating and validating admission controllers for a single API request?**

   Answer: Mutating admission controllers execute first, then validating admission controllers execute afterwards.

2. **What Kyverno resource type applies policies to all namespaces in the cluster?**

   Answer: A ClusterPolicy applies to all namespaces, while a Policy is scoped to a single namespace.

3. **In OPA Gatekeeper, what is the difference between a ConstraintTemplate and a Constraint?**

   Answer: A ConstraintTemplate defines the policy logic and the schema for parameters, while a Constraint applies that template to specific resources with specific parameter values.

4. **Can an admission controller enforce policies on existing resources that were created before the policy was installed?**

   Answer: No, admission controllers only intercept new or modified requests. Existing resources must be audited and updated separately.

5. **You need to ensure every new Pod has a default seccomp profile added if none is specified. Should you use a mutating or validating admission controller?**

   Answer: You should use a mutating admission controller, because it can modify the request to add the missing seccomp profile before the Pod is created.

---

Interactive version with quiz and diagrams: https://courseiva.com/learn/cncf-cks/supply-chain-policy-enforcement
