CKA Certification Course - Certified Kubernetes Administrator
Scheduling
Solution Validating and Mutating Admission Controllers 2025 Updates
In this lesson, we will explore the lab on validating and mutating admission controllers. You will learn how these controllers work together, create a namespace and TLS secret, deploy the webhook server with corresponding configurations, and test pod security contexts.
Determining the Correct Controller Actions
The first step in this lab is to understand which admission controller action is mutating and which one is validating. Consider the following:
- The "namespace auto-provision" admission controller is mutating because it automatically creates and modifies a namespace's configuration.
- The "namespace exists" admission controller is validating as it solely checks for the existence of a namespace.
Thus, the correct pairing is:
- Mutating: Namespace auto-provision
- Validating: Namespace exists
Remember, the admission controller invocation sequence always applies mutations first and then validations.
Creating the Namespace
Next, create a namespace called webhook-demo
by running:
root@controlplane ~ ➜ kubectl create ns webhook-demo
Verify the creation of the namespace with:
root@controlplane ~ ➜ kubectl get ns
NAME STATUS AGE
default Active 37m
kube-node-lease Active 37m
kube-public Active 37m
kube-system Active 37m
webhook-demo Active 3s
Creating the TLS Secret
For secure webhook communication, you need to create a TLS secret named webhook-server-tls
in the webhook-demo
namespace. This secret uses a certificate and key from specific file paths:
root@controlplane ~ ➜ kubectl -n webhook-demo create secret tls webhook-server-tls \
--cert "/root/keys/webhook-server-tls.crt" \
--key "/root/keys/webhook-server-tls.key"
You should see the confirmation:
secret/webhook-server-tls created
Deploying the Webhook Deployment
Deploy the webhook server next using the provided deployment definition in webhook-deployment.yaml
. First, you can inspect the file with:
cat webhook-deployment.yaml
Then, apply the configuration:
root@controlplane ~ ➜ kubectl apply -f webhook-deployment.yaml
The expected output should be similar to:
deployment.apps/webhook-server created
Deploying the Webhook Service
A service definition for the webhook server is provided in the webhook-service.yaml
file. Deploy it by running:
root@controlplane ~ ➜ kubectl apply -f webhook-service.yaml
This command creates the service necessary for the webhook server to operate.
Deploying the Mutating Webhook Configuration
Next, configure the mutating webhook with the settings provided in webhook-configuration.yaml
. This file defines a webhook that intercepts pod creation events. For instance, it uses the following rule to match create operations for pods:
webhooks:
- name: webhook-server.webhook-demo.svc
clientConfig:
service:
name: webhook-server
namespace: webhook-demo
path: "/mutate"
caBundle: L0s0tLS0tcj1jSURqekNDQ1Z0F3SUJBZ0lTmt1QW5SxkbkhmU3o1SctFU3g5Z1lDQ2Nnd0RRNUp1b1pJaHzjkFRRUWkQ1FBDx6XRNRQ3NHQTFVRUF3d2tRV1JvYh0emF0XYUJR2Ym5SeWlY...
rules:
- operations: [ "CREATE" ]
apiGroups: [ "" ]
apiVersions: [ "v1" ]
resources: [ "pods" ]
Apply this configuration using:
root@controlplane ~ ➜ kubectl apply -f webhook-configuration.yaml
You might receive a deprecation warning:
Warning
Warning: admissionregistration.k8s.io/v1beta1 MutatingWebhookConfiguration is deprecated in v1.16+ and will be unavailable in v1.22+. Please use admissionregistration.k8s.io/v1 MutatingWebhookConfiguration.
The webhook is designed to reject any pod request that attempts to run as root without a proper security context. If no runAsNonRoot
value is specified, the webhook mutates the pod by setting runAsNonRoot
to true and defaults the user ID to 1234. However, if the security context explicitly sets runAsNonRoot
to false, the webhook will not override it.
Testing the Default Pod Security Context
First, test the webhook by deploying a pod without a security context. The file pod-with-defaults.yaml
contains a pod definition that, by default, would run as root. However, the webhook mutates it:
# A pod with no securityContext specified.
# Without the webhook, it would run as user root (0). The webhook mutates it.
apiVersion: v1
kind: Pod
metadata:
name: pod-with-defaults
labels:
app: pod-with-defaults
spec:
restartPolicy: OnFailure
containers:
- name: busybox
image: busybox
command: ["sh", "-c", "echo I am running as user $(id -u)"]
Apply the configuration:
root@controlplane ~ ➜ kubectl apply -f pod-with-defaults.yaml
After deployment, inspect the pod configuration (or use kubectl describe pod pod-with-defaults
) to confirm that the security context has been mutated — runAsNonRoot
should be true and runAsUser
should be set to 1234.
Deploying a Pod with an Explicit Security Context
Now, deploy a pod that explicitly permits running as root. The file pod-with-override.yaml
contains the configuration that sets runAsNonRoot
to false, preventing the webhook from applying its defaults:
# A pod with a securityContext explicitly allowing it to run as root.
# The effect of deploying this with and without the webhook is the same.
# The explicit setting prevents the webhook from applying more secure defaults.
apiVersion: v1
kind: Pod
metadata:
name: pod-with-override
labels:
app: pod-with-override
spec:
restartPolicy: OnFailure
securityContext:
runAsNonRoot: false
containers:
- name: busybox
image: busybox
command: ["sh", "-c", "echo I am running as user $(id -u)"]
Deploy the pod with:
root@controlplane ~ ➜ kubectl apply -f pod-with-override.yaml
For further reference, you can view the content of pod-with-conflict.yaml
with:
cat pod-with-conflict.yaml
Deploying a Pod with a Conflicting Security Context
The final test involves deploying a pod with a conflicting security context. In this scenario, the pod configuration requires the container to run as a non-root user by setting runAsNonRoot
to true while explicitly requesting a user ID of 0 (root). Without the webhook, this could lead to a CreateContainerConfigError
. With the webhook enabled, the pod creation is rejected:
# A pod with a conflicting securityContext setting:
# It has to run as a non-root user, but we explicitly request a user id of 0 (root).
# Without the webhook, the pod could be created but would fail to launch due to an unenforceable
# security context, leading to a 'CreateContainerConfigError' status.
# With the webhook, the creation of this pod is outright rejected.
apiVersion: v1
kind: Pod
metadata:
name: pod-with-conflict
labels:
app: pod-with-conflict
spec:
restartPolicy: OnFailure
securityContext:
runAsNonRoot: true
runAsUser: 0
containers:
- name: busybox
image: busybox
command: ["sh", "-c", "echo I am running as user $(id -u)"]
Attempt to deploy the conflicting pod:
root@controlplane ~ ➜ kubectl apply -f pod-with-conflict.yaml
The expected error message should be similar to:
Error from server: error when creating "pod-with-conflict.yaml": admission webhook "webhook-server.webhook-demo.svc" denied the request: runAsNonRoot specified, but runAsUser set to 0 (the root user)
This confirms that the webhook correctly rejected the pod due to the conflicting security settings.
Conclusion
In this lab, you learned how to:
- Distinguish between mutating and validating admission controllers.
- Create a namespace and configure TLS for secure webhook communication.
- Deploy a webhook server along with its service and webhook configurations.
- Test the webhook behavior by deploying pods with default, overridden, and conflicting security contexts.
By following these steps, you ensure that your Kubernetes resources adhere to the required security policies enforced by admission controllers.
Watch Video
Watch video content