Manual Test Runbook — D4 + X4: Kyverno Policy Bundle¶
Owner: Sagar | Time: ~20 min (offline) · +30 min (optional kind-cluster install) | Sandbox: none for Parts A–B; local kind for Part C
D4 ships the SnowOps Kyverno bundle (policy/kyverno/rules/). X4 ships the
test harness (policy/kyverno/tests/). This runbook validates both —
offline via kyverno test, then end-to-end via an actual admission round-trip
on a local kind cluster (Part C is optional).
Prerequisites¶
- Local tooling:
kyverno >= 1.18(brew install kyvernoon macOS).- (Part C only)
kind,kubectl, Docker daemon running.
- Working directory: repo root.
- Clean working tree.
Steps¶
Part A — kyverno test per-rule fixtures (~2 min)¶
- Run the full X4 suite:
- Expected:
Running 9 kyverno test suites...
── disallow-host-namespaces ── PASS
── disallow-latest-tag ── PASS
── disallow-privileged-containers ── PASS
── require-network-policy ── PASS
── require-non-root ── PASS
── require-pod-labels ── PASS
── require-probes ── PASS
── require-resource-limits ── PASS
── require-signed-images ── PASS
All 9 suites passed.
- Per-rule breakdown:
disallow-latest-tag— 6 assertions (pass + fail per rule × 3 pods × 2 rules)disallow-privileged-containers— 10 assertions (5 pods × 2 rules)require-pod-labels— 3 assertions (well-labelled / missing-owner / no-labels)require-network-policy— 1 assertion (Namespace → generated default-deny NetworkPolicy matchestests/require-network-policy/generated.yaml)require-signed-images— 1 assertion (non-ACR image short-circuits toskip; ACR signature verification is exercised in Part C only — kyverno-test cannot reach a live registry)require-resource-limits— 3 assertions (bounded / unbounded / requests-only pods)require-non-root— 3 assertions (pod-levelrunAsNonRoot=true/=false/ unset)disallow-host-namespaces— 5 assertions (isolated, explicitly-isolated, hostNetwork, hostPID, hostIPC pods)-
require-probes— 3 assertions (both probes / neither probe / liveness-only) -
If a suite fails, fix the rule and the fixture before re-running. The wrapper prints the full kyverno-test output for any failing suite.
Part B — per-rule deep-dive (~5 min, optional)¶
- Run a single suite with verbose output to confirm individual results:
- Spot-check the result table:
tagged-pod× both rules →Pass / Okuntagged-pod×require-image-tag→Pass / Ok(test assertsfail)latest-tag-pod×disallow-latest-tag→Pass / Ok(test assertsfail)
"Result: Pass" in the table = "the assertion matched". "Reason: Ok" = the policy WAS evaluated (vs "Excluded" / "Not found"). Both must be present for a clean run.
Part C — live admission on a kind cluster (~30 min, optional)¶
Required before signing off the rule bundle for client install. Skip if only iterating on rule wording or the test harness.
- Spin up a local kind cluster + install Kyverno:
kind create cluster --name snowops-d4-test
kubectl create namespace kyverno
# Kyverno v1.18.x install via Helm
helm repo add kyverno https://kyverno.github.io/kyverno/
helm install kyverno kyverno/kyverno --namespace kyverno --version 3.4.x \
--set replicaCount=1
kubectl -n kyverno rollout status deploy/kyverno-admission-controller --timeout=5m
- Apply the SnowOps rule bundle:
kubectl apply -f policy/kyverno/rules/
kubectl get clusterpolicy
# NAME BACKGROUND VALIDATE ACTION READY
# snowops-disallow-host-namespaces true Enforce true
# snowops-disallow-latest-tag true Enforce true
# snowops-disallow-privileged-containers true Enforce true
# snowops-require-default-deny-netpol true Enforce true
# snowops-require-non-root true Enforce true
# snowops-require-pod-labels true Enforce true
# snowops-require-probes true Enforce true
# snowops-require-resource-limits true Enforce true
# snowops-require-signed-images true Enforce true
- Validate each rule end-to-end. Create a workload namespace first:
kubectl create namespace app
# Expect: kyverno generates a default-deny NetworkPolicy automatically.
kubectl -n app get networkpolicy default-deny
# NAME POD-SELECTOR AGE
# default-deny <none> 5s
-
Attempt to create a Pod with
:latest: -
Attempt to create a privileged Pod (use a quick manifest):
kubectl -n app apply -f - <<'EOF' apiVersion: v1 kind: Pod metadata: name: priv-test labels: app.kubernetes.io/name: priv-test app.kubernetes.io/version: 1.0.0 app.kubernetes.io/part-of: snowops-suite snowops.io/owner: platform-team spec: containers: - name: bad image: nginx:1.27.0 securityContext: privileged: true EOF # Expect rejection: "Privileged containers are not allowed." -
Attempt to create a Pod missing the SnowOps labels:
-
Create a fully-conformant Pod and confirm admission succeeds:
kubectl -n app apply -f - <<'EOF' apiVersion: v1 kind: Pod metadata: name: good-pod labels: app.kubernetes.io/name: good-pod app.kubernetes.io/version: 1.0.0 app.kubernetes.io/part-of: snowops-suite snowops.io/owner: platform-team spec: securityContext: runAsNonRoot: true containers: - name: web image: nginx:1.27.0 resources: requests: cpu: 100m memory: 128Mi limits: cpu: 500m memory: 256Mi livenessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 5 periodSeconds: 10 readinessProbe: httpGet: path: /ready port: 8080 initialDelaySeconds: 5 periodSeconds: 10 EOF # Expect: pod/good-pod created -
Confirm the exclude block keeps system namespaces working:
-
Cleanup:
Pass criteria¶
- Part A —
policy/kyverno/tests/run-tests.shreports 9/9 suites passing. - Part B — per-suite
kyverno test --detailed-resultsruns showReason: Okfor every evaluated case (noExcluded/Not found). - (Part C) kind cluster boots and Kyverno admission controller is
Ready. - (Part C)
kubectl apply -f policy/kyverno/rules/succeeds andkubectl get clusterpolicyshows all 9 policiesReady=true. - (Part C) Each of the four failure paths (latest tag, privileged, missing labels, untagged image) is rejected by the admission webhook with the expected error message.
- (Part C) A fully-conformant Pod is admitted.
- (Part C)
default-denyNetworkPolicy is auto-generated in workload namespaces and NOT generated inkube-system/kyverno/default/kube-public/kube-node-lease.
Teardown¶
D4 + X4 have no cloud side-effects. Part C creates a local kind cluster that
must be deleted with kind delete cluster --name snowops-d4-test.
If Part A flagged a regression, revert the offending file:
Sign-off¶
- Tester: Sagar Chhabra | Date: 16/6/2026 | Result: PASS
- Notes: