Admission policies
Admission policies
PSA covers Pod security only. Organisation rules - “every Deployment has an
owner label”, “images only from our registry”, “no latest” - need a
policy engine in the admission chain.
graph LR
R[request, authorized] --> M[mutating admission<br/>add defaults, sidecars]
M --> S[schema validation]
S --> V[validating admission<br/>PSA, VAP, Kyverno, Gatekeeper]
V -->|allowed| E[(etcd)]
V -->|denied| X[error to the user]
ValidatingAdmissionPolicy (built in)
Rules written in CEL, evaluated inside the API server - no extra component. GA since Kubernetes 1.30.
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata: { name: no-latest-tag }
spec:
failurePolicy: Fail
matchConstraints:
resourceRules:
- apiGroups: ["apps"]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["deployments"]
validations:
- expression: >-
object.spec.template.spec.containers.all(c,
c.image.contains(':') && !c.image.endsWith(':latest'))
message: "images must have an explicit tag other than latest"
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata: { name: no-latest-tag }
spec:
policyName: no-latest-tag
validationActions: ["Deny"] # or Warn, Audit
matchResources:
namespaceSelector:
matchLabels: { policy: enforced }
Policy = what, binding = where and how strictly.
Policy engines compared
| VAP (built in) | Kyverno | OPA Gatekeeper | |
|---|---|---|---|
| Language | CEL | YAML (+ CEL) | Rego |
| Validate | yes | yes | yes |
| Mutate / generate | MutatingAdmissionPolicy (newer) | yes | mutate only |
| Reports on existing objects | no | PolicyReports | audit |
| Extra component | none | yes | yes |
Start with VAP for simple rules; take Kyverno when you need mutation, generation or reports.