Admission Controllers Deep Dive
What Are Admission Controllers?
Admission controllers are plugins that intercept requests to the Kubernetes API server after authentication and authorization, but before the object is persisted to etcd. They can validate, mutate, or reject requests.
CKS Exam Relevance
Admission controllers are a critical security mechanism. The CKS exam may ask you to:
- Enable or disable specific admission controllers
- Configure ImagePolicyWebhook
- Create validating or mutating webhook configurations
- Understand the admission chain order
- Troubleshoot webhook failures
Admission Controller Chain
Key Ordering Rules
- Mutating webhooks run first -- they can modify the object
- Object schema validation runs next -- checks the object is valid
- Validating webhooks run last -- they can only accept or reject
- If any admission controller rejects, the entire request fails
- Mutating webhooks may run multiple times if they modify the object
Types of Admission Controllers
Built-in Admission Controllers
These are compiled into the API server binary:
| Controller | Type | Purpose |
|---|---|---|
NamespaceLifecycle | Validating | Prevents operations in terminating/non-existent namespaces |
LimitRanger | Mutating | Enforces LimitRange constraints |
ServiceAccount | Mutating | Automates SA token mounting |
DefaultStorageClass | Mutating | Assigns default storage class |
ResourceQuota | Validating | Enforces ResourceQuota limits |
PodSecurity | Validating | Enforces Pod Security Standards |
NodeRestriction | Validating | Limits kubelet's API permissions |
AlwaysPullImages | Mutating | Forces imagePullPolicy: Always |
ImagePolicyWebhook | Validating | External image admission |
EventRateLimit | Validating | Limits API event rate |
DenyServiceExternalIPs | Validating | Blocks external IPs on services |
Dynamic Admission Controllers (Webhooks)
These are user-configured and run as external services:
| Type | CRD | Can Modify Object? |
|---|---|---|
| MutatingAdmissionWebhook | MutatingWebhookConfiguration | Yes |
| ValidatingAdmissionWebhook | ValidatingWebhookConfiguration | No (accept/reject only) |
Viewing and Enabling Admission Controllers
Check Enabled Controllers
# View current admission controllers on the API server
kubectl -n kube-system get pod kube-apiserver-controlplane -o yaml | \
grep -A1 enable-admission
# Or check the API server manifest directly
cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep enable-admissionEnable Additional Controllers
Edit the API server static pod manifest:
# /etc/kubernetes/manifests/kube-apiserver.yaml
apiVersion: v1
kind: Pod
metadata:
name: kube-apiserver
namespace: kube-system
spec:
containers:
- command:
- kube-apiserver
- --enable-admission-plugins=NodeRestriction,PodSecurity,ImagePolicyWebhook
# ... other flags ...Disable a Controller
- --disable-admission-plugins=DefaultStorageClassImportant
After modifying /etc/kubernetes/manifests/kube-apiserver.yaml, the kubelet automatically restarts the API server. Wait for it to come back up:
# Watch for API server restart
watch crictl ps | grep kube-apiserver
# Or
kubectl get nodes # Will fail temporarily, then succeedSecurity-Critical Built-in Controllers
NodeRestriction
Limits what kubelets can modify. Without this, a compromised node could modify any object in the cluster.
What it restricts:
- Kubelets can only modify their own Node object
- Kubelets can only modify pods bound to their node
- Kubelets cannot modify labels with
node-restriction.kubernetes.io/prefix - Prevents compromised nodes from escalating privileges
# Always enable NodeRestriction
- --enable-admission-plugins=NodeRestrictionPodSecurity
Enforces Pod Security Standards (Baseline, Restricted, Privileged) at the namespace level. This is the replacement for the deprecated PodSecurityPolicy.
# Label a namespace to enforce restricted standards
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restrictedAlwaysPullImages
Forces imagePullPolicy: Always on all containers. This prevents using cached images that might have been modified and ensures registry authentication is always checked.
- --enable-admission-plugins=AlwaysPullImagesSecurity Benefit
Without AlwaysPullImages, a pod could use a locally cached image that was pulled by another pod with different registry credentials. This could allow unauthorized access to private images.
ImagePolicyWebhook
ImagePolicyWebhook is an admission controller that consults an external service to decide whether to allow or deny an image. This is a common CKS exam topic.
Architecture
Step 1: Create the Admission Configuration
# /etc/kubernetes/admission/admission-config.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: ImagePolicyWebhook
configuration:
imagePolicy:
kubeConfigFile: /etc/kubernetes/admission/kubeconfig.yaml
allowTTL: 50 # Cache allow decisions (seconds)
denyTTL: 50 # Cache deny decisions (seconds)
retryBackoff: 500 # Retry interval (ms)
defaultAllow: false # DENY if webhook is unreachabledefaultAllow: false
Setting defaultAllow: false means that if the webhook is unreachable, all image pulls are denied. This is the secure default for production but can break the cluster if the webhook goes down. On the CKS exam, read the question carefully to determine which setting is required.
Step 2: Create the Kubeconfig for the Webhook
# /etc/kubernetes/admission/kubeconfig.yaml
apiVersion: v1
kind: Config
clusters:
- name: image-policy-webhook
cluster:
server: https://image-policy.example.com:8443/image-policy
certificate-authority: /etc/kubernetes/admission/webhook-ca.crt
users:
- name: api-server
user:
client-certificate: /etc/kubernetes/admission/api-server-client.crt
client-key: /etc/kubernetes/admission/api-server-client.key
current-context: image-policy
contexts:
- context:
cluster: image-policy-webhook
user: api-server
name: image-policyStep 3: Configure the API Server
# /etc/kubernetes/manifests/kube-apiserver.yaml
spec:
containers:
- command:
- kube-apiserver
- --enable-admission-plugins=NodeRestriction,ImagePolicyWebhook
- --admission-control-config-file=/etc/kubernetes/admission/admission-config.yaml
volumeMounts:
- name: admission
mountPath: /etc/kubernetes/admission
readOnly: true
volumes:
- name: admission
hostPath:
path: /etc/kubernetes/admission
type: DirectoryOrCreateValidating Webhook Configuration
A ValidatingWebhookConfiguration registers an external webhook as a validating admission controller.
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: pod-validator
webhooks:
- name: pod-validator.example.com
admissionReviewVersions: ["v1"]
sideEffects: None
clientConfig:
service: # In-cluster webhook service
name: pod-validator
namespace: webhook-system
path: /validate-pods
port: 443
caBundle: <base64-encoded-CA-cert>
rules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["pods"]
scope: "Namespaced"
failurePolicy: Fail # Fail closed (reject if webhook errors)
matchPolicy: Equivalent
namespaceSelector:
matchExpressions:
- key: kubernetes.io/metadata.name
operator: NotIn
values: ["kube-system"] # Skip kube-system
timeoutSeconds: 10Key Fields Explained
| Field | Purpose | Recommended |
|---|---|---|
failurePolicy | What to do if webhook is unavailable | Fail (fail closed) |
sideEffects | Whether webhook has side effects | None |
timeoutSeconds | Max time to wait for response | 10 seconds |
namespaceSelector | Which namespaces to apply to | Exclude system namespaces |
rules | Which resources/operations to intercept | Be specific |
caBundle | CA cert to verify webhook's TLS cert | Required |
Mutating Webhook Configuration
Mutating webhooks can modify the request object. They run before validating webhooks.
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
name: pod-defaults
webhooks:
- name: pod-defaults.example.com
admissionReviewVersions: ["v1"]
sideEffects: None
clientConfig:
service:
name: pod-defaults
namespace: webhook-system
path: /mutate-pods
port: 443
caBundle: <base64-encoded-CA-cert>
rules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE"]
resources: ["pods"]
reinvocationPolicy: IfNeeded # Re-run if other mutating webhooks change object
failurePolicy: Fail
namespaceSelector:
matchExpressions:
- key: kubernetes.io/metadata.name
operator: NotIn
values: ["kube-system"]
timeoutSeconds: 10Common Mutating Webhook Use Cases
| Use Case | What It Does |
|---|---|
| Inject sidecar containers | Istio sidecar injection |
| Add default labels/annotations | Ensure compliance metadata |
| Set default resource limits | Prevent pods without limits |
| Add imagePullSecrets | Inject registry credentials |
| Modify security contexts | Enforce non-root by default |
Failure Policies
| Policy | Value | Behavior | Security |
|---|---|---|---|
| Fail Closed | Fail | Reject request if webhook is down | More secure, can cause outages |
| Fail Open | Ignore | Allow request if webhook is down | Less secure, more available |
Exam Tip
On the CKS exam, failurePolicy: Fail (fail closed) is generally the more secure choice. However, be aware that if applied to critical system namespaces, it can prevent the cluster from functioning if the webhook goes down.
Common CKS Exam Scenarios
Scenario 1: Enable an Admission Controller
# Edit the API server manifest
sudo vi /etc/kubernetes/manifests/kube-apiserver.yaml
# Add to --enable-admission-plugins
# --enable-admission-plugins=NodeRestriction,PodSecurity,ImagePolicyWebhook
# Wait for API server restart
kubectl get nodes # Retry until it worksScenario 2: Configure ImagePolicyWebhook
- Create the admission configuration file
- Create the kubeconfig file pointing to the webhook
- Add
--admission-control-config-fileto API server - Add
ImagePolicyWebhookto enabled plugins - Mount the configuration directory
- Restart and verify
Scenario 3: Debug a Failing Webhook
# Check webhook configurations
kubectl get validatingwebhookconfigurations
kubectl get mutatingwebhookconfigurations
# Check webhook endpoint health
kubectl get endpoints -n webhook-system
# Check webhook pod logs
kubectl logs -n webhook-system -l app=webhook
# Check for webhook-related events
kubectl get events --field-selector reason=FailedCreate
# Temporarily disable a webhook
kubectl delete validatingwebhookconfigurations <name>Scenario 4: Exclude a Namespace from Webhooks
# Add namespace selector to skip certain namespaces
# Most webhooks already exclude kube-system
# Add labels to namespaces you want to exclude
kubectl label namespace critical-ns skip-webhook=trueThen update the webhook configuration:
namespaceSelector:
matchExpressions:
- key: skip-webhook
operator: DoesNotExistQuick Reference
# List all admission webhook configurations
kubectl get validatingwebhookconfigurations
kubectl get mutatingwebhookconfigurations
# Describe a webhook
kubectl describe validatingwebhookconfigurations <name>
# Check enabled admission plugins
ps aux | grep kube-apiserver | grep enable-admission
# View API server manifest
cat /etc/kubernetes/manifests/kube-apiserver.yaml
# Test if a resource would be admitted
kubectl apply -f resource.yaml --dry-run=server
# Check webhook endpoint connectivity
kubectl get endpoints -n <webhook-namespace>
# View admission-related events
kubectl get events -A --field-selector reason=FailedCreate