Skip to main content
4 min read·714 words

ImagePolicyWebhook: Enforcing Image Provenance at Admission

Required knowledge for the CKS certification.

Every container that runs in a Kubernetes cluster was pulled from a registry. Without a mechanism to enforce which registries and images are permitted, an attacker who gains the ability to create pods — directly or via a compromised CI pipeline — can introduce arbitrary images, including images containing malware, backdoors, or vulnerable dependencies.

The ImagePolicyWebhook admission controller is a built-in Kubernetes control that delegates image admission decisions to an external webhook backend. The API server sends the list of container images in a pod creation request to the backend; the backend approves or denies them. This keeps image policy logic outside the cluster, where it can be maintained, updated, and audited independently.


How It Works​

ImagePolicyWebhook is a Validating admission controller. It is disabled by default and must be explicitly enabled on the API server.

When enabled, the admission controller intercepts pod creation and modification requests. For each request, it POSTs an imagepolicy.k8s.io/v1alpha1 ImageReview object to the configured webhook backend. The object contains the container image references and the pod namespace. The backend responds with status.allowed: true or status.allowed: false, and the API server either admits or rejects the pod accordingly.

The admission controller evaluates images in containers, initContainers, and ephemeralContainers. Images referenced under image volumes are outside its scope.


Enabling the Admission Controller​

Two API server flags are required:

--enable-admission-plugins=ImagePolicyWebhook
--admission-control-config-file=/etc/kubernetes/admission/admission-config.yaml

Additionally, enable the imagepolicy.k8s.io/v1alpha1 API extension group so the API server can serialize ImageReview objects:

--runtime-config=imagepolicy.k8s.io/v1alpha1=true

On a kubeadm-managed cluster, add these flags to the kube-apiserver static pod manifest at /etc/kubernetes/manifests/kube-apiserver.yaml under spec.containers[0].command.


Configuration​

AdmissionConfiguration file​

The --admission-control-config-file flag points to an AdmissionConfiguration manifest that maps the plugin to its configuration file:

apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: ImagePolicyWebhook
path: /etc/kubernetes/admission/imagepolicyconfig.yaml

Alternatively, embed the configuration inline:

apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: ImagePolicyWebhook
configuration:
imagePolicy:
kubeConfigFile: /etc/kubernetes/admission/webhook-kubeconfig.yaml
allowTTL: 50
denyTTL: 50
retryBackoff: 500
defaultAllow: false

Image policy configuration fields​

FieldTypeDescription
kubeConfigFilestringPath to a kubeconfig file describing how to connect to the backend webhook server. Required.
allowTTLintegerSeconds to cache an approval decision. Avoids repeated calls for the same image.
denyTTLintegerSeconds to cache a denial decision.
retryBackoffintegerMilliseconds to wait between retries when the backend is unreachable.
defaultAllowbooleanBehavior when the backend is unreachable or returns an error. false = fail-closed (deny the pod). true = fail-open (allow the pod).

Use defaultAllow: false in production. A fail-open policy defeats the purpose of the admission control — if the webhook backend is unavailable, pods with unapproved images will be admitted. Reserve defaultAllow: true only for environments where the operational risk of blocking all pod creation during a webhook outage outweighs the security risk of temporarily bypassing image policy.

Webhook backend kubeconfig​

The image policy configuration references a standard kubeconfig file. The backend must serve over HTTPS. The API server presents a client certificate to authenticate to the backend.

clusters:
- name: image-policy-backend
cluster:
certificate-authority: /etc/kubernetes/admission/backend-ca.pem
server: https://image-policy.example.internal/policy

users:
- name: kube-apiserver
user:
client-certificate: /etc/kubernetes/admission/apiserver-client.pem
client-key: /etc/kubernetes/admission/apiserver-client-key.pem

ImageReview Request and Response​

The API server sends a POST request containing an imagepolicy.k8s.io/v1alpha1 ImageReview object to the backend:

{
"apiVersion": "imagepolicy.k8s.io/v1alpha1",
"kind": "ImageReview",
"spec": {
"containers": [
{ "image": "registry.example.com/app:v1.4.2" },
{ "image": "registry.example.com/sidecar@sha256:beb6bd6a68f114c1dc2ea4b28db81bdf91de202a9014972bec5e4d9171d90ed" }
],
"annotations": {
"mycluster.image-policy.k8s.io/ticket-1234": "break-glass"
},
"namespace": "production"
}
}

A permissive response from the backend:

{
"apiVersion": "imagepolicy.k8s.io/v1alpha1",
"kind": "ImageReview",
"status": {
"allowed": true
}
}

A denial response with a reason:

{
"apiVersion": "imagepolicy.k8s.io/v1alpha1",
"kind": "ImageReview",
"status": {
"allowed": false,
"reason": "image registry not in approved list"
}
}

The reason field is surfaced in the pod creation error message returned to the caller.


Extending Policy with Annotations​

Any pod annotation whose key matches the pattern *.image-policy.k8s.io/* is forwarded to the webhook backend in the ImageReview.spec.annotations field. This allows operators to pass hints to the backend without modifying the core admission protocol.

Common uses include:

  • Break-glass overrides: an annotation carrying a ticketing system reference allows the backend to conditionally admit images during declared emergencies.
  • Image ID hints: the backend can skip a registry lookup if the caller provides the resolved image digest in an annotation.

The backend is responsible for validating annotations — Kubernetes does not validate them.


Scope and Limitations​

What ImagePolicyWebhook covers: containers, initContainers, and ephemeralContainers fields in the pod specification.

What it does not cover: images referenced under image volumes. If your workloads use image volumes, enforce image policy for those references through a separate mechanism such as a ValidatingAdmissionPolicy or Kyverno policy.

Alpha API: The ImageReview API is currently at imagepolicy.k8s.io/v1alpha1. Alpha APIs can change between Kubernetes versions. Review the API stability guarantees and the admission controller documentation for each Kubernetes release before upgrading.


Verification​

After enabling the admission controller, verify that unapproved images are rejected:

kubectl run test-denied \
--image=docker.io/library/ubuntu:latest \
--restart=Never \
--dry-run=server

If the webhook backend denies the image, the API server returns an error similar to:

Error from server: admission webhook "image-policy-backend" denied the request: image registry not in approved list

Confirm that approved images still admit correctly:

kubectl run test-allowed \
--image=registry.example.com/app:v1.4.2 \
--restart=Never \
--dry-run=server

A successful --dry-run=server response confirms the image passes the backend policy without creating the pod.


References​

This article is based on information from the following official sources:

  1. Admission Controllers Reference — ImagePolicyWebhook - Kubernetes Documentation
  2. Admission Controllers Reference — Enabling Plugins - Kubernetes Documentation
  3. imagepolicy.v1alpha1 API - Kubernetes Documentation