Building an HTTP Validator Webhook for Kubernetes using Python
Introduction
A validator webhook in Kubernetes is an HTTP callback that intercepts admission requests and can either allow or reject them based on custom logic. Now we will see how to build a validator webhook for Kubernetes using Python and Flask over HTTP. This webhook will validate that container images in new deployments start with the prefix “msalinasc_” (excluding the registry URL). We will cover the deployment of the webhook, creation of the Kubernetes Service, and implementation of the ValidatingWebhookConfiguration resource.
Prerequisites
To follow along with this tutorial, you should have:
- Basic knowledge of Kubernetes, Python, and Flask
- A running Kubernetes cluster with
kubectl
configured - Python 3 installed on your local machine
Step 1: Implement the validator webhook using Flask
First, create a new Python script, validator_webhook.py
, and add the following code:
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route("/validate", methods=["POST"])
def validate():
request_data = request.get_json()
# Extract the container image name from the request data
container_image = request_data["request"]["object"]["spec"]["template"]["spec"]["containers"][0]["image"]
# Extract the image name without the registry URL
image_name = container_image.split("/")[-1]
# Check if the image name starts with "msalinasc_"
if image_name.startswith("msalinasc_"):
return jsonify({"response": {"allowed": True}})
else:
return jsonify({"response": {"allowed": False, "status": {"message": "Invalid image name"}}})
if __name__ == "__main__":
app.run(host="0.0.0.0", port=80)
Step 2: Create a Dockerfile for the webhook
Create a Dockerfile
in the same directory as your validator_webhook.py
script with the following content:
FROM python:3.9
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY validator_webhook.py .
CMD ["python", "/app/validator_webhook.py"]
Create a requirements.txt
file that includes the required libraries:
flask==2.1.1
Step 3: Build and push the Docker image
Build the Docker image and push it to a container registry accessible by your Kubernetes cluster:
$ docker build -t <your-registry>/<your-repo>/validator-webhook:v1 .
$ docker push <your-registry>/<your-repo>/validator-webhook:v1
Step 4: Deploy the webhook as a Kubernetes Deployment
Create a new YAML file, validator-webhook-deployment.yaml
, with the following content:
apiVersion: apps/v1
kind: Deployment
metadata:
name: validator-webhook
spec:
replicas: 1
selector:
matchLabels:
app: validator-webhook
template:
metadata:
labels:
app: validator-webhook
spec:
containers:
- name: validator-webhook
image: <your-registry>/<your-repo>/validator-webhook:v1
imagePullPolicy: Always
ports:
- containerPort: 80
name: webhook
Replace <your-registry>
and <your-repo>
with the appropriate values for your container registry. Apply the deployment to your cluster:
$ kubectl apply -f validator-webhook-deployment.yaml
Step 5: Create a Kubernetes Service for the webhook
Create a new YAML file, validator-webhook-service.yaml
, with the following content:
apiVersion: v1
kind: Service
metadata:
name: validator-webhook-service
spec:
selector:
app: validator-webhook
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP
Apply the Service to your cluster:
$ kubectl apply -f validator-webhook-service.yaml
Step 6: Create the ValidatingWebhookConfiguration resource
Create a new YAML file, validator-webhook-config.yaml
, with the following content:
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: validator-webhook-config
webhooks:
- name: validator-webhook.default.svc
clientConfig:
service:
name: validator-webhook-service
namespace: default
path: "/validate"
rules:
- operations: ["CREATE", "UPDATE"]
apiGroups: ["apps"]
apiVersions: ["v1"]
resources: ["deployments"]
admissionReviewVersions: ["v1"]
sideEffects: None
timeoutSeconds: 5
failurePolicy: Fail
Apply the configuration to your cluster:
$ kubectl apply -f validator-webhook-config.yaml
If the rule defined in the ValidatingWebhookConfiguration
is not met, the user will receive an error message when trying to create or update a deployment. In our specific case, the error message returned is defined in the validator webhook code:
return jsonify({"response": {"allowed": False, "status": {"message": "Invalid image name"}}})
The error message would appear as follows:
Error from server (Invalid image name): error when creating "deployment.yaml": admission webhook "validator-webhook.default.svc" denied the request: Invalid image name
This message informs the user that the deployment request was denied by the admission webhook due to an invalid image name, and they should use a properly named image according to the defined rule in the webhook.