Exploring the Role of Admission Controllers in Kubernetes

In the Kubernetes ecosystem, Admission Controllers play a critical role in governing and securing workloads at the cluster level. While authentication and authorization determine who can interact with the Kubernetes API, admission controllers decide what is allowed to be admitted into the cluster after the request is authenticated and authorized.

In this blog, we’ll break down what admission controllers are, how they work, and explore some of the most commonly used built-in admission controllers provided by Kubernetes.

What is an Admission Controller?

An Admission Controller is a plug-in within the Kubernetes API server that intercepts API requests after authentication and authorization, but before the request data is persisted in etcd.

They apply to requests that:

  • Create objects

  • Modify objects

  • Delete objects

  • Block certain custom verbs (e.g., connecting to a pod via the API server proxy)

There are two types:

  1. Mutating Admission Controllers – Modify incoming requests before they are persisted (e.g., adding labels or defaults)

  2. Validating Admission Controllers – Reject requests that don’t meet specific criteria.

Note: Admission controllers only act on mutating operations (create, update, delete). They do not intercept read requests (get, list, watch), so they cannot be used to hide data—only to enforce policies on changes.

API Request Lifecycle:

Mutating Admission Controllers

Mutating admission controllers take a Kubernetes resource request and make changes to it—like adding or updating fields—before it moves to the next step in the process.

Use cases:

  • Automatically injecting default values (e.g., labels, resource limits).

  • Adding sidecar containers (e.g., Istio or Linkerd).

  • Assigning service accounts if not specified.

Here is an example of ServiceAccount

—> Automatically injects a service account token and assigns a default service account if none is specified.

pod.yml

apiVersion: v1
kind: Pod
metadata:
  name: ex-pod
spec:
  containers:
  - name: nginx
    image: nginx

here in above yaml no service account mentioned,but but kuberenetes by default attach a default service account to it.

This are some default mutating controllers:

MutatingAdmissionWebhookSends incoming objects to an external webhook before persistence. Used for tasks like auto-injecting sidecars (e.g., Istio proxies). Commonly applies default labels, runtime constraints, and security settings.
DefaultStorageClassAssigns a default StorageClass to PersistentVolumeClaims lacking one, ensuring PVCs use appropriate backends like EBS, GCP PD, or Azure Disk. Simplifies volume provisioning.
DefaultTolerationSecondsSets default tolerations for Pods exposed to NoExecute taints, preventing unwanted evictions when tolerations are missing. Ensures predictable pod lifecycles.
DefaultIngressClassAssigns a default IngressClass when none is specified to avoid confusion in multi-ingress setups (e.g. NGINX, HAProxy, or AWS ALB). Fails creation if multiple defaults exist to maintain clarity. Skips if no default is configured.

Note: In Kuberenetes some admission controller acts as mutating as well as validating controllers. eg. Priority,ServiceAccount, LimitRanger,AlwaysPullImages,etc.

Reference_Doc

Validating Admission Controllers

Validating controllers do not change the request, but they can accept or reject it based on logic.

Use cases:

  • Blocking privileged containers.

  • Enforcing naming conventions.

  • Rejecting deployments missing required labels.

Here is an example of ResourceQuota

—> Enforces namespace-level quotas for CPU, memory, and storage, preventing resource overconsumption in multi-tenant clusters.

resourcequota.yaml

apiVersion: v1
kind: ResourceQuota
metadata:
  name: quota
  namespace: dev
spec:
  hard:
    requests.cpu: "1"
    requests.memory: 1Gi
    limits.cpu: "2"
    limits.memory: 2Gi

If a user tries to schedule a pod exceeding this, the request is rejected.

pod.yml

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
  namespace: dev
spec:
  containers:
  - name: app
    image: nginx
    resources:
      requests:
        cpu: "1.5"
        memory: "1.5Gi"
      limits:
        cpu: "2.5"
        memory: "2.5Gi"
Error from server (Forbidden): error when creating "pod.yaml": pods "my-pod" is forbidden: exceeded quota: quota, requested: requests.cpu=1500m, requests.memory=1536Mi, limits.cpu=2500m, limits.memory=2560Mi, used: requests.cpu=0, requests.memory=0, limits.cpu=0, limits.memory=0, limited: requests.cpu=1, requests.memory=1Gi, limits.cpu=2, limits.memory=2Gi

This are some default validating controllers:

PluginWhat It Does
PodSecurityMakes sure Pods follow security rules like not running as root or using host paths. Replaces old PSP rules.
ResourceQuotaStops users from using too much CPU, memory, or storage in a namespace.
ValidatingAdmissionWebhookSends objects to external tools to check if they follow custom rules, like blocking unsafe settings.
NamespaceLifecycleBlocks actions in namespaces that are being deleted or don’t exist.
NodeRestrictionStops kubelets from making changes to other nodes or workloads they don’t own.

How to Enable/Disable Admission Controllers:

cat /etc/kubernetes/manifests/kube-apiserver.yaml

--enable-admission-plugins: Adds plugins in addition to the default set.

--disable-admission-plugins: Explicitly removes plugins from the default set.

Conclusion

Admission controllers act as gatekeepers to your cluster’s stability and security, ensuring that only valid and compliant configurations are accepted. They help mutate incoming requests with defaults and validate them against custom or built-in policies. By using both built-in and custom admission controllers, you can enforce governance, security, and resource controls effectively. As your Kubernetes environment scales, leveraging admission controllers becomes essential for maintaining order and consistency. They silently shape every API request—before it ever hits etcd.

0
Subscribe to my newsletter

Read articles from Chaitanya Kharche directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Chaitanya Kharche
Chaitanya Kharche

DevOps Engineer with 3 years of experience | AWS | Kubernetes | Docker | Terraform | Jenkins | Python