Secure Container Image Signing with Cosign and OPA
Add Cosign image verification and OPA policy enforcement to a Kubernetes deployment pipeline
This tutorial shows how to sign a container image with Cosign, verify the signature in a Harness Deploy stage, and enforce the result with Open Policy Agent (OPA) policies. You reuse the Guestbook sample application pipeline from the Kubernetes Manifest tutorial, then add Shell Script and Policy steps before Rollout Deployment so only an approved signed image can deploy.
Harness Continuous Delivery (CD) and GitOps include OPA so you can evaluate policies during pipeline execution. Cosign keyless signing authenticates through OpenID Connect (OIDC) with trusted providers such as Google, GitHub, or Microsoft.
This tutorial is also available as a video.
What you will learn from this topic
How to sign images with Cosign and push signatures to your registry
How to set the deployment goal for digest and annotation checks
How to verify the image in the pipeline with a Shell Script step
How to write and attach OPA policies that deny unapproved digests or environments
How to test policy enforcement with passing and failing cases
Before you begin
Complete the following before you start:
Harness account: A Harness Enterprise account (paid or trial). If you do not have an account, sign up.
Kubernetes Manifest tutorial: Complete the Kubernetes Manifest tutorial (GitOps workflow or CD pipeline). This tutorial reuses the pipeline and resources from that topic.
Docker Hub (or another registry): Ability to push images and Cosign signatures to a registry you control.
OIDC identity: An identity and issuer you can use for Cosign keyless signing (Google, Microsoft, GitHub, or GitLab).
Policy-based governance
Policy-based governance is a paid feature on the Harness platform.
Architecture
Without Cosign or OPA, a CD pipeline can pull an image and deploy it without verification. With Cosign and OPA, verification and policy evaluation run before the deploy step.

Sign images with Cosign
In this section you pull the public Guestbook image (a sample application), tag it for development and production, sign it with Cosign, and push it to an image registry. The examples use Docker Hub.
Perform the following steps to sign and push the images:
Install Cosign and set image variables
Download and install Cosign, then export image references:
Sign by digest
Check your Docker repository for the image digest for both images (in the form sha256:xxxx...). Both images can share the same digest when tagged from the same source image. Sign by digest, not by tag, so you do not sign a different image than intended.
The -a flag adds an annotation to each signature. A browser window opens for OIDC sign-in. After authentication, Sigstore reports success:

Terminal output looks like this:
After signing, your registry shows additional repositories or digests for the detached Cosign signatures. Cosign stores a separate cryptographic signature object with the image so verification can retrieve both the image and its signature.
Deployment goal
Before developers deploy, put verification in place so the image is checked before rollout. You can have three container images for the same Guestbook sample application. Here is an example of how digests and annotations compare:
gcr.io/heptio-images/ks-guestbook-demo
0.1
sha256:fe18...7f47
-
No
dewandemo/guestbook-dev
0.1
sha256:aa11...9975
env:dev
Yes
dewandemo/guestbook-prod
0.1
sha256:aa11...9975
env:prod
Yes
In this example, the goal is to allow deployment of dewandemo/guestbook-dev@sha256:aa11...9975 with annotation env:dev, and deny any other image. Replace the example image details with your own registry, digest, and annotations.
Verify the image in the pipeline
From the Kubernetes Manifest tutorial, you have a stage deploy-guestbook in the deployment pipeline harness_guestbook_pipeline with a single Rollout Deployment step. Add two steps before Rollout Deployment: image verification with Cosign, then policy enforcement with OPA.

Add the Cosign verify Shell Script step
Add a Shell Script step. Name it cosign_verify. Keep the other settings at their defaults, and add the following script. Replace YOUR_DOCKERHUB_USERNAME, YOUR_OIDC_CERTIFICATE_IDENTITY (for example your associated email), and YOUR_OIDC_ISSUER (google, microsoft, github, or gitlab).
OIDC issuer values:
Google:
https://accounts.google.comMicrosoft:
https://login.microsoftonline.comGitHub:
https://github.com/login/oauthGitLab:
https://gitlab.com
The script installs Cosign, runs cosign verify, and parses fields from the response into step variables. Define script output variables for all five outputs. Example mapping:
sov_image_registry
image_registry
sov_image_digest
image_digest
sov_image_signed_string
image_signed_string
sov_image_sign_issuer
image_sign_issuer
sov_image_env
image_env
When you reference these variables later, use the format <+execution.steps.cosign_verify.output.outputVariables.sov_image_registry>. Your step identifier might differ from cosign_verify.
The first four variables help confirm that the correct digest comes from an approved registry with an expected OIDC issuer. The last variable is the env annotation. Adjust the script and variables to match the checks you need.
Write and attach OPA policies
Add a Policy step after cosign_verify (for example policy_enforcement). You can define and store policies in the OPA service in Harness. Policies are written in Rego.
Create the policies
From Project Setup, go to Policies and create two policies named Check Image Digest and Check Environment. Store both inline in Harness.
Check Image Digest policy:
This Rego policy denies access if the digest value in the input is not YOUR_IMAGE_DIGEST_FOR_GUESTBOOK-DEV. Replace the placeholder with your actual image digest.
Check Environment policy:
This Rego policy denies access unless the env value in the input is dev.
Save both policies. In Harness, you add Rego policies to a Policy Set. On configured events (for example saving or running a pipeline, or evaluating on a step), Harness evaluates the action with that Policy Set.
Create the Policy Set and wire the step
Perform the following steps to attach the policies to the pipeline:
From the Policies navigation menu, select Policy Sets, then select + New Policy Set.
Enter a name (for example
Check Image Sign Policy Set), select Custom for Entity Type that this policy set applies to, and select On Step for On what event should the policy set be evaluated.Select Next, select the Check Image Digest and Check Environment policies, then select Finish.
Open the pipeline, select the policy_enforcement step, keep the default settings, select
Check Image Sign Policy Setfor the policy set, and add this payload:
The payload reads values from the script output variables defined in the previous step. If your Shell Script step uses a different identifier, update the expressions to match it.
Test policy enforcement
Validate the setup with a passing case and two failing cases. The steps below assume you forked harnesscd-example-apps.
Passing case
Update https://github.com/YOUR_GITHUB_USERNAME/harnesscd-example-apps/blob/master/guestbook/guestbook-ui-deployment.yaml and set spec.containers.image to YOUR_DOCKERHUB_USERNAME/guestbook-dev:0.1. On your Harness pipeline, the cosign_verify step already checks for this signed container image. Run the pipeline. Both Check Image Digest and Check Environment policies should pass. Under Project Setup -> Policies -> Evaluations, open the recent evaluation to confirm the policy and payload match. The Rollout Deployment step runs and the deployment succeeds.
Failing case: unsigned image
Update the same deployment manifest and set spec.containers.image to the public unsigned image gcr.io/heptio-images/ks-guestbook-demo:0.1. In the cosign_verify Shell Script step, update the verify command:
Policy validation fails because of mismatches between the policy and the payload. Both Check Image Digest and Check Environment fail, and the deployment does not proceed.
Failing case: wrong environment annotation
Update the deployment manifest and set spec.containers.image to YOUR_DOCKERHUB_USERNAME/guestbook-prod:0.1. In the cosign_verify Shell Script step, update the verify command:
Check Image Digest can pass when digests match, but Check Environment fails when the annotation is prod instead of dev. The deployment does not proceed.
Optional follow-on: registry policy
Five script output variables were declared, but only sov_image_digest and sov_image_env appear in the sample policies. Add another policy to the Policy Set that requires the image to come from an approved registry. Use sov_image_registry in the payload. You can also enforce the other unused output variables the same way.
Troubleshooting
Next steps
Generate SLSA Provenance: Add supply-chain attestation steps in Harness pipelines.
Verify SLSA Provenance: Verify provenance before you promote an artifact.
Add a governance Policy step: Apply OPA checks at other pipeline events.
Kubernetes Manifest tutorial: Revisit the base Guestbook deployment this topic extends.
Last updated
Was this helpful?