Blog

Validate and Track Signatures of OCI Images Using Cosign and Rekor

Author: Yolanda Robla
/
4 mins read
/
Aug 22, 2023
/ Subscribe

Container security is a critical priority in modern software development. Ensuring the authenticity and integrity of your container images is crucial to prevent security breaches. In a previous post, we guided you through the process of signing Docker containers using GitHub Actions and Cosign - a Sigstore tool designed for signing and verifying container images. In this tutorial, we will guide you through the process of validating the signature of OCI images using Cosign and tracking the signatures using the Rekor transparency log.

The only prerequisite is to have a valid OCI image that has been signed with Cosign via GitHub Actions (access the previous post for the full tutorial).

About the Sigstore project

Sigstore is an initiative championed by the Linux Foundation (https://www.linuxfoundation.org/) and OpenSSF (https://openssf.org/) , that is designed to bring openness, accountability, and trust to software supply chains. It addresses the challenges of securing software artifacts by leveraging cryptographic verification and open standards to ensure provenance and transparency. Stacklok is a key contributor and supporter of the Sigstore project.

Our previous post on signing Docker containers covered in more detail the components of Sigstore, including Cosign, Rekor, and Fulcio.

Validate OCI images signed from GitHub

When building an image with GitHub and signing it via GitHub Action, those images are signed in a keyless mode using the GitHub OIDC issuer, and published to a GitHub registry.

The details of the pushed images can be seen on the GitHub UI:

In this case the signatures are also stored as artifacts, along with the image itself.

The first step for validating the signature of an image is to actually get the signature for it. This can be achieved with the help of cosign triangulate, which is a command that allows you to extract cryptographic signatures from container images:

JavaScript
$ cosign triangulate ghcr.io/yrobla-org/test:main

ghcr.io/yrobla-org/test:sha256-218cd345965ba093aed79fb77b8c6806fca76972bfd943c62317733164c07430.sig

This signature is stored as an artifact inside GitHub’s container registry, and can be parsed using the same tools. Having the signature artifact, the first step would be to retrieve the associated manifest, extract the certificate associated with the signature and parse it, to specifically get information about the identity and OIDC issuer from the signature. The extraction of the manifest can be achieved using cranehttps://github.com/google/go-containerregistry/blob/main/cmd/crane/README.md

JavaScript
$ crane manifest ghcr.io/yrobla-org/test:sha256-218cd345965ba093aed79fb77b8c6806fca76972bfd943c62317733164c07430.sig | jq -r '.layers[0].annotations["dev.sigstore.cosign/certificate"]' > /tmp/signature.crt

$ cat /tmp/signature.crt

-----BEGIN CERTIFICATE-----
MIIGyDCCBk+gAwIBAgIUA3VuD3AG45XxbrN23SOeH4GKerYwCgYIKoZIzj0EAwMw
...
0NHJNw6QQYU2DgIwJqQHjnrydI/qcrfVdM0OSJHhevGfWYRg/Xvqh8QKnQXcu0E5
Ce9R8ZwCPlkLZ30M
-----END CERTIFICATE-----

The certificate can be inspected, and this will show all properties and annotations about the signature:

JavaScript
$ openssl x509 -in /tmp/signature.crt -text -noout

Certificate:
    Data:
	 ...
        Issuer: O = sigstore.dev, CN = sigstore-intermediate
        ...
        Subject Public Key Info:
            ...                      URI:https://github.com/yrobla-org/test/.github/workflows/docker-publish.yml@refs/heads/main
            1.3.6.1.4.1.57264.1.1:
                https://token.actions.githubusercontent.com
...

Being a keyless signature, the information to validate it relies on the certificate identity (https://github.com/yrobla-org/test/.github/workflows/docker-publish.yml@refs/heads/main) and OIDC issuer (https://token.actions.githubusercontent.com), so we need to parse the certificate to extract those fields:

JavaScript
IDENTITY=$(openssl x509 -in /tmp/signature.crt -noout -ext subjectAltName  | awk -F 'URI:' '{print $2}' | tr -d '\n')
ISSUER=$(openssl x509 -in /tmp/signature.crt -text -noout | awk '/1.3.6.1.4.1.57264.1.1:/  {getline;  sub(/^[ \t]+/, "");print}')

Once we have the identity and issuer, cosign can be used to actually verify the signature of the image based on them. This will provide information about the signature verification, but also about the origin of the signature process, and information about the Rekor transparency log:

JavaScript
$ cosign verify --certificate-oidc-issuer $ISSUER --certificate-identity $IDENTITY ghcr.io/yrobla-org/test:main

Verification for ghcr.io/yrobla-org/test:main --
The following checks were performed on each of these signatures:
  - The cosign claims were validated
  - Existence of the claims in the transparency log was verified offline
  - The code-signing certificate was verified using trusted certificate authority certificates

[{"critical":{"identity":{"docker-reference":"ghcr.io/yrobla-org/test"},"image":{"docker-manifest-digest":"sha256:299966bbc840306e2a63bed41347e55676f23a2f7b01a0ea56624f11299a9b21"},"type":"cosign container image signature"},
...

Details from the signature can be parsed using jq:

JavaScript
$ cosign verify --certificate-oidc-issuer $ISSUER --certificate-identity $IDENTITY ghcr.io/yrobla-org/test:main | jq

[
  {
    "critical": {
      "identity": {
        "docker-reference": "ghcr.io/yrobla-org/test"
      },
      "image": {
        "docker-manifest-digest": "sha256:299966bbc840306e2a63bed41347e55676f23a2f7b01a0ea56624f11299a9b21"
      },
      "type": "cosign container image signature"
    },
...
      "Issuer": "https://token.actions.githubusercontent.com",
      "Subject": "https://github.com/yrobla-org/test/.github/workflows/docker-publish.yml@refs/heads/main",
      "githubWorkflowName": "Docker",
      "githubWorkflowRef": "refs/heads/main",
      "githubWorkflowRepository": "yrobla-org/test",
      "githubWorkflowSha": "5e6b69ab15e9d02e4381a50a141be01010860fe6",
      "githubWorkflowTrigger": "schedule"
    }
  }
]
Reviewing information published in Rekor

As part of the signature verification, Cosign is extracting data identifiers to search in the Rekor transparency log registry. The Rekor log id or uuid can be extracted with:

JavaScript
$ cosign verify --certificate-oidc-issuer $ISSUER --certificate-identity $IDENTITY ghcr.io/yrobla-org/test:main | jq  '.[].optional.Bundle.Payload.logIndex'

30636318

Those IDs can be used to search in the Rekor UI. This will show all the information stored in the transparency log for this specific image and signature, allowing us to track all the information:

Conclusions

In this tutorial, we explored the process of verifying image signatures using cosign, to ensure their authenticity and integrity, preventing security breaches. Validating images and their signatures and tracking them on a transparency log assures image authenticity and enables accountability.

By enabling image signing and further verification and tracking, you can boost your container security, ensuring the trustworthiness of images in your software supply chain.

Stacklok has contributed Minder to the OpenSSF out of a deep belief in the power of the open source community

Luke Hinds /
Oct 28, 2024
Continue Reading
This Month in Minder - September 2024

This Month in Minder: September 2024

Stacklok /
Sep 26, 2024
Continue Reading
Flexible policy enforcement with Minder profile selectors

Flexible policy enforcement with Minder profile selectors

Dan Barr /
Sep 19, 2024
Continue Reading