Understanding Kubernetes Admission Controllers


What’s the Deal with Admission Controllers?
Alright, let’s talk about Admission Controllers in Kubernetes. If you’re new to this, don’t sweat it—they’re not as scary as they sound. Imagine them as the cool security guards at a concert, checking your ticket and maybe adding a VIP wristband before letting you in. In Kubernetes, they’re plugins that jump in to check or tweak API requests right after you’ve been verified but before your stuff gets saved in the cluster’s storage (etcd). Let’s break it down, keep it real, and throw in a beefy example to make it crystal clear.
What Are Admission Controllers?
These bad boys are part of the Kubernetes API server, stepping in after authentication (who are you?) and authorization (can you do this?). They either validate your request to make sure it’s legit or modify it to fit the vibe of your cluster. They’ve got two main jobs:
Validation: Checking if your request plays by the rules—like, “Does this pod have the right labels or image?”
Mutation: Sprucing up the request, like adding default resource limits or tossing in a sidecar container for logging.
They work in two phases:
Mutating: Tweaks the resource to make it better or match your setup.
Validating: Gives a thumbs-up or shuts it down. If any controller says “nope,” your request is toast.
How Do They Work?
Here’s the flow when you fire off a kubectl apply
:
Authentication: Kubernetes checks you’re legit.
Authorization: Makes sure you’ve got the green light for the action.
Admission Control: Your request hits a chain of controllers that can tweak it, check it, or reject it.
Persistence: If it passes, it’s saved in etcd, and you’re golden.
The cluster admin sets the order of these controllers in the API server config. If one controller throws a fit, the whole request gets bounced with an error.
Types of Admission Controllers
Kubernetes comes with a bunch of built-in controllers, split into mutating (the tweakers) and validating (the rule-enforcers). Here’s a quick look at some heavy hitters:
Mutating Controllers
These guys change your resource before it’s saved.
DefaultStorageClass: Slaps a default storage class on PersistentVolumeClaims if you didn’t pick one.
PodPreset: Injects stuff like env vars or volumes into pods based on preset rules.
LimitRanger: Sets default CPU/memory limits to keep pods from going rogue.
ServiceAccount: Hooks up a default service account and mounts its token.
MutatingAdmissionWebhook: Lets you run custom code to tweak resources (we’ll dig into webhooks soon).
Validating Controllers
These are the strict ones that approve or deny.
ValidatingAdmissionWebhook: Runs your custom validation logic.
ResourceQuota: Keeps resource usage in check within a namespace.
PodSecurity: Blocks pods with sketchy settings, like running as root.
DenyServiceExternalIPs: Rejects services using unapproved external IPs.
NamespaceLifecycle: Stops you from creating stuff in namespaces that are being deleted.
Turning Them On or Off
You control these in the API server config with flags like:
--enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount
--disable-admission-plugins=PodSecurity
Kubernetes enables a default set, but you can tweak it to match your needs.
Getting Spicy with Webhooks
For the real power moves, you can use dynamic admission controllers like MutatingAdmissionWebhook and ValidatingAdmissionWebhook. These let you write custom logic in a separate server (a webhook) that the API server calls to check or modify requests.
How Webhooks Roll
You send a request to the API server.
The server pings your webhook over HTTPS with the request details.
Your webhook either tweaks the resource or says “yay” or “nay.”
The API server applies the webhook’s response and moves on.
Why Webhooks Are Awesome
Custom Rules: Block pods that don’t follow your org’s security standards.
Auto-Tweaks: Add a monitoring sidecar to every pod.
Compliance: Ensure resources meet legal or company policies.
A Beefy Example: Validating Webhook to Enforce Pod Security
Let’s level up with a stronger example. Say your company has a strict policy: no pods can run containers as root, and they must have a specific label (security=hardened
). Here’s how you’d set up a validating webhook to enforce this.
Step 1: Write the Webhook Server
We’ll use Python with Flask to create a webhook server that checks pods. It’ll reject any pod where a container runs as root (i.e., securityContext.runAsUser
is 0 or unset) or lacks the security=hardened
label.
from flask import Flask, request, jsonify
import json
app = Flask(__name__)
@app.route('/validate', methods=['POST'])
def validate_pod():
admission_review = request.get_json()
pod = admission_review['request']['object']
uid = admission_review['request']['uid']
# Check for required label
labels = pod.get('metadata', {}).get('labels', {})
if labels.get('security') != 'hardened':
return jsonify({
'apiVersion': 'admission.k8s.io/v1',
'kind': 'AdmissionReview',
'response': {
'uid': uid,
'allowed': False,
'status': {'message': 'Pod must have label security=hardened'}
}
})
# Check for root user
for container in pod.get('spec', {}).get('containers', []):
security_context = container.get('securityContext', {})
run_as_user = security_context.get('runAsUser', None)
run_as_non_root = security_context.get('runAsNonRoot', False)
if run_as_user == 0 or (run_as_user is None and not run_as_non_root):
return jsonify({
'apiVersion': 'admission.k8s.io/v1',
'kind': 'AdmissionReview',
'response': {
'uid': uid,
'allowed': False,
'status': {'message': 'Containers cannot run as root'}
}
})
# All good
return jsonify({
'apiVersion': 'admission.k8s.io/v1',
'kind': 'AdmissionReview',
'response': {
'uid': uid,
'allowed': True
}
})
if __name__ == '__main__':
app.run(ssl_context=('cert.pem', 'key.pem'), host='0.0.0.0', port=443)
Step 2: Deploy the Webhook Server
Package the Python app in a Docker container.
Deploy it as a Kubernetes
Deployment
and expose it via aService
(e.g.,webhook-service
in thedefault
namespace).Generate a TLS certificate and key (e.g.,
cert.pem
andkey.pem
) and store them in a KubernetesSecret
.
apiVersion: apps/v1
kind: Deployment
metadata:
name: webhook-deployment
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: webhook
template:
metadata:
labels:
app: webhook
spec:
containers:
- name: webhook
image: your-webhook-image:latest
ports:
- containerPort: 443
volumeMounts:
- name: tls
mountPath: /app/tls
readOnly: true
volumes:
- name: tls
secret:
secretName: webhook-tls
---
apiVersion: v1
kind: Service
metadata:
name: webhook-service
namespace: default
spec:
selector:
app: webhook
ports:
- port: 443
targetPort: 443
Step 3: Register the Webhook
Create a ValidatingWebhookConfiguration
to tell Kubernetes to call your webhook for pod CREATE
and UPDATE
operations.
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: pod-security-webhook
webhooks:
- name: pod-security-webhook.example.com
rules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["pods"]
scope: "*"
clientConfig:
service:
name: webhook-service
namespace: default
path: /validate
caBundle: <base64-encoded-ca-cert>
admissionReviewVersions: ["v1"]
sideEffects: None
failurePolicy: Fail
Replace
<base64-encoded-ca-cert>
with the base64-encoded CA certificate used to sign your webhook’s TLS cert.The
failurePolicy: Fail
means if the webhook is down, requests are rejected (safer for security policies).
Step 4: Test It Out
Try creating a pod that breaks the rules:
apiVersion: v1
kind: Pod
metadata:
name: bad-pod
spec:
containers:
- name: nginx
image: nginx
securityContext:
runAsUser: 0 # Running as root
You’ll get an error like: Pod must have label security=hardened
or Containers cannot run as root
.
Now try a good pod:
apiVersion: v1
kind: Pod
metadata:
name: good-pod
labels:
security: hardened
spec:
containers:
- name: nginx
image: nginx
securityContext:
runAsNonRoot: true
This should go through without a hitch.
Cool Stuff You Can Do with Admission Controllers
Tighten Security:
Use
PodSecurity
to block privileged or root-running pods.Write webhooks to enforce trusted image registries.
Manage Resources:
LimitRanger
sets default CPU/memory limits to keep things tidy.ResourceQuota
caps resource usage per namespace.
Keep Things Consistent:
DefaultStorageClass
ensures uniform storage setups.Webhooks can inject sidecars for logging or monitoring.
Stay Compliant:
- Use webhooks to add annotations for cost tracking or regulatory compliance.
Tips to Keep It Chill
Don’t Go Overboard: Only enable the controllers you need—too many can slow your cluster down.
Webhook Reliability: Make sure your webhook is always up and snappy, or it’ll mess with API calls.
Test Like Crazy: Bad webhook code can block good requests or cause security gaps.
Monitor and Log: Track webhook performance and log decisions for debugging.
Secure Your Webhooks: Use TLS and RBAC to keep things locked down.
Watch Out for These Gotchas
Performance Drag: More controllers mean slower API requests.
Webhook Fails: If your webhook crashes, it can block requests unless you set
failurePolicy: Ignore
.Order Matters: The sequence of controllers can change how mutations stack up.
Debugging Sucks: Controller errors can be tricky to pin down, especially with custom webhooks.
Wrapping It Up
Admission controllers are your Kubernetes wingman, helping you enforce rules, tweak resources, and keep your cluster in check. From built-in ones like PodSecurity
to custom webhooks like our pod security example, they give you mad control. Just be smart—test thoroughly, keep it lean, and secure your webhooks. Wanna play around? Spin up a test cluster and try the built-in controllers or build a webhook like the one above. The Kubernetes docs are your friend for diving deeper. Have fun running your cluster like a boss!
Subscribe to my newsletter
Read articles from Animesh Srivastava directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Animesh Srivastava
Animesh Srivastava
I’m a DevOps engineer with over 4 years of experience in platform development and cloud-native technologies. I specialize in building and automating cloud infrastructure, designing efficient CI/CD pipelines, and writing code in Python and Golang. My work often focuses on Kubernetes, ensuring scalability, reliability, and fault-tolerance in high-availability systems. I’m passionate about tackling technical challenges, optimizing workflows, and creating robust platforms that drive operational efficiency.