Building a Kubernetes Admission Webhook

Building a Kubernetes Admission Webhook

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:

  • A running Kubernetes cluster (v1.16+).
  • kubectl configured to interact with your cluster.
  • A valid HTTPS endpoint for the webhook (you can use self-signed certificates for testing).


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

  • Advanced Mutations: Extend the webhook logic to perform more advanced mutations, like injecting sidecars or setting resource requests/limits.
  • Error Handling: Improve error handling and logging in your webhook.
  • Scaling: Consider scaling your webhook server and hardening it for production environments.

要查看或添加评论,请登录

社区洞察

其他会员也浏览了