Skip to Content
DocsKubernetesScan Images using Admission Controller

Scan images using admission controller

We can use Trivy as an admission controller to scan images before they are deployed in a Kubernetes cluster. This helps ensure that only images that meet your security policies are allowed to run in your environment.

With this approach, it might delay the deployment process because the admission controller will scan the image everytime before it is deployed and if the image is not compliant, it will block the deployment. So, the alternative way is to have your own internal registry with all pre-scanned images and use that registry in your deployment process. This way, the admission controller will only scan the images that are not in your internal registry.

Steps to scan images using Trivy with an admission controller

Step 1: Create TLS Certificates

openssl.cnf
[req] distinguished_name = req_distinguished_name req_extensions = v3_req prompt = no [req_distinguished_name] CN = trivy-service.default.svc [v3_req] keyUsage = keyEncipherment, dataEncipherment extendedKeyUsage = serverAuth subjectAltName = @alt_names [alt_names] DNS.1 = trivy-service.default.svc DNS.2 = trivy-service DNS.3 = trivy-service.default DNS.4 = localhost DNS.5 = trivy-service.default.svc.cluster.local
# Step 1: Generate a Certificate Authority (CA) # Generate the CA private key openssl genrsa -out ca.key 2048 # Generate the CA certificate openssl req -x509 -new -nodes -key ca.key -sha256 -days 365 -out ca.crt -subj "/CN=trivy-ca" # Step 2: Generate the Server Key and Certificate Signing Request (CSR) # Generate the server private key openssl genrsa -out server-key.pem 2048 # Generate the CSR using the openssl.cnf file openssl req -new -key server-key.pem -out server.csr -config openssl.cnf # Step 3: Sign the Server Certificate with the CA # Sign the server certificate openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server-cert.pem -days 365 -extensions v3_req -extfile openssl.cnf # Step 4: Base64 Encode the CA Certificate for the caBundle # Base64 encode the CA certificate cat ca.crt | base64 -w 0 # copy and replace <base64-encoded-CA-cert> in your validating-webhook.yaml file.

Step 2: Create a Webhook Service

Create a simple Flask application that will act as a webhook server. This server will receive admission review requests from the Kubernetes API server and respond with whether the image is allowed or not.

trivy-webhook.py
import subprocess from flask import Flask, request, jsonify app = Flask(__name__) @app.route('/validate', methods=['POST']) def validate(): admission_review = request.get_json() pod_spec = admission_review['request']['object']['spec']['containers'] for container in pod_spec: image = container['image'] # Scan the image using Trivy result = subprocess.run( ["trivy", "image", "--quiet", "--severity", "CRITICAL", image], stdout=subprocess.PIPE, stderr=subprocess.PIPE ) if result.returncode != 0: return jsonify({ "response": { "allowed": False, "status": { "message": f"Image {image} has critical vulnerabilities." } } }) return jsonify({"response": {"allowed": True}}) if __name__ == '__main__': context = ('/etc/webhook/certs/server-cert.pem', '/etc/webhook/certs/server-key.pem') app.run(host='0.0.0.0', port=443, ssl_context=context)

Then, create a Dockerfile for the webhook server.

Dockerfile
# Use Python as the base image FROM python:3.9-slim # Install Trivy RUN apt-get update && apt-get install -y wget \ && wget -qO- https://github.com/aquasecurity/trivy/releases/latest/download/trivy_0.45.0_Linux-64bit.tar.gz | tar zxv \ && mv trivy /usr/local/bin/ \ && apt-get clean && rm -rf /var/lib/apt/lists/* # Set the working directory WORKDIR /app # Copy the webhook service code COPY webhook.py /app/webhook.py # Install Python dependencies RUN pip install flask # Expose the webhook service port EXPOSE 443 # Run the webhook service CMD ["python", "webhook.py"]

Build the Docker image.

docker build -t trivy-webhook .

Step 3: Deploy the Webhook Service

deployment.yaml
apiVersion: apps/v1 kind: Deployment metadata: name: trivy-webhook spec: replicas: 1 selector: matchLabels: app: trivy-webhook template: metadata: labels: app: trivy-webhook spec: containers: - name: trivy-webhook image: trivy-webhook:latest ports: - containerPort: 443 volumeMounts: - name: webhook-certs mountPath: /etc/webhook/certs volumes: - name: webhook-certs secret: secretName: trivy-webhook-certs
kubectl apply -f deployment.yaml kubectl apply -f service.yaml

Step 4: Create validating webhook configuration

validating-webhook.yaml
apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration metadata: name: trivy-image-scan-webhook webhooks: - name: trivy-scan.example.com clientConfig: service: name: trivy-service namespace: default path: /validate caBundle: <base64-encoded-CA-cert> rules: - operations: ["CREATE"] apiGroups: [""] apiVersions: ["v1"] resources: ["pods"] admissionReviewVersions: ["v1"] sideEffects: None
kubectl apply -f validating-webhook.yaml

Step 5: Test the Webhook

sample-pod.yaml
apiVersion: v1 kind: Pod metadata: name: test-pod spec: containers: - name: test-container image: alpine:3.10
kubectl apply -f sample-pod.yaml

If the webhook is working correctly, the pod creation should be blocked, and you should see an error message similar to:

Error from server (Image alpine:3.10 has critical vulnerabilities.): admission webhook "trivy-scan.example.com" denied the request
Last updated on