Module 6: Runtime Security Admission Control And Image Scanning

1. Module introduction: the gatekeeper before `etcd`

Description

Modules 1-5 built a complete cluster: Pods, Deployments, Services, ConfigMaps, Secrets, probes, autoscaling, Ingress, NetworkPolicy, and pull-based GitOps with ArgoCD syncing andes-cargo-status-api from a real Git repository. At no point, however, was any object asked — before creating it — whether it was allowed to exist. Any well-formed Pod, with any image, with any amount of CPU or memory (or none), reached etcd with no one stopping it. This module closes that door: it installs two different policy engines that stand, literally, at the cluster's single entry point (kube-apiserver, Module 1, lesson 6) and decide, object by object, whether it's allowed through — before it exists.

This isn't a new topic for the Andes Cargo ecosystem. It's a topic another guide already named, precisely, and explicitly left for this one.

Connection to the module

cloud-security-and-guardrails-guide built eight complete modules of preventive and detective security over Andes Cargo's Terraform/AWS infrastructure — OIDC, secrets, conftest/Rego against a terraform plan, HCL scanning with Trivy and Checkov, SBOM and signing with cosign. In its Module 7, that guide explicitly named the pair of concepts this module builds (preventive/detective), and in its scope boundary it left this exact citation, unambiguously, pointing here.


The complete textual citation

cloud-security-and-guardrails-guide/DISENO.md, in the "Does NOT cover (boundary with sibling guides)" section, says, word for word:

"EKS and container runtime (in-cluster admission controllers — OPA Gatekeeper, Kyverno —, Docker image scanning, NetworkPolicy) → kubernetes-and-eks-in-production-guide. Andes Cargo has no containers in this ecosystem (the Lambda handler is packaged as .zip, not an image); this guide's scanning and signing are for deployment artifacts and IaC, not container images."

It's worth reading that citation twice, because it names three things it delegates, not one:

  1. In-cluster admission controllers — OPA Gatekeeper and Kyverno, named by their own names, not built there.
  2. Docker image scanning — different, the citation itself clarifies, from the scanning that guide does perform (trivy config over HCL, in its Module 5).
  3. NetworkPolicy — which, in fact, you already built, in Module 4 of this very guide, before getting here. It's named in the citation because, at the time that boundary was written, cloud-security-and-guardrails-guide didn't yet know in what order you'd read the ecosystem's guides — the citation is a coverage promise, not an announcement that it still needed building.

And there's one phrase, inside that same citation, that deserves its own paragraph: "Andes Cargo has no containers in this ecosystem". That was the exact picture up to the point when cloud-security-and-guardrails-guide was written — the Andes Cargo case, at its origin (aws-core-services-guide), is a Lambda handler packaged as .zip, with no Docker involved. You, however, started this module having already built five complete modules on a real Kubernetes cluster, with a real Docker image (andes-cargo-status-api:latest) running in real Pods. The premise "Andes Cargo has no containers" stopped being true the exact moment you finished Module 1 of this guide — this module is, among other things, the one that inherits that continuity correction and uses it as real, not hypothetical, working ground.


What cloud-security-and-guardrails-guide does NOT cover, and what this module does cover

The table below isn't a generic summary — it's the literal, point-by-point translation of the citation above into what you're going to build over the next seven lessons:

What cloud-security-and-guardrails-guide did buildThe exact artifactWhat this module builds in its place
Policy-as-code with conftest/OPA (Module 4)Rego evaluating a terraform plan in JSON, outside the cluster, as a static fileRego (Gatekeeper) and YAML (Kyverno) evaluating live objects, inside the cluster, at the instant of each kubectl apply
Infrastructure scanning with Trivy (Module 5)trivy config over .tf (HCL) files — looks for insecure configuration declared, never executedtrivy image over andes-cargo-status-api:latest — looks for real vulnerabilities, already installed, in the layers of an artifact that actually runs
Preventive vs. detective, named (Module 7)CloudTrail as the central example of "detective": a record is kept, after the factAdmission control as the central example of "preventive": it decides before the object exists — the same vocabulary, applied to a different layer
Supply chain, SBOM, and signing with cosign (Module 6)Verifies a deployment artifact (the Lambda .zip) wasn't tampered with, before apply.yml uploads itOutside this module's scope — signing and verifying a container image (cosign over OCI) would follow the same conceptual pattern, but isn't built here; it stays named as the natural next step if the ecosystem ever needed it

The row that truly matters to keep, before moving on: the same problem — "is this configuration safe before it runs?" — has a different solution depending on which layer the object you're evaluating lives on. A terraform plan is a JSON document that exists before AWS creates anything; conftest evaluates it as a file, with no live API touched. A Kubernetes Pod, on the other hand, is born the exact moment kube-apiserver receives it — there's no separate "plan" to evaluate beforehand, so the policy engine has to intercept the request itself, live, inside the cluster. That architectural difference — static file outside vs. live object inside — is the thread running through this module's eight lessons, and you're going to see it named explicitly in lesson 3.


Analogy: the gatekeeper who checks the list before letting you in

Picture an office building with two security systems. The first is a camera in the lobby: it records everything that happens, and if someone gets in without authorization, there's a recording that proves it — after the fact, once they're already inside. The second is a gatekeeper, standing at the building's one door, with a list in hand: they check that list before anyone crosses the threshold, and if the name isn't on it, that person never gets in — there's nothing to record, because the event the camera would have captured simply never happened.

An admission controller is that gatekeeper, applied to kube-apiserver. It isn't the camera that reviews things afterward (that, in the vocabulary cloud-security-and-guardrails-guide already used in its Module 7, is "detective" — CloudTrail, Kubernetes' audit logs, which this module doesn't build). It's the gatekeeper standing exactly at the cluster's one entry point — the same kube-apiserver Module 1's lesson 6 showed you as "the single entry door" — and deciding, object by object, before etcd stores it, whether it's allowed to exist. You're going to see the exact point in a request's lifecycle where this gatekeeper stands in lesson 2.


This module's map: the 8 lessons

   MODULE 6 — RUNTIME SECURITY: ADMISSION CONTROL AND IMAGE SCANNING

   M6.1  Introduction (this one)                       the citation, the boundary, the gatekeeper
   M6.2  What an admission controller is                the exact point in the lifecycle
   M6.3  OPA Gatekeeper: ConstraintTemplate/Constraint   Rego inside a K8s CRD
   M6.4  Hands-on: Gatekeeper + first policy             EXECUTED — v3.23.0, real rejection
   M6.5  Kyverno: the YAML-native alternative            Rego vs. YAML, no winner
   M6.6  Hands-on: the same policy, in Kyverno           EXECUTED — v1.18.2, real rejection
   M6.7  Hands-on: trivy image                           EXECUTED — real findings
   M6.8  Project: runtime guardrails                     EXECUTED — both engines + trivy
#LessonWhat it builds
1Introduction (this one)The complete textual citation, the exact boundary, the gatekeeper as the central analogy
2What an admission controller isAuthentication → authorization → admissionetcd; preventive, not detective
3OPA Gatekeeper: ConstraintTemplate/ConstraintRego inside a CRD; explicit contrast with conftest (outside the cluster)
4Hands-on: installing GatekeeperExecuted: v3.23.0, a resource-limits policy, a rejected Pod and an admitted Pod
5Kyverno: the YAML-native alternativeRego vs. declarative YAML; when a team picks each one, with no declared winner
6Hands-on: the same policy, in KyvernoExecuted: v1.18.2, same test, side-by-side comparison
7Hands-on: trivy imageExecuted: real findings from andes-cargo-status-api:latest, by severity
8Project: Andes Cargo's runtime guardrailsExecuted: both engines active at once + trivy image as a gate before loading an image

By the end of this module, andes-cargo-cluster is going to have, for the first time since Module 1, a real gatekeeper standing at kube-apiserver — and you're going to know, with literal evidence, exactly what message it shows when it rejects something.


Common mistakes

Thinking this module repeats cloud-security-and-guardrails-guide's Module 4 under a different name (apparent overlap). What happens: someone, seeing "policies" and "Rego" mentioned in both guides, assumes this module is a copy of the conftest module. How to spot it: if your mental summary is "I already saw this, it's the same thing with a different logo." How to fix it: the language (Rego) may match in Gatekeeper, but the moment and the object it evaluates are completely different — conftest reads a static JSON file, generated by terraform plan, on a machine with no connection to AWS at the instant of evaluation. Gatekeeper runs inside the cluster, as a real Deployment, and responds to every HTTP request kube-apiserver forwards to it, at the exact instant it happens. Lesson 3 of this module devotes an entire section to this distinction.

Assuming "Andes Cargo has no containers" is still true (outdated continuity). What happens: someone reads cloud-security-and-guardrails-guide's citation in this lesson and concludes the whole Andes Cargo ecosystem is still Docker-free. How to spot it: if your mental model of Andes Cargo, after reading this lesson, is still "four serverless services, one Lambda .zip, no containers at all." How to fix it: that premise was true when cloud-security-and-guardrails-guide was written; it stopped being true in this guide's Module 1, the moment you loaded andes-cargo-status-api:latest into andes-cargo-cluster for the first time. This guide's Module 8 (lesson 6) documents that continuity correction explicitly, so no student in the ecosystem is left with the old picture.

Expecting this module to sign images with cosign, because cloud-security-and-guardrails-guide did that with the Lambda .zip (parity expectation). What happens: someone, seeing the sibling guide sign its deployment artifact, expects this module to sign andes-cargo-status-api:latest the same way. How to spot it: if you look for a cosign sign/cosign verify step in lessons 4, 6, or 8. How to fix it: signing OCI images is, conceptually, the same idea applied to a different artifact — but it's outside this module's declared scope, which focuses on admission control and vulnerability scanning. This lesson names it in the table above, explicitly, as the natural next step this module does not build — not out of oversight, but because the citation itself already states this module's scope: "in-cluster admission controllers... Docker image scanning," not "image signing."


Exercises

Exercise 1 — Rewrite the citation from memory, without copying it. Without looking back at this lesson's "The complete textual citation" section, write in your own words the three exact things cloud-security-and-guardrails-guide delegates to this guide.

See solution

The three things are: (1) admission controllers running inside the cluster — explicitly named as OPA Gatekeeper and Kyverno; (2) Docker image scanning — different from the HCL/Terraform scanning that guide did perform; and (3) NetworkPolicy — which, in the real order you read the ecosystem, you already built in Module 4 of this very guide before getting here. If your answer named all three without needing to reread the citation, you have a clear grasp of the exact boundary governing this module.

Exercise 2 — Explain why "Andes Cargo has no containers" stopped being true, with an exact date. In one sentence, name the exact point — the module and the lesson — where that premise became false, and why.

See solution

It became false in Module 1, lesson 7 of this guide (kubernetes-and-eks-in-production-guide), the moment andes-cargo-status-api:latest first got loaded into andes-cargo-cluster with kind load docker-image — from that instant on, Andes Cargo genuinely has a service running as a container inside a real cluster, not just a Lambda handler packaged as .zip.

Exercise 3 — Predict the difference between conftest and Gatekeeper before reading lesson 3. Based solely on this lesson's table, predict: if you showed the same Pod YAML to both conftest (with a copied Rego rule) and Gatekeeper, which of the two can reject it before that Pod actually exists on a cluster, and which can't?

See solution

Neither one "wins" in that sense — both are preventive, each in its own context. conftest can evaluate that YAML as a static file, on any machine, with no cluster running at all — perfect for a CI step before anyone tries to apply anything. Gatekeeper, on the other hand, needs a real cluster running, with the webhook installed, to intercept the request at the exact moment of kubectl apply — it can't evaluate a file unless someone actually applies it against kube-apiserver. The difference isn't "who is more preventive": it's "at what instant in infrastructure's lifecycle each one acts" — conftest, before any intent to apply anything exists; Gatekeeper, at the exact instant someone tries.


Summary and next step

This lesson connected this module, with the complete textual citation, to the boundary cloud-security-and-guardrails-guide left explicit in its own design: in-cluster admission controllers (OPA Gatekeeper, Kyverno) and Docker image scanning, delegated here because that guide works on a Lambda .zip, not a container image. You saw the complete table of what each guide builds for the same general problem — "is this safe before it runs?" — on different layers, and this module's central analogy: the gatekeeper who checks the list before letting you in, not the camera that records afterward.

Before moving on you should be able to: cite from memory the three things the boundary delegates; explain why "Andes Cargo has no containers" stopped being true, with the exact module and lesson; and distinguish, in one sentence, the architectural difference between conftest (static file, outside the cluster) and an admission controller (live object, inside the cluster).

Next lesson: what an admission controller is, and why it runs before the object exists. There you're going to see the exact point in a request's lifecycle — authentication, authorization, admission, etcd — and why this is, with the same vocabulary cloud-security-and-guardrails-guide already used in its Module 7, strictly preventive.

Resources

  1. cloud-security-and-guardrails-guide (NIEVA), DISENO.md, "Does NOT cover" section — the complete textual source for this lesson's citation.
  2. cloud-security-and-guardrails-guide (NIEVA), Module 7, lesson 1 — the preventive/detective distinction this module picks back up in lesson 2.
  3. kubernetes-and-eks-in-production-guide (NIEVA), Module 1, lesson 6 — kube-apiserver as the cluster's single entry door, the technical foundation for this entire module.
  4. Kubernetes — Admission Controllers Reference — official documentation for the mechanism this module builds with two real engines.
  5. Open Policy Agent Gatekeeper — the first of the two engines, actually installed in lesson 4.
  6. Kyverno — the second engine, actually installed in lesson 6.
  7. Trivy — Container Image — the image scanner, actually run in lesson 7.