Admission Policy
Admission controllers intercept API requests before objects are persisted. Pod Security Admission (PSA) is built-in and enforces security standards by namespace label. OPA Gatekeeper and Kyverno provide custom policy-as-code. Start with PSA in warn mode, then graduate to enforce.
Pod Security Admission (Built-in)
PSA replaced PodSecurityPolicy (removed in 1.25); apply it per namespace with labels — use warn first to see what would fail before switching to enforce.
# PSA profiles: privileged | baseline | restricted
# Modes: enforce (reject) | audit (log) | warn (warn in API response)
# Apply restricted profile in warn+audit mode first
kubectl label namespace my-app \
pod-security.kubernetes.io/warn=restricted \
pod-security.kubernetes.io/warn-version=latest \
pod-security.kubernetes.io/audit=restricted \
pod-security.kubernetes.io/audit-version=latest
# Once workloads are compliant, enforce:
kubectl label namespace my-app \
pod-security.kubernetes.io/enforce=restricted \
pod-security.kubernetes.io/enforce-version=latest
# Check which namespaces have PSA labels
kubectl get ns -o jsonpath='{range .items[*]}{.metadata.name}: {.metadata.labels}{"\n"}{end}' | grep "pod-security"
# Dry-run apply to see PSA warnings before deploying
kubectl apply -f deployment.yaml --dry-run=server| Profile | What it blocks | When to use |
|---|---|---|
| privileged | Nothing — full access | Infra namespaces (monitoring, CNI, CSI) |
| baseline | Known privilege escalations (hostPID, hostNetwork, privileged containers) | Most workloads; good starting point |
| restricted | Everything in baseline + requires non-root, read-only rootfs, drop ALL capabilities | Production app namespaces |
Kyverno Policies
Kyverno is a Kubernetes-native policy engine; policies are written in YAML (no Rego), making them accessible to engineers who are already comfortable with Kubernetes manifests.
# Validate: require resource requests and limits on every container
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-resources
spec:
validationFailureAction: Audit # Audit | Enforce
background: true # also check existing resources
rules:
- name: check-resources
match:
any:
- resources:
kinds: [Pod]
validate:
message: "Resource requests and limits are required on all containers."
pattern:
spec:
containers:
- (name): "*"
resources:
requests:
memory: "?*"
cpu: "?*"
limits:
memory: "?*"
---
# Mutate: add a default team label if missing
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: add-default-labels
spec:
rules:
- name: add-team-label
match:
any:
- resources:
kinds: [Pod]
mutate:
patchStrategicMerge:
metadata:
labels:
+(team): unknown # + prefix = only add if missing
---
# Generate: create a NetworkPolicy for every new namespace
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: default-deny-ingress
spec:
rules:
- name: generate-network-policy
match:
any:
- resources:
kinds: [Namespace]
generate:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
name: default-deny-ingress
namespace: "{{request.object.metadata.name}}"
data:
spec:
podSelector: {}
policyTypes: [Ingress]# Test policies locally before applying to cluster
kyverno apply policy.yaml --resource deployment.yaml
# Check policy reports (audit violations without blocking)
kubectl get policyreport -A
kubectl get clusterpolicyreport
# View violations for a namespace
kubectl get policyreport -n my-app -o json | jq '.results[] | select(.result=="fail")'OPA Gatekeeper (Overview)
Gatekeeper uses Rego (OPA policy language) for highly flexible policies; more powerful than Kyverno for complex logic, but Rego has a steeper learning curve — use it if you already have OPA expertise.
# Check Gatekeeper status
kubectl get constrainttemplates
kubectl get constraints -A # all constraint instances
# View violations for a constraint
kubectl describe constraint require-resource-limits
# Audit mode: scan existing resources without blocking new ones
kubectl get constrainttemplate <name> -o yaml | grep enforcementAction
# dryrun = audit only; deny = enforce