What Are Admission Controllers in Kubernetes?

advanced|securitydevopssreCKA
TL;DR

Admission controllers are plugins that intercept API requests after authentication and authorization but before the object is persisted to etcd. They can validate or mutate requests. Kubernetes includes many built-in controllers, and you can add custom ones via webhooks.

Detailed Answer

The API Request Pipeline

Every request to the Kubernetes API server passes through this pipeline:

Client Request
  -> Authentication (who are you?)
  -> Authorization (are you allowed?)
  -> Mutating Admission (modify the request)
  -> Schema Validation (is the object valid?)
  -> Validating Admission (apply policy checks)
  -> Persistence to etcd

Admission controllers operate in the two admission phases. If any admission controller rejects a request, the API server returns an error and the object is not created.

Built-in Admission Controllers

Kubernetes ships with numerous compiled-in admission controllers. Important ones include:

| Controller | Type | Purpose | |---|---|---| | NamespaceLifecycle | Validating | Prevents operations in terminating or non-existent namespaces | | LimitRanger | Mutating | Applies default resource requests/limits from LimitRange objects | | ServiceAccount | Mutating | Auto-mounts service account tokens and sets default SA | | DefaultStorageClass | Mutating | Assigns default StorageClass to PVCs without one | | ResourceQuota | Validating | Enforces resource quotas per namespace | | PodSecurity | Validating | Enforces Pod Security Standards | | MutatingAdmissionWebhook | Mutating | Calls external mutating webhooks | | ValidatingAdmissionWebhook | Validating | Calls external validating webhooks |

# View enabled admission controllers
kubectl -s https://<api-server>:6443 api-versions
# Check API server flags:
# --enable-admission-plugins=NamespaceLifecycle,LimitRanger,...

Mutating Admission Webhooks

Mutating webhooks modify API requests before they are validated. The most common use case is sidecar injection (e.g., Istio's envoy proxy).

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
  name: sidecar-injector
webhooks:
  - name: inject.sidecar.io
    admissionReviewVersions: ["v1"]
    clientConfig:
      service:
        name: sidecar-injector
        namespace: sidecar-system
        path: /inject
      caBundle: <base64-encoded-CA>
    rules:
      - operations: ["CREATE"]
        apiGroups: [""]
        apiVersions: ["v1"]
        resources: ["pods"]
    namespaceSelector:
      matchLabels:
        sidecar-injection: enabled
    failurePolicy: Fail
    sideEffects: None
    timeoutSeconds: 10

This webhook intercepts Pod creation in namespaces labeled sidecar-injection: enabled and can modify the Pod spec to add a sidecar container.

Validating Admission Webhooks

Validating webhooks check requests against policies and accept or reject them. They cannot modify the request.

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  name: image-policy
webhooks:
  - name: validate.images.io
    admissionReviewVersions: ["v1"]
    clientConfig:
      service:
        name: image-validator
        namespace: policy-system
        path: /validate
      caBundle: <base64-encoded-CA>
    rules:
      - operations: ["CREATE", "UPDATE"]
        apiGroups: [""]
        apiVersions: ["v1"]
        resources: ["pods"]
    failurePolicy: Fail
    sideEffects: None
    timeoutSeconds: 5

ValidatingAdmissionPolicy (Native CEL-based)

Kubernetes 1.30+ supports ValidatingAdmissionPolicy, which allows writing validation rules directly in CEL (Common Expression Language) without deploying a webhook server:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
  name: require-non-root
spec:
  failurePolicy: Fail
  matchConstraints:
    resourceRules:
      - apiGroups: [""]
        apiVersions: ["v1"]
        operations: ["CREATE"]
        resources: ["pods"]
  validations:
    - expression: "object.spec.securityContext.runAsNonRoot == true"
      message: "Pods must set runAsNonRoot: true"
    - expression: "object.spec.containers.all(c, c.securityContext.allowPrivilegeEscalation == false)"
      message: "All containers must set allowPrivilegeEscalation: false"
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
  name: require-non-root-binding
spec:
  policyName: require-non-root
  validationActions:
    - Deny
  matchResources:
    namespaceSelector:
      matchLabels:
        environment: production

OPA Gatekeeper

OPA (Open Policy Agent) Gatekeeper is a popular third-party admission controller that uses Rego policies:

# Install Gatekeeper
kubectl apply -f https://raw.githubusercontent.com/open-policy-agent/gatekeeper/v3.15.0/deploy/gatekeeper.yaml
# Define a constraint template
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}] {
          provided := {label | input.review.object.metadata.labels[label]}
          required := {label | label := input.parameters.labels[_]}
          missing := required - provided
          count(missing) > 0
          msg := sprintf("Missing required labels: %v", [missing])
        }
---
# Apply the constraint
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
  name: require-team-label
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Namespace"]
  parameters:
    labels:
      - "team"
      - "environment"

Webhook Best Practices

  1. High availability: Run webhook servers with multiple replicas and proper health checks.
  2. Timeouts: Keep timeouts short (5-10 seconds). Long timeouts slow down all API operations.
  3. Failure policy: Use Fail for security-critical webhooks, Ignore for non-critical ones.
  4. Namespace selectors: Exclude kube-system from webhooks to prevent locking yourself out.
  5. Dry-run support: Implement sideEffects: None to support kubectl --dry-run=server.
# Always exclude kube-system to avoid breaking the cluster
namespaceSelector:
  matchExpressions:
    - key: kubernetes.io/metadata.name
      operator: NotIn
      values:
        - kube-system

Why Interviewers Ask This

Interviewers ask this to assess your understanding of the Kubernetes API request pipeline and your ability to implement custom policies and governance at the cluster level.

Common Follow-Up Questions

What is the difference between mutating and validating admission webhooks?
Mutating webhooks can modify the request (e.g., inject sidecars), while validating webhooks can only accept or reject it. Mutating runs first, then validating.
What happens if an admission webhook is unreachable?
It depends on the failurePolicy: Fail blocks the request, Ignore allows it through. Production webhooks should use Fail with high availability.
Name some important built-in admission controllers.
NamespaceLifecycle, LimitRanger, ServiceAccount, DefaultStorageClass, ResourceQuota, PodSecurity, and MutatingAdmissionWebhook/ValidatingAdmissionWebhook.

Key Takeaways

  • Admission controllers sit between authorization and persistence in the API pipeline
  • Mutating webhooks run before validating webhooks
  • Built-in controllers handle defaults; webhooks enable custom policy enforcement