Building a Kubernetes Admission Webhook
Aditya Joshi
Senior Software Engineer @ Walmart | Blockchain, Hyperledger, Kubernetes | Lead Dev Advocate @Hyperledger | CKS | CKA | CKAD
Kubernetes admission webhooks are powerful tools that allow you to enforce custom policies on the objects being created or modified in your cluster.
They come in two flavors: validating and mutating webhooks. Validating webhooks enforce policies by accepting or rejecting requests, while mutating webhooks modify objects before they are persisted.
In this blog, we will walk through building a mutating admission webhook in Kubernetes based on the repository . We'll cover everything from setting up the webhook to deploying it on a Kubernetes cluster.
What is a Mutating Admission Webhook?
A mutating admission webhook intercepts API requests before they reach the Kubernetes API server and allows you to modify or "mutate" the objects in the requests. For example, you might want to add default labels, inject sidecar containers, or adjust resource limits.
Prerequisites
Before we begin, ensure you have the following:
Step 1: Setting Up Your Development Environment
First, clone the repository:
git clone https://github.com/adityajoshi12/kubernetes-development.git
cd kubernetes-development/mutating-webhook
This repository contains a basic structure for a mutating admission webhook written in Go.
Step 2: Writing the Mutating Logic
The core logic for a mutating webhook resides in a Go function that processes the admission review requests. The repository provides a sample implementation in main.go:
package main
import (
"encoding/json"
"fmt"
"net/http"
admissionv1 "k8s.io/api/admission/v1"
)
func handleMutate(w http.ResponseWriter, r *http.Request) {
var admissionReview admissionv1.AdmissionReview
err := json.NewDecoder(r.Body).Decode(&admissionReview)
if err != nil {
http.Error(w, "Could not decode body", http.StatusBadRequest)
return
}
admissionResponse := &admissionv1.AdmissionResponse{
Allowed: true,
}
// Implement your mutation logic here
if admissionReview.Request.Kind.Kind == "Pod" {
// Example: add a label to all created pods
patch := `[{"op":"add","path":"/metadata/labels/mutated","value":"true"}]`
admissionResponse.Patch = []byte(patch)
patchType := admissionv1.PatchTypeJSONPatch
admissionResponse.PatchType = &patchType
}
admissionReview.Response = admissionResponse
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(admissionReview)
}
func main() {
http.HandleFunc("/mutate", handleMutate)
fmt.Println("Starting server on :8080")
http.ListenAndServeTLS(":8080", "/etc/webhook/certs/tls.crt", "/etc/webhook/certs/tls.key", nil)
}
In this example, we intercept all Pod creation requests and add a label mutated=true to each pod. The mutation is applied using a JSON Patch.
Step 3: Creating Certificates
For the Kubernetes API server to communicate securely with your webhook, you need to create TLS certificates. You can use tools like openssl or cfssl for this.
Generate a self-signed certificate:
openssl req -newkey rsa:2048 -nodes -keyout tls.key -x509 -days 365 -out tls.crt -subj "/CN=webhook.default.svc"
These certificates need to be stored in a Kubernetes secret that will be mounted to the webhook server pod.
kubectl create secret tls webhook-certs --cert=tls.crt --key=tls.key -n default
领英推荐
Step 4: Deploying the Webhook Server
We need to create a Kubernetes deployment and a service for the webhook server.
Deployment YAML:
apiVersion: apps/v1
kind: Deployment
metadata:
name: mutating-webhook
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: mutating-webhook
template:
metadata:
labels:
app: mutating-webhook
spec:
containers:
- name: mutating-webhook
image: yourdockerrepo/mutating-webhook:latest
ports:
- containerPort: 8080
volumeMounts:
- name: webhook-certs
mountPath: "/etc/webhook/certs"
readOnly: true
volumes:
- name: webhook-certs
secret:
secretName: webhook-certs
Service YAML:
apiVersion: v1
kind: Service
metadata:
name: mutating-webhook
namespace: default
spec:
ports:
- port: 443
targetPort: 8080
selector:
app: mutating-webhook
Deploy these resources:
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
Step 5: Configuring the MutatingWebhookConfiguration
Now, configure Kubernetes to call your webhook whenever a pod is created. You do this by creating a MutatingWebhookConfiguration:
apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
name: pod-mutating-webhook
webhooks:
- name: pod-mutating-webhook.default.svc
clientConfig:
service:
name: mutating-webhook
namespace: default
path: "/mutate"
caBundle: <CA_BUNDLE>
rules:
- operations: ["CREATE"]
apiGroups: [""]
apiVersions: ["v1"]
resources: ["pods"]
admissionReviewVersions: ["v1"]
sideEffects: None
Make sure to replace <CA_BUNDLE> with the base64 encoded CA certificate.
You can obtain the CA certificate with:
kubectl get secret webhook-certs -o jsonpath='{.data.tls\.crt}' | base64 -d | base64 -w0
Apply the webhook configuration:
kubectl apply -f webhook-configuration.yaml
Step 6: Testing the Webhook
With everything in place, create a test pod and check if it has the label mutated=true.
kubectl run test-pod --image=nginx
kubectl get pod test-pod -o jsonpath='{.metadata.labels}'
You should see the output indicating the pod has the mutated label added by your webhook.
Conclusion
In this blog, we've built a simple mutating admission webhook for Kubernetes. We've covered writing the webhook logic, creating necessary certificates, deploying the webhook, and configuring Kubernetes to use it. This serves as a foundation to build more complex webhooks to enforce your organization's policies dynamically.
Next Steps