Chains Signed Provenance Tutorial

This tutorial will cover how to set up Chains to sign OCI images built in Tekton, and how to automatically generate and sign in-toto attestations for each image. This tutorial will also cover how to store these attestations in a transparency log and query the log for the attestation.

This tutorial will guide you through:

  • Generating your own keypair and storing it as a Kubernetes Secret
  • Setting up authentication for your OCI registry to store images, image signatures and signed image attestations
  • Configuring Tekton Chains to generate and sign provenance
  • Building an image with kaniko in a Tekton TaskRun
  • Verifying the signed image and the signed provenance

Prerequisites

A Kubernetes cluster with the following installed:

  • Tekton Chains
  • Tekton Pipelines

Generate a Key Pair

First, we’ll generate an encrypted x509 keypair and save it as a Kubernetes secret. Install cosign and run the following:

cosign generate-key-pair k8s://tekton-chains/signing-secrets

cosign will prompt you for a password, which will be stored in a Kubernetes secret named signing-secrets in the tekton-chains namespace.

The public key will be written to a local file called cosign.pub.

Set up Authentication

There are two forms of authentication that need to be set up:

  1. The Chains controller will be pushing signatures to an OCI registry using the credentials linked to your TaskRun’s service account. See our authentication doc
  2. The Kaniko Task that will build and push the image needs push permissions for your registry.

To set up auth for the Kaniko Task, you’ll need a Kubernetes secret of a docker config.json file which contains the required auth. You can create the secret by running:

kubectl create secret generic [DOCKERCONFIG_SECRET_NAME] --from-file [PATH TO CONFIG.JSON]

Configuring Tekton Chains

You’ll need to make these changes to the Tekton Chains Config:

  • artifacts.taskrun.format=slsa/v1
  • artifacts.taskrun.storage=oci
  • artifacts.oci.storage=oci
  • transparency.enabled=true

You can set these fields by running

kubectl patch configmap chains-config -n tekton-chains -p='{"data":{"artifacts.taskrun.format": "slsa/v1"}}'
kubectl patch configmap chains-config -n tekton-chains -p='{"data":{"artifacts.taskrun.storage": "oci"}}'
kubectl patch configmap chains-config -n tekton-chains -p='{"data":{"artifacts.oci.storage": "oci"}}'
kubectl patch configmap chains-config -n tekton-chains -p='{"data":{"transparency.enabled": "true"}}'

Note, you can continue to use the older alias of slsa/v1: in-toto above.

This tells Chains to generate an in-toto attestation and store it in the specified OCI registry. Attestations will also be stored in rekor since transparency is enabled.

Start the Kaniko Task

Great, now that the setup is done we’re finally ready to build an image with kaniko!

First, apply the Kaniko Task to your cluster:

kubectl apply -f examples/kaniko/kaniko.yaml

and set the following environment variables:

REGISTRY=[The registry you'll be pushing to]
DOCKERCONFIG_SECRET_NAME=[The name of the secret with the docker config.json]

Then, you can start the Kaniko Task with the Tekton CLI tool, tkn:

tkn task start --param IMAGE=$REGISTRY/kaniko-chains --use-param-defaults --workspace name=source,emptyDir="" --workspace name=dockerconfig,secret=$DOCKERCONFIG_SECRET_NAME kaniko-chains

You can watch the logs of this Task until they complete; if authentication is set up correctly than the final image should be pushed to $REGISTRY/kaniko-chains.

Verifying the Image and Attestation

Once the TaskRun has successfully completed, you’ll need to wait a few seconds for Chains to generate provenance and sign it.

Once you see the chains.tekton.dev/signed=true annotation on your TaskRun you know that Chains has completed the signing process and you’re ready to move on to verification:

kubectl get tr [TASKRUN_NAME] -o json | jq -r .metadata.annotations

{
  "chains.tekton.dev/signed": "true",
  ...
}

To verify the image and the attestation, we’ll use cosign again:

cosign verify --key cosign.pub $REGISTRY/kaniko-chains
cosign verify-attestation --key cosign.pub --type slsaprovenance $REGISTRY/kaniko-chains

You should see verification output for both!

Finding Provenance in Rekor

To find provenance for the image in Rekor, first get the digest of the $REGISTRY/kaniko-chains image you just built. You can look this up in the TaskRun, or pull the image to get the digest.

You can then search rekor to find all entries that match the sha256 digest of the image you just built with the rekor-cli tool:

rekor-cli search --sha [IMAGE_DIGEST]

[UUID1]
[UUID2]

The search will print out the UUIDs of matching entries. It may take a little guessing, but one of those UUIDs holds the attestation. You can see the attestation by using jq:

rekor-cli get --uuid [UUID] --format json | jq -r .Attestation | base64 --decode | jq

Congratulations! You have officially built an image, signed it, and generated signed provenance for it with Tekton Chains 🎉

What you just created

This diagram shows what you just deployed:

signed-provenance-setup