Use a ValidatingAdmissionPolicy with a CEL expression that checks every container image against an approved registry prefix, then bind it with Warn and Audit before switching to Deny. It runs inside the API server, needs no webhook, and is stable since Kubernetes v1.30.
Definition: a registry allowlist
A registry allowlist is a rule that says a pod may only run images whose reference starts with a registry you control or trust. It does not prove an image is safe. It narrows where images can come from, so an attacker or a tired developer cannot pull a lookalike from a public registry by changing one line.
Step 1: write the policy
The Kubernetes documentation describes ValidatingAdmissionPolicy as a declarative, in process alternative to validating webhooks, using the Common Expression Language. A starting point for pods looks like this. The registry name is a placeholder.
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicy
metadata:
name: approved-registries
spec:
failurePolicy: Fail
matchConstraints:
resourceRules:
- apiGroups: [""]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["pods"]
validations:
- expression: "object.spec.containers.all(c, c.image.startsWith('registry.example.com/'))"
message: "Images must come from registry.example.com"
- expression: "object.spec.?initContainers.orValue([]).all(c, c.image.startsWith('registry.example.com/'))"
message: "Init containers must also use registry.example.com"
Do not forget init containers. A check on containers alone leaves a side door. Ephemeral containers are a third place images appear, so decide whether your rule should cover them too.
Step 2: bind it in warning mode
A policy does nothing until a binding names it. The binding carries validationActions, and the documentation lists Deny, Warn and Audit.
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionPolicyBinding
metadata:
name: approved-registries-warn
spec:
policyName: approved-registries
validationActions: [Warn, Audit]
Run for a week. The audit events show which workloads would have been refused. Fix those, then change the action to Deny. Optionally add a namespaceSelector to the binding so you can start with one environment.
Step 3: test before and after
Test with a pod that should pass and one that should fail, using a server side dry run so the policy actually evaluates.
| Test case | Expected result |
|---|---|
| Image from the approved registry | Accepted |
| Image from a public registry | Warned, later denied |
| Approved main container, unapproved init container | Warned, later denied |
| Image reference with a tag but no registry | Denied, since it resolves to a default registry |
Matching only pods has a catch
Because the rule matches pods, a Deployment with a bad image is created fine, and the refusal shows up when its ReplicaSet tries to create pods. People then look at the Deployment and see nothing wrong. Teach your team to check the ReplicaSet events. You can also add rules that match Deployments directly for earlier feedback, at the cost of maintaining more expressions.
Prefix checks have limits
A startsWith check is simple, which is its strength. Be aware that it says nothing about tags, signatures or content. Pair it with digest pinning and signature verification if you need to know the exact bytes that run.
Where Safeguard fits
Safeguard's container image scanning and policies, gates and guardrails look at what is inside an image before it ships, which complements a rule about where it comes from. A free scan is a quick way to see what a first look turns up.
Try it yourself
Click through it at your own pace, or open it full screen. Look at how an image from an approved source can still carry findings.
Quick check
Does a registry allowlist make images safe?
No. It limits where images come from. You still need scanning, pinning and signature checks for what is inside.
Should I use a webhook or ValidatingAdmissionPolicy?
For simple field checks like this one, a ValidatingAdmissionPolicy avoids running a separate service. Use a webhook when you need logic CEL cannot express.
Sources
Last reviewed: 19 October 2026.