Kyverno has been the broad-spectrum Kubernetes policy engine of choice for many platform teams since the project hit 1.0 in 2021. In 2026 it shipped a dedicated policy type for the image-verification path — ImageValidatingPolicy — that consolidates what used to live across ClusterPolicy verifyImages rules into a single, cleaner resource. We migrated a 60-cluster fleet from the legacy ClusterPolicy approach to ImageValidatingPolicy over three months and tracked the differences in policy author ergonomics, admission performance, and audit clarity.
What is ImageValidatingPolicy and why does it exist?
Kyverno's image verification has long lived inside the generic ClusterPolicy resource via a verifyImages rule type. That worked but produced policies that mixed image checks with mutation and validation logic, which made the resulting YAML hard to audit. ImageValidatingPolicy is a focused CRD that does only image verification — signatures, attestations, SBOM checks, registry restrictions, image digest mutation — and it pairs with an Admission Reports CRD to record decisions. The policy type also introduces named verification stages (signature, attestation, mutation) so the order of checks is explicit in the YAML rather than implicit in Kyverno's internal pipeline.
The migration from verifyImages-in-ClusterPolicy to ImageValidatingPolicy is mechanical for most rules; the structural change is mostly that you stop mixing image checks with mutateExisting or generate rules. Kyverno's docs recommend a transition window where both run in audit mode; ImageValidatingPolicy is enforced and the old ClusterPolicy is left in audit-only to catch any gaps before retirement.
How does the new policy type compare to Sigstore Policy Controller?
Both projects enforce cosign signatures and attestations at admission. The 2026 honest comparison.
| Capability | Kyverno ImageValidatingPolicy | Sigstore Policy Controller v0.15 | |---|---|---| | Cosign keyless / Fulcio | Yes | Yes | | Sigstore bundle v0.3 | Partial | Yes | | Notary v2 / notation | Yes | No | | SLSA attestation policy language | Rego, JMESPath, CEL | CUE | | Image digest mutation | Yes (default) | No | | Sigstore TUF delegation | Limited | Yes (v0.15) | | Other Kyverno features (gen, mutate) | Yes | No | | Policy author familiarity | High (Kyverno is widely deployed) | Lower (CUE) |
If your team already runs Kyverno for broader policy, ImageValidatingPolicy is the lowest-friction option. If you are Sigstore-deep and want the project that tracks cosign most closely, Policy Controller is the choice. Many of the largest enterprise deployments we see run both — Kyverno for the policy program at large, Policy Controller for the canonical Sigstore enforcement path.
What does a real ImageValidatingPolicy look like?
A representative policy that requires a cosign signature, a SLSA provenance attestation, an SBOM attestation, and mutates the image reference to its digest.
apiVersion: policies.kyverno.io/v1alpha1
kind: ImageValidatingPolicy
metadata:
name: prod-image-verification
spec:
validationActions: [Deny]
matchConditions:
- name: production-namespace
expression: "object.metadata.namespace in ['payments', 'identity', 'web']"
images:
- registry.example.com/prod/*
mutateDigest: true # default; replaces tag with digest
verifyDigest: true
required: true
attestors:
- name: corporate-keyless
keyless:
roots:
- kind: Secret
name: corporate-fulcio-root
namespace: kyverno
subject: "https://github.com/example/*"
issuer: "https://token.actions.githubusercontent.com"
rekor:
url: https://rekor.example.com
attestations:
- name: slsa-provenance
predicateType: https://slsa.dev/provenance/v1
conditions:
all:
- key: "{{ predicate.buildDefinition.buildType }}"
operator: Equals
value: "https://example.com/build-types/github-actions@v1"
- name: cyclonedx-sbom
predicateType: https://cyclonedx.org/schema
conditions:
all:
- key: "{{ predicate.metadata.tools.components[?name=='cdxgen'].version }}"
operator: NotEquals
value: ""
The two interesting bits: mutateDigest: true replaces the human-readable tag with the verified digest, which prevents image-tag spoofing later in the pod lifecycle, and the SBOM attestation condition checks that the SBOM was actually produced by a recognized generator (cdxgen, syft, or whatever your build pipeline standardizes on).
How fast is admission with ImageValidatingPolicy?
We measured admission latency on a 12-node test cluster with 800 deployments cycling through, comparing the legacy ClusterPolicy verifyImages path against the new ImageValidatingPolicy.
| Path | p50 | p95 | p99 | |---|---|---|---| | ClusterPolicy verifyImages (1.17) | 158ms | 287ms | 412ms | | ImageValidatingPolicy (1.18) | 121ms | 198ms | 312ms |
The 25% reduction is real and traces mostly to the elimination of the validate/mutate phase intermingling that used to add a second pass through the engine for image references. The bigger operational win is that admission reports now carry a stable policy: ImageValidatingPolicy/<name> annotation so audit log queries find them cleanly — previously you had to grep across heterogeneous ClusterPolicy decision blobs.
What does migration look like for an existing Kyverno deployment?
A four-step pattern that worked across the fleet. First, install Kyverno 1.18 with both engines enabled but ImageValidatingPolicy in audit-only. Second, port your existing verifyImages ClusterPolicy rules into ImageValidatingPolicy resources, keeping both running in parallel for 14 days to compare decisions. Third, switch ImageValidatingPolicy to enforce mode and move the ClusterPolicy verifyImages rules to audit-only as a safety net. Fourth, after 30 days of clean enforcement, remove the legacy rules. In our migration the parallel-decision compare found nine policy gaps where the old rules accepted images that the new policy would have blocked — mostly around attestation requirements that the legacy syntax didn't express cleanly. These were real coverage improvements, not regressions.
What does Kyverno still get wrong?
Two complaints carry over from earlier versions. First, signature verification against an unreachable Rekor instance fails closed by default and can take Kyverno's grace period to surface — if your Rekor is the public sigstore.dev instance and there's a network blip, deployments will stall. Build a local Rekor mirror or set a sensible timeout. Second, the Notary v2 (notation) integration is functional but lags cosign in feature parity; if you need notation-signed images at scale, expect to wait on a few rough edges through 2026.
How do you handle exception management in 2026?
ImageValidatingPolicy ships with a built-in exception model — PolicyException resources scope which images are exempt from which checks. The 2026 best practice is to make exceptions narrowly scoped, time-bound, and reviewed. A typical exception we landed on: a vendor image that shipped without cosign signatures gets a PolicyException matching that exact image digest, valid for 45 days, with a Jira ticket referenced in the resource annotations. When the 45 days pass, the exception expires and the next pod admission triggers the policy refusal — which usually causes the vendor to either ship a signed image or for the platform team to wrap the unsigned image with their own re-attestation pipeline. The pattern that does not scale: blanket exceptions for entire registries. Once you say "everything from gcr.io/google_containers/* is exempt," your policy is theatre. Narrow scope, short duration, documented justification.
How Safeguard Helps
Safeguard sits below the admission layer as the system of record. ImageValidatingPolicy decisions stream into Safeguard's policy timeline, joined with the SBOM and attestation evidence so an admitted pod has a single auditable chain: signed by which identity, attested with which build type, with which SBOM, against which policy version. Griffin AI watches for policy drift — when a service's attestation predicate type changes without a corresponding policy update, Safeguard flags the mismatch before it becomes a stale-policy gap. The platform also acts as the resolver across multiple admission engines: a fleet running both Kyverno and Sigstore Policy Controller produces two streams that Safeguard normalizes into one. Kyverno enforces at admission; Safeguard makes it explainable to auditors and customers.