For the complete documentation index, see llms.txt. This page is also available as Markdown.

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


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.

  • Platform concepts: Familiarity with Harness pipelines, stages, and steps.

  • 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).


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.

Developer flow with Cosign and OPA

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:

1

Log in to the registry

Log in to Docker Hub from the terminal so you can push images:

2

Pull and tag the Guestbook image

Pull the public Guestbook image, then tag development and production variants:

3

Install Cosign and set image variables

Download and install Cosign, then export image references:

4

Push the images

Push both tags to your registry:

5

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:

Cosign verify successful

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:

Image
Tag
Digest
Annotation
Signed

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.

Updated deploy-guestbook stage

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.com

  • Microsoft: https://login.microsoftonline.com

  • GitHub: https://github.com/login/oauth

  • GitLab: 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:

Script output variable
Bash output variable

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:

  1. From the Policies navigation menu, select Policy Sets, then select + New Policy Set.

  2. 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.

  3. Select Next, select the Check Image Digest and Check Environment policies, then select Finish.

  4. Open the pipeline, select the policy_enforcement step, keep the default settings, select Check Image Sign Policy Set for 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

Cosign verify fails during the Shell Script step

Confirm the Delegate can reach the image registry and the Sigstore services your Cosign version requires. Confirm YOUR_OIDC_CERTIFICATE_IDENTITY and YOUR_OIDC_ISSUER match the identity used at sign time. Prefer verifying by digest when tags can move.

Policy evaluation fails even for the signed development image

Confirm the Policy step payload expressions use the correct step identifier and output variable names. Confirm the digest string in Rego matches the digest Cosign returns, including the sha256: prefix.

Deployment continues when you expect a deny

Confirm the Policy Set event is On Step, the Policy step references that Policy Set, and the policy action is configured to fail the step on deny. Go to Add a governance Policy step to review Policy step behavior.


Next steps

Last updated

Was this helpful?