Image Signing Guide with Tekton Chains on OCP

A complete guide to image signing with Tekton Chains on OCP — covering the concept, the setup, the pipeline integration, and verification.—

What Tekton Chains does

Tekton Chains works by reconciling the run of a task or a pipeline. Once the run is observed as completed, Tekton Chains takes a snapshot of the completed TaskRun/PipelineRun, and starts its core work in the order of: formatting (generate provenance JSON) → signing (sign the payload using the configured key) → uploading (upload the provenance and its signature to the configured storage).

It operates entirely automatically — you don’t modify your pipeline at all. Chains watches completed runs and signs in the background.


Step 1 — Chains is already installed on OCP

The Red Hat OpenShift Pipelines Operator installs Tekton Chains by default. You can configure Tekton Chains by modifying the TektonConfig custom resource; the Operator automatically applies the changes that you make.

# Verify Chains is running
oc get pods -n openshift-pipelines | grep chains
# tekton-chains-controller-xxx Running

Step 2 — Generate a signing key pair

# Install cosign (if not already)
brew install cosign # or download binary
# Generate key pair — stores private key as K8s secret automatically
cosign generate-key-pair k8s://openshift-pipelines/signing-secrets
# This creates:
# signing-secrets (K8s Secret) — holds cosign.key + cosign.password
# cosign.pub (local file) — distribute this for verification
# Extract public key for distribution/verification
oc get secret signing-secrets -n openshift-pipelines \
-o jsonpath='{.data.cosign\.pub}' | base64 -d > cosign.pub

For production, use a KMS (AWS KMS, HashiCorp Vault, GCP KMS) instead of a file-based key:

# AWS KMS example
cosign generate-key-pair --kms awskms:///arn:aws:kms:ca-central-1:123456:key/abc-def

Step 3 — Configure Chains via TektonConfig

Cluster administrators can use Tekton Chains to sign and verify images and provenances by: creating an encrypted x509 key pair and saving it as a Kubernetes secret; setting up authentication for the OCI registry to store images, image signatures, and signed image attestations; and configuring Tekton Chains to generate and sign provenance.

apiVersion: operator.tekton.dev/v1alpha1
kind: TektonConfig
metadata:
name: config
spec:
chain:
# Format for TaskRun attestations
artifacts.taskrun.format: "slsa/v1" # SLSA v1.0 provenance
artifacts.taskrun.storage: "oci" # store in OCI registry
# Format for PipelineRun attestations (recommended)
artifacts.pipelinerun.format: "slsa/v1"
artifacts.pipelinerun.storage: "oci"
artifacts.pipelinerun.enable-deep-inspection: "true" # inspect child TaskRuns
# OCI image signature format
artifacts.oci.format: "simplesigning"
artifacts.oci.storage: "oci"
# Transparency log (Sigstore Rekor)
transparency.enabled: "true"
transparency.url: "https://rekor.sigstore.dev" # or your internal Rekor
# Signing key reference
signers.cosign.key: "k8s://openshift-pipelines/signing-secrets"

Apply via oc patch if you prefer:

oc patch tektonconfig config --type=merge -p='{
"spec": {
"chain": {
"artifacts.pipelinerun.format": "slsa/v1",
"artifacts.pipelinerun.storage": "oci",
"artifacts.oci.format": "simplesigning",
"artifacts.oci.storage": "oci",
"transparency.enabled": "true"
}
}
}'

Step 4 — Type-hint your pipeline so Chains knows what to sign

Chains discovers what the output artifact is via type hints in Task results. Your build task must emit IMAGE_URL and IMAGE_DIGEST results:

apiVersion: tekton.dev/v1
kind: Task
metadata:
name: buildah-push
spec:
params:
- name: IMAGE
type: string
results:
# Type hints — Chains reads these to find the artifact
- name: IMAGE_URL
description: The image URL
- name: IMAGE_DIGEST
description: The image digest (sha256)
steps:
- name: build-and-push
image: registry.redhat.io/rhel8/buildah
script: |
buildah bud -t $(params.IMAGE) .
buildah push $(params.IMAGE) \
--digestfile /tmp/digest
# Emit type hints for Chains
echo -n "$(params.IMAGE)" | tee $(results.IMAGE_URL.path)
cat /tmp/digest | tee $(results.IMAGE_DIGEST.path)

For pipeline-level provenance, also emit results at the Pipeline level:

apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
name: ci-pipeline
spec:
results:
- name: IMAGE_URL
value: $(tasks.build.results.IMAGE_URL)
- name: IMAGE_DIGEST
value: $(tasks.build.results.IMAGE_DIGEST)
tasks:
- name: build
taskRef:
name: buildah-push

Step 5 — What happens automatically after a run

Once your PipelineRun completes, Chains fires automatically. You can watch for the signed annotation:

# Watch for Chains to finish signing
oc get pipelinerun my-run -o json | jq '.metadata.annotations'
# {
# "chains.tekton.dev/signed": "true",
# "chains.tekton.dev/transparency": "https://rekor.sigstore.dev/api/v1/log/entries?logIndex=12345678"
# }
# What gets stored in the OCI registry alongside your image:
# myimage:sha256-abc123.sig ← cosign image signature
# myimage:sha256-abc123.att ← SLSA provenance attestation

Step 6 — Verify images before deployment

# Set your image reference (always use digest, not tag)
IMAGE="quay.io/my-org/my-app@sha256:abc123..."
# 1. Verify the image signature
cosign verify \
--key cosign.pub \
$IMAGE
# 2. Verify the SLSA provenance attestation
cosign verify-attestation \
--key cosign.pub \
--type slsaprovenance \
$IMAGE | jq '.payload | @base64d | fromjson'
# 3. Check the Rekor transparency log entry
rekor-cli search --sha sha256:abc123...

The SLSA provenance JSON tells you exactly what built the image — the git commit, the pipeline name, each task step, and all input dependencies.


Step 7 — Enforce signatures at admission (policy gate)

Verification at deployment time is where this pays off. Use OCP’s built-in image policy or Kyverno/OPA to block unsigned images:

# Kyverno policy — block any image without a valid Chains signature
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-signed-images
spec:
validationFailureAction: Enforce
rules:
- name: check-image-signature
match:
resources:
kinds: [Pod]
verifyImages:
- imageReferences:
- "quay.io/my-org/*"
attestors:
- entries:
- keys:
publicKeys: |-
-----BEGIN PUBLIC KEY-----
<your cosign.pub contents>
-----END PUBLIC KEY-----
attestations:
- predicateType: https://slsa.dev/provenance/v1
conditions:
- all:
- key: "{{ builder.id }}"
operator: Equals
value: "https://tekton.dev/chains/v2"

SLSA levels Tekton Chains achieves

SLSA levelRequirementChains status
Level 1Provenance existsAchieved — attestation generated automatically
Level 2Signed provenance, hosted buildAchieved — cosign signature + Rekor log entry
Level 3Hardened build platform, non-falsifiable provenanceAchieved with OCP’s isolated pod builds
Level 4Two-party review, hermetic buildsPartial — requires additional hermetic build config

The key benefit: by implementing provenance in CI/CD pipelines, you protect your supply chain from tampering and unauthorized access, streamline compliance with evolving industry and government regulations, and enhance visibility and trust throughout your software lifecycle.

Leave a comment