Working with Admission Controllers
1. Understanding Admission Controllers
Plugins that intercept requests after authn/authz but before persistence. Two types: mutating (modify) and validating (accept/reject).
| Phase | Order |
| Mutating | First — modify request |
| Validating | Last — final check |
2. Enabling Admission Plugins
kube-apiserver \
--enable-admission-plugins=NodeRestriction,ResourceQuota,LimitRanger,PodSecurity \
--disable-admission-plugins=AlwaysAdmit
| Built-in | Use |
| NodeRestriction | Kubelets can only modify own node/pods |
| ResourceQuota | Enforce namespace quotas |
| PodSecurity | Apply PSS profiles |
| DefaultStorageClass | Inject default SC |
3. Using ValidatingWebhookConfiguration
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata: { name: image-policy }
webhooks:
- name: image.policy.svc
rules:
- { apiGroups: [""], apiVersions: [v1], operations: [CREATE], resources: [pods] }
clientConfig:
service: { namespace: policy, name: webhook, path: /validate }
caBundle: LS0tLS1CRUdJTi...
admissionReviewVersions: [v1]
sideEffects: None
failurePolicy: Fail
timeoutSeconds: 5
4. Using MutatingWebhookConfiguration
| Field | Detail |
| reinvocationPolicy | Never / IfNeeded |
| matchPolicy | Exact / Equivalent |
| objectSelector | Filter by object labels |
5. Implementing OPA Gatekeeper
helm repo add gatekeeper https://open-policy-agent.github.io/gatekeeper/charts
helm install gatekeeper gatekeeper/gatekeeper -n gatekeeper-system --create-namespace
6. Creating Constraint Templates
apiVersion: templates.gatekeeper.sh/v1
kind: ConstraintTemplate
metadata: { name: k8srequiredlabels }
spec:
crd:
spec:
names: { kind: K8sRequiredLabels }
validation:
openAPIV3Schema:
type: object
properties: { labels: { type: array, items: { type: string } } }
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package k8srequiredlabels
violation[{"msg": msg}] {
required := input.parameters.labels
provided := {label | input.review.object.metadata.labels[label]}
missing := required - provided
count(missing) > 0
msg := sprintf("missing labels: %v", [missing])
}
7. Creating Constraints
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata: { name: require-owner }
spec:
match: { kinds: [{ apiGroups: [""], kinds: [Namespace] }] }
parameters: { labels: [owner, cost-center] }
8. Implementing Image Policy
| Tool | Approach |
| Kyverno | YAML policies; verifyImages with Sigstore/Cosign |
| Gatekeeper | Rego rules on image string |
| ImagePolicyWebhook | Built-in plugin (custom backend) |
9. Using LimitRanger
Built-in mutating admission controller that injects defaults from LimitRange and enforces min/max.
| Action | Effect |
| Mutate | Inject default requests/limits |
| Validate | Reject if outside min/max |
10. Testing Admission Webhooks
kubectl apply --dry-run=server -f bad-pod.yaml
kubectl get validatingwebhookconfiguration,mutatingwebhookconfiguration
11. Troubleshooting Admission Failures
| Symptom | Cause |
| Webhook timeout | Slow service; increase timeoutSeconds |
| x509 errors | Stale caBundle after cert rotation |
| All API calls fail | failurePolicy=Fail + webhook down |
| Self-loop | Webhook can't pull image because webhook gates it; use namespaceSelector to exclude |