Your build system produces an application image, but how do you stop a cluster from running a different image or one published by an unapproved account? An image signature lets you check who signed a particular image. A signature policy can then require that identity before Kubernetes accepts a deployment.
Cosign, from the Sigstore project, signs and verifies container images and related statements. Kyverno is a Kubernetes policy engine that can check those signatures when a matching request reaches the cluster. That request-time check is called admission. An attestation is a signed statement about an image, such as the result of a build or scan; the policy must check its contents as well as its signer.
Use signature verification when you need to allow only images from approved publishers or build systems. Add attestation checks when deployment also depends on a specific build or scan result. A valid signature does not prove that software has no vulnerabilities. This guide explains the policy and a staged Kyverno migration; its proposed allow, deny and outage cases have not been executed by Kubedex.
Separate the three assertions
| Assertion | Evidence to require | Boundary |
|---|---|---|
| The image came from an approved publishing identity | A verified signature for the exact digest, from the allowed key or OIDC issuer and build identity. | A trusted signer can still publish vulnerable software. |
| The digest passed your release checks | A trusted attestation whose subject, predicate type and policy fields meet your rules. | The existence of a signed scan report is insufficient if its contents show failure or its scan is stale. |
| This Kubernetes request is permitted | A matching policy that covers the workload and all required image fields, with tested exception and failure behavior. | An unmatched request or an overly broad exception can avoid the intended check. |
Build the scan decision using the container-scanning workflow. Keep the identity allowed to publish release evidence separate from an arbitrary developer identity that can sign an image. Sigstore's verification documentation shows the identity and OIDC issuer checks for keyless signatures. Avoid a trust rule that accepts every identity from a public issuer.
Write down which images and signers are allowed
For each workload cohort, record its permitted registries/repositories, immutable image references, build identity or key, required attestation types, acceptable evidence age, exception owner and outage behavior. Make the allowed identity narrow enough to distinguish the intended repository and release workflow from an unrelated workflow using the same issuer. Plan rotation of keys and trust roots alongside workload rollout.
For a scan attestation, decide which producer is trusted to assert the result and define the required fields: subject digest, scan time, scanner and database identity, policy version, outcome and any exception reference. Those are requirements for your attestation contract; they are not a promise that every Trivy output format or example already carries them. Validate the actual signed payload your pipeline emits.
ImageValidatingPolicy supports image-signature and attestation verification, registry credentials and digest-related configuration. Its examples illustrate mechanisms, not your complete trust contract. Where an example checks only that an attestation signature verifies, add the content conditions your release decision requires. An expired report signed by an approved identity must not accidentally satisfy a freshness requirement.
Review selection and rejection separately. matchImageReferences selects images for verification; selecting your own registry is not a complete policy for images outside that selection. Require an explicit rejection of unapproved registries or repositories across the protected workload scope, and test an image from outside the verification pattern. Also check custom resources: their image fields may need explicit extraction rather than the automatic handling for Pods and Pod templates.
Kyverno 1.19 changes the migration path
The 20 August 2026 release announcement deprecates legacy ClusterPolicy/Policy and related legacy types, with removal planned for 1.20, estimated for November 2026. Image-verification rules move to ImageValidatingPolicy in the policies.kyverno.io group. This is a change in policy structure and expressions, not a rename of apiVersion.
| Legacy responsibility | CEL policy destination | Review during migration |
|---|---|---|
verifyImages | ImageValidatingPolicy | Image selection, attestors, attestations, digest settings and expressions. |
| Validation, mutation and generation rules in a mixed policy | Separate ValidatingPolicy, MutatingPolicy and GeneratingPolicy resources | Each rule's match scope, preconditions, variables and behavior. |
| Legacy cleanup policies | DeletingPolicy | Selection and deletion behavior, independent of image verification. |
| Legacy policy exceptions | PolicyException in policies.kyverno.io | Rebuild narrowly scoped exceptions against the new policies and validate their effect. |
Use the field-by-field migration guide for the exact mappings. A legacy deny condition may require logic inversion in a validation expression. Match/exclude behavior, background evaluation and exception semantics deserve their own tests. Do not infer equivalent behavior because both policy files are accepted by the API server.
Also record the controller, CLI, chart and CRD versions. The 1.19 announcement moves CRD management into a dedicated kyverno-api chart dependency and distinguishes the current storage API version from the planned 1.20 version. Rolling documentation may show an API version different from your installed CRDs. Check the supported release matrix and upgrade procedure before choosing an upgrade sequence.
Migrate one workload cohort at a time
- Inventory the effective policies. Include policy resources, exceptions, admission settings, registry secrets, external trust dependencies and the workload owners affected by denial. Preserve the known-working configuration.
- Translate and test offline. Reuse existing Kyverno CLI test cases where applicable and add the negative cases below. Record expected outcomes for both the old and new policy.
- Exercise a disposable cluster or namespace. Use the chosen Kubernetes/Kyverno versions and the real registry/authentication pattern. Include Deployments and other controllers that create Pods, not only hand-written Pod examples.
- Observe the candidate policy. Keep the current protection while evaluating the new policy on a controlled scope. Compare mismatched outcomes and ensure the candidate's digest mutation or other behavior does not interfere with the old policy.
- Rehearse failure and recovery. Test unavailable registries, missing credentials and trust-service failures. Demonstrate the scoped exception and policy rollback before denial becomes a production dependency.
- Move the cohort to enforcement. Confirm the new policy is active and its expected denial occurs before removing the corresponding old protection. Expand only after application owners can interpret and resolve rejections.
Do not keep two overlapping enforcement systems indefinitely without an owner. During transition, identify which policy caused each rejection and which source of configuration is authoritative. Avoid deleting legacy policies solely because a new policy object exists; readiness, matching and negative-case results are the evidence that protection transferred.
Acceptance cases that expose a permissive policy
| Case | Expected result under the proposed trust contract |
|---|---|
| Approved digest, expected build identity, accepted current attestation | Admitted in the intended scope. |
| Unsigned image or valid signature from another identity | Denied when enforcement is active. |
| Image from an unapproved registry outside the verification pattern | Denied by the explicit registry/repository restriction; falling outside the signature check must not create a bypass. |
| Correct signer, attestation for another digest | Denied; evidence must bind to the deployed subject. |
| Correctly signed report with a failed result or expired scan | Denied if outcome and freshness are requirements. |
| Disallowed image in an init container or another image field in scope | Denied; exercise every image-bearing field the policy must cover. |
| A tag now resolves to a different digest | The changed artifact must meet the policy; an earlier tag check must not authorize it. |
| Registry, credential or trust dependency unavailable | The documented failure behavior occurs, with useful diagnostics and a rehearsed recovery path. |
| A scoped emergency exception | Only the intended request is permitted; unrelated workloads remain denied and expiry/removal is verified. |
These are proposed tests, not observed results. Retain the submitted workload, policy revision, admission response and corresponding reports for each case. Repeat them when changing match rules, trust identities, exception handling or controller versions.
Treat admission availability and exceptions as part of the design
A policy's validation action and its behavior on evaluation/webhook failure are separate choices. Observation of policy violations does not establish that an unavailable dependency is harmless. Test both paths. A fail-closed design can reject Pod creation or updates during an outage; a fail-open design can let a request continue without that webhook's verification. Choose the required behavior per workload and staff the recovery procedure.
At the Kubernetes webhook boundary, failurePolicy: Ignore applies to calling errors such as timeouts. It does not override an explicit, successfully returned denial. The admission documentation makes this distinction. During a registry or trust outage, record whether Kyverno returned a denial or the API server failed to call the webhook; the recovery action depends on which path failed.
Give exception creation and modification tighter permissions than ordinary workload deployment. Scope emergency access to the smallest affected workload or artifact, assign an owner and expiry, and retain the audit record. A date written in a ticket or annotation does not remove an exception automatically: identify the mechanism that expires or deletes it and verify denial resumes afterwards. Review the exception matching rules against your installed release.
Kyverno's security guidance covers the policy engine's own privileged position, including RBAC and access to external data. Keep its security updates and service credentials within the same review process as application releases.
Finally, admission primarily evaluates matching API requests. Enabling a signature rule does not evict every running container that would now fail it. Join the rollout to an inventory and remediation plan for existing workloads, and measure rejected requests, dependency failures and expiring exceptions alongside policy coverage.
Sources & further reading
- Sigstore identity and signature verification
- Kyverno ImageValidatingPolicy
- Kyverno 1.19 release and deprecation schedule
- Kyverno migration to CEL policies
- Kyverno supported releases
- Kyverno upgrade procedure
- Kyverno security and hardening
- Kubernetes dynamic admission controls
- Kyverno exception scope and matching
Spotted something that needs another look?
Help improve this page →