This article explains how to build a Kubernetes admission controller webhook that automatically injects sidecar containers into pods based on specific annotations or labels.
Understanding the sidecar pattern
A sidecar container is a container that runs alongside the primary application container (a.k.a the application container), sharing resources such as network and storage interfaces, to enhance or extend the functionality of the primary container without modifying its core responsibilities.
What are admission controllers?
Starting in Kubernetes v1.7, alpha support for external admission controllers was introduced. These controllers allow you to add custom business logic to the Kubernetes API server to modify or validate objects as they are created.
An admission controller is a piece of code that intercepts requests to the Kubernetes API server before the persistence of the object but after the request is authenticated and authorised.
Kubernetes provides two webhook-based admission controllers:
MutatingAdmissionWebhook
: Alters (mutates) incoming requests before they are persisted.ValidatingAdmissionWebhook
: Validates incoming requests and can reject them if necessary.
There are many use cases where admission webhook/s can be helpful:
- Implementing image scanning to detect vulnerabilities in container images.
- Enforcing policies, such as requiring specific annotations or labels on resources.
- Injecting sidecar containers into pods (e.g., Istio-style proxies for service mesh functionalities).
- And many, many others…
This article focuses on the MutatingAdmissionWebhook to create a sidecar injection controller using a simple busybox-curl
sidecar container.
Setting up Your Kubernetes cluster for webhooks
First, ensure that admissionregistration.k8s.io/v1
API is enabled on your Kubernetes cluster using this command:
If the output is empty, you need to enable the API by adding the following line to the --enable-admission-plugins
flag in your Kubernetes API server configuration.
Implementation
Note: The following code snippets have been edited to fit the article format better. The complete code is available on this repo.
The sidecar injection controller requires an HTTP API server to handle webhook requests from the Kubernetes API server and mutate pod specifications accordingly.
Let’s first understand the structure of admission webhook requests and responses.
Webhook request
Kubernetes sends admission webhook requests as HTTP POST requests containing an AdmissionReview
object serialized to JSON.
In the AdmissionReview
object, the request key with the type AdmissionRequest
contains all the details for the admission request.
Webhook response
The webhook API should respond with:
- HTTP status code:
2xx
for success, non-2xx
for failure. - Body: An
AdmissionReview
object containing theAdmissionResponse
, which specifies the mutation changes as a base64-encoded array of JSON patch operations.
You can check the JSON patch documentation for more details about how it can be used to describe JSON object changes.
Mutation logic
Here’s how the mutation function works:
- Add the sidecar container: Appends a
busybox-curl
container to the pod’s containers array. - Generate the JSON Patch: Uses a library like
fast-json-patch
to create an array of JSON patch operations. - Encode the Patch: Serializes the JSON patch and encodes it as a base64 string.
Well, but how can we deploy it?
Deploying the sidecar injection webhook
The admission webhook server is deployed as a regular Kubernetes Deployment.
And to make the Deployment’s pods accessible by the Kubernetes API server, we need a Kubernetes Service.
Admission webhooks are served via HTTPS, so we need proper certificates for the server. The certificate can be self-signed and stored in a Kubernetes secret.
The certificate’s Common Name (CN) must match the service’s fully qualified domain name (e.g., <service-name>.<namespace>.svc
).
Here is an example of a Kubernetes Secret that holds the TLS certificate cert and private key.
More about generating the certificate comes later in the demo section below. Still, if you don’t want to use Helm, you can generate a self-signed certificate using the openssl CLI tool and put it manually inside a Kubernetes Secret.
Finally, the Kubernetes MutatingWebhookConfiguration defines the admission webhook and determines which objects will be processed by the webhook server.
Here are the key components of the MutatingWebhookConfiguration:
objectSelector
andrules
: Configure the webhook to apply only to pod objects labelled withsidecar.me/inject: True
during creation.clientConfig
: Defines the webhook server’s hostname (kubernetes-sidecar-injector
in thedefault
namespace) and the API endpoint (/mutation/pod).caBundle
: Specifies the PEM-encoded CA bundle used by the Kubernetes API server to validate the webhook server’s TLS certificate.
For more information on the various configurations available for the admission webhook, refer to the official Kubernetes documentation.
Demo
For the demo, we use Helm to package and deploy the following:
kubernetes-sidecar-injector
chart: Deploys the webhook server and related resources.httpbin
chart: Deploys an echo HTTP server to test sidecar injection.
I used both genSignedCert
and genCA
helm functions to generate an x509 certificate with both Subject Common Name (CN) and Subject Alternative Name (SAN) set to the service fully qualified hostname.
Let’s install both charts:
To verify the sidecar injection, list all containers in the httpbin
Deployment’s Pod, you should see an additional container named curl
.
Accessing the httpbin
HTTP server from inside the curl
container.
Woohoo! The pod has been successfully mutated to include an additional sidecar container, sharing the same network interface as the primary container.
Conclusion
Kubernetes admission controllers are powerful tools for extending cluster functionality. By implementing a custom webhook, you can add domain-specific logic to mutate or validate resources at runtime before the object state persists.
Comments