Lab: Admission Guardrails in Audit and Enforce Modes (Advanced)
Goal
Run a practical Kyverno workflow in develop:
- enable starter policies in
Audit - trigger and inspect violations
- switch one policy to
Enforce - validate deny -> fix -> admit behavior
Prerequisites
- Kyverno engine running in cluster
- access to
developnamespace - starter templates available:
flux/infrastructure/policy/packs/chapter-15-admission-guardrails/disallow-latest-tag.example.yamlflux/infrastructure/policy/packs/chapter-15-admission-guardrails/require-security-context.example.yamlflux/infrastructure/policy/packs/chapter-15-admission-guardrails/require-requests-limits.example.yaml
Quick checks:
kubectl -n kyverno get pods
kubectl get ns develop
kubectl get cpol
Step 1: Apply Starter Policies in Audit Mode
kubectl apply -f flux/infrastructure/policy/packs/chapter-15-admission-guardrails/disallow-latest-tag.example.yaml
kubectl apply -f flux/infrastructure/policy/packs/chapter-15-admission-guardrails/require-security-context.example.yaml
kubectl apply -f flux/infrastructure/policy/packs/chapter-15-admission-guardrails/require-requests-limits.example.yaml
Confirm mode is Audit:
kubectl get cpol disallow-latest-tag-example -o jsonpath='{.spec.validationFailureAction}'; echo
kubectl get cpol require-security-context-example -o jsonpath='{.spec.validationFailureAction}'; echo
kubectl get cpol require-requests-limits-example -o jsonpath='{.spec.validationFailureAction}'; echo
Step 2: Apply a Risky Workload (Expected Audit Violation)
Create risky manifest:
cat <<'YAML' >/tmp/ch15-risky-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: ch15-risky
namespace: develop
spec:
containers:
- name: app
image: nginx:latest
YAML
kubectl apply -f /tmp/ch15-risky-pod.yaml
Inspect audit signals:
kubectl -n develop get events --sort-by=.lastTimestamp | tail -n 30
kubectl get policyreport -A | rg develop
Step 3: Switch One Policy to Enforce
kubectl patch cpol disallow-latest-tag-example \
--type merge \
-p '{"spec":{"validationFailureAction":"Enforce"}}'
Re-apply risky manifest (expected deny):
kubectl delete -f /tmp/ch15-risky-pod.yaml --ignore-not-found=true
kubectl apply -f /tmp/ch15-risky-pod.yaml
Step 4: Remediate and Re-Apply
Create compliant manifest:
cat <<'YAML' >/tmp/ch15-compliant-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: ch15-compliant
namespace: develop
spec:
securityContext:
runAsNonRoot: true
containers:
- name: app
image: nginx:1.27.5
securityContext:
runAsNonRoot: true
allowPrivilegeEscalation: false
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 200m
memory: 256Mi
YAML
kubectl apply -f /tmp/ch15-compliant-pod.yaml
kubectl -n develop get pod ch15-compliant
Step 5: Controlled Cleanup
Return policy to Audit for shared environments:
kubectl patch cpol disallow-latest-tag-example \
--type merge \
-p '{"spec":{"validationFailureAction":"Audit"}}'
Cleanup resources:
kubectl -n develop delete pod ch15-risky ch15-compliant --ignore-not-found=true
rm -f /tmp/ch15-risky-pod.yaml /tmp/ch15-compliant-pod.yaml
Optional policy cleanup:
kubectl delete cpol \
disallow-latest-tag-example \
require-security-context-example \
require-requests-limits-example \
--ignore-not-found=true
Evidence to Capture
- policy mode before/after (
Audit/Enforce) - audit event/policyreport for risky manifest
- deny message in
Enforce - successful admit of compliant manifest
Hard Stop Conditions
- disabling Kyverno deployment instead of fixing manifests
- creating namespace-wide permanent exception
- moving all policies to
Enforcein production without audit data
Done When
- learner demonstrates
Audit -> Enforce -> Auditsafely - learner can explain deny reason and exact remediation