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).

PhaseOrder
MutatingFirst — modify request
ValidatingLast — final check

2. Enabling Admission Plugins

kube-apiserver \
  --enable-admission-plugins=NodeRestriction,ResourceQuota,LimitRanger,PodSecurity \
  --disable-admission-plugins=AlwaysAdmit
Built-inUse
NodeRestrictionKubelets can only modify own node/pods
ResourceQuotaEnforce namespace quotas
PodSecurityApply PSS profiles
DefaultStorageClassInject 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

FieldDetail
reinvocationPolicyNever / IfNeeded
matchPolicyExact / Equivalent
objectSelectorFilter 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

ToolApproach
KyvernoYAML policies; verifyImages with Sigstore/Cosign
GatekeeperRego rules on image string
ImagePolicyWebhookBuilt-in plugin (custom backend)

9. Using LimitRanger

Built-in mutating admission controller that injects defaults from LimitRange and enforces min/max.

ActionEffect
MutateInject default requests/limits
ValidateReject 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

SymptomCause
Webhook timeoutSlow service; increase timeoutSeconds
x509 errorsStale caBundle after cert rotation
All API calls failfailurePolicy=Fail + webhook down
Self-loopWebhook can't pull image because webhook gates it; use namespaceSelector to exclude