Lab A — Offline admission
Before this: Install the toolchain pinned gator and confirmed Lab prerequisites.
This lab proves the spine claim it owes: policy passes before any cluster exists. Everything below runs on your laptop with no cluster credentials and no live webhook. Work in a clone of jajera/kiro-eks-argocd-migration.
The chain: one policy, four artifacts
Section titled “The chain: one policy, four artifacts”Every Gatekeeper
OPA Gatekeeper — Kubernetes admission controller that evaluates ConstraintTemplates against cluster objects. rule in the factory is proven by the same four-step chain before it reaches a cluster. Walking httpsonly end to end shows what each artifact contributes:
- ConstraintTemplate
Gatekeeper CRD that defines a Rego policy and the shape of parameters a Constraint can set. — the Rego
OPA policy language — Gatekeeper ConstraintTemplates embed Rego `violation` rules that gator and the webhook both evaluate. rule itself. - Constraint
Gatekeeper object that binds a ConstraintTemplate to a match scope (kinds, namespaces, labels). — binds the rule to a match scope. - Test suite
Gatekeeper test Suite — wires a template, a constraint, and named pass/fail object cases for `gator verify`. — named cases with expected pass/fail outcomes. gator verify— runs the suite offline and reportsPASSor a violation message.
1. ConstraintTemplate — the rule
Section titled “1. ConstraintTemplate — the rule”Source: infrastructure/gatekeeper/constraint-templates/httpsonly.yaml
apiVersion: templates.gatekeeper.sh/v1kind: ConstraintTemplatemetadata: name: k8shttpsonly# ... targets: - target: admission.k8s.gatekeeper.sh rego: | package k8shttpsonly
violation[{"msg": msg}] { input.review.object.kind == "Ingress" regex.match("^(extensions|networking.k8s.io)/", input.review.object.apiVersion) ingress := input.review.object not https_complete(ingress) not tls_is_optional msg := sprintf("Ingress should be https. tls configuration and allow-http=false annotation are required for %v", [ingress.metadata.name]) }# ... https_complete(ingress) = true { ingress.spec["tls"] count(ingress.spec.tls) > 0 ingress.metadata.annotations["kubernetes.io/ingress.allow-http"] == "false" }The Rego violation rule is the actual check: an Ingress
Kubernetes API object that exposes HTTP/HTTPS routes into the cluster — often an ALB Ingress on EKS. fails when it lacks both a tls block and the kubernetes.io/ingress.allow-http: "false" annotation. This template defines the rule but binds to nothing yet — it is reusable across any number of constraints.
2. Constraint — the binding
Section titled “2. Constraint — the binding”Source: infrastructure/gatekeeper/constraints/httpsonly/constraint.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1kind: K8sHttpsOnlymetadata: labels: owner: platform name: ingress-https-onlyspec: enforcementAction: dryrun match: kinds: - apiGroups: ["extensions", "networking.k8s.io"] kinds: ["Ingress"]The Constraint is what actually runs. It names the template (K8sHttpsOnly, from metadata.name: k8shttpsonly in the template) and scopes it: only Ingress objects in the extensions and networking.k8s.io API groups are reviewed. enforcementAction: dryrun means this constraint currently logs violations rather than blocking admission — a live-cluster detail, not something gator
Gatekeeper offline CLI — runs `gator verify` against constraint suites without a live cluster (Lab A). cares about offline.
3. Test suite — the test cases
Section titled “3. Test suite — the test cases”Source: infrastructure/gatekeeper/tests/httpsonly/suite.yaml
kind: SuiteapiVersion: test.gatekeeper.sh/v1alpha1tests: - name: ingress-https-only template: ../../constraint-templates/httpsonly.yaml constraint: ../../constraints/httpsonly/constraint.yaml cases: - name: ingress-with-tls object: pass.yaml assertions: - violations: "no" - name: ingress-without-tls object: fail.yaml assertions: - violations: "yes"A suite wires one template and one constraint together, then runs named cases against them. Each
case points at an object file and asserts how many violations gator should find: pass.yaml
should produce violations: "no", fail.yaml should produce violations: "yes". gator verify
fails the whole suite if either assertion does not hold.
4. The failing case
Section titled “4. The failing case”Source: infrastructure/gatekeeper/tests/httpsonly/fail.yaml
apiVersion: networking.k8s.io/v1kind: Ingressmetadata: name: test-ingress namespace: my-appspec: rules: - host: example.com http: paths: - path: / pathType: Prefix backend: service: name: my-svc port: number: 80This Ingress has no tls block and no allow-http annotation, so it fails both conditions in
https_complete. The expected result of the ingress-without-tls case is one violation with this
message, built by the Rego sprintf call:
Ingress should be https. tls configuration and allow-http=false annotation are required for test-ingressThat block is an expected result, not a captured terminal session — no failing run exists in the walkthrough recording, because CI only records the passing suite.
Reading a violation message
Section titled “Reading a violation message”Every gator violation has three parts, and they show up in that sprintf output above:
- Constraint kind —
K8sHttpsOnly, from the template’smetadata.namefield, tells you which rule fired. - Reviewed object identity —
test-ingress, interpolated into the message, tells you which object failed. - Message string — the human-readable explanation the Rego author wrote, here naming the
two missing pieces (
tls configuration and allow-http=false annotation).
When gator verify reports a failure, these three parts are what you read to find the fix.
Run the full offline suite
Section titled “Run the full offline suite”The httpsonly chain above is one of fourteen constraints. The remaining policies live under
infrastructure/gatekeeper/constraints/
and
infrastructure/gatekeeper/tests/,
each following the same template → constraint → suite shape.
- Confirm
pwdis insidekiro-eks-argocd-migrationandgator versionprints3.22.0. - Run
gator verifyagainst the full tests tree so all fourteen suites execute. - Build both policy overlays with
kustomizeso the bundle that Argo would sync still renders. - Confirm every suite reports
okand the run ends inPASS.
gator verify infrastructure/gatekeeper/tests/...kustomize build policies/overlays/dev-eks-1 >/dev/nullkustomize build policies/overlays/prod-eks-1 >/dev/nullExpect all suites ok and a final PASS.
This lab only proves the first clause of the factory’s contract —
skills generate → hooks enforce → humans approve. Passing gator verify here means the
policy layer is sound; it does not yet show that a thin prompt can hit these constraints
automatically. Lab B picks that up next.
Tradeoff
Section titled “Tradeoff”Choice: offline admission testing with gator
Gatekeeper offline CLI — runs `gator verify` against constraint suites without a live cluster (Lab A).. Alternative: wait for a live cluster webhook. Reason: gator catches constraint regressions in CI and on the laptop without cluster credentials, so Lab A stays reproducible for every reader.
If gator verify fails in a way that looks like a broken install rather than a real policy
violation, check Known scars: gator version skew — that
mismatch is the usual culprit.
Next: What makes prompts safe (Lab B) — the .kiro/ map that makes thin prompts safe.