Image Signature Verification

In Tekton Chains, it can automatically sign the built image and record the signature in the SLSA Provenance.

TOC

Feature Overview

This method uses Tekton Chains to automatically sign the built image and then use cosign or Kyverno to verify the signature:

  1. Configure Tekton Chains to automatically sign the built image.
  2. Use buildah Tekton Task to build the image.
  3. (Optional) Use cosign cli to verify the signature.
  4. Configure Kyverno rules to allow only signed images.
  5. Use the image to create a Pod to verify the signature.
TIP

Compared to Quick Start: Signed Provenance, this method only adds more verification steps.

Use Cases

The following scenarios require referring to the guidance in this document:

  • Implementing image signature verification in Kubernetes clusters using Kyverno
  • Enforcing security policies to only allow signed images to be deployed
  • Setting up automated image signature verification in CI/CD pipelines
  • Ensuring image integrity and authenticity in production environments
  • Implementing supply chain security controls for container images

Prerequisites

  • A Kubernetes cluster with Tekton Pipelines, Tekton Chains and Kyverno installed
  • A registry with image pushing enabled
  • kubectl CLI installed and configured to access your cluster
  • cosign CLI tool installed
  • jq CLI tool installed

Process Overview

StepOperationDescription
1Generate signing keysCreate a key pair for signing artifacts using cosign
2Set up authenticationConfigure registry credentials for image pushing
3Configure Tekton ChainsSet up Chains to use OCI storage and configure signing
4Create a sample pipelineCreate a pipeline definition with necessary tasks and workspaces
5Run a sample pipelineCreate and run a PipelineRun with proper configuration
6Wait for signingWait for the PipelineRun to be signed by Chains
7Get image informationExtract image URI and digest from the PipelineRun
8Verify signatures with KyvernoConfigure and verify the image signature using Kyverno policies
9Clean up resourcesDelete the test Pods and policies

Step-by-Step Instructions

Steps 1-7: Basic Setup

These steps are identical to the Quick Start: Signed Provenance guide. Please follow the instructions in that guide for:

Step 8: Verify the signature with Kyverno

At Step 8: Verifying the Image and Attestation, we use cosign CLI to verify the signature. Here we use Kyverno to verify the signature.

Step 8.1: Create a Kyverno policy to allow only signed images to be deployed

TIP

This step requires cluster administrator privileges.

More details about Kyverno ClusterPolicy, please refer to Kyverno ClusterPolicy

The policy is as follows:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: only-cosign-image-deploy
spec:
  webhookConfiguration:
    failurePolicy: Fail
    timeoutSeconds: 30
  background: false
  rules:
    - name: check-image
      match:
        any:
          - resources:
              kinds:
                - Pod
              namespaces:
                - policy
      verifyImages:
        - imageReferences:
            - "*"
            # - "<registry>/test/*"
          skipImageReferences:
            - "ghcr.io/trusted/*"
          failureAction: Enforce
          verifyDigest: false
          required: false
          useCache: false
          imageRegistryCredentials:
            allowInsecureRegistry: true
            secrets:
              # The credential needs to exist in the namespace where kyverno is deployed
              - registry-credentials

          attestors:
            - count: 1
              entries:
                - keys:
                    publicKeys: |- # <- The public key of the signer
                      -----BEGIN PUBLIC KEY-----
                      MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEFZNGfYwn7+b4uSdEYLKjxWi3xtP3
                      UkR8hQvGrG25r0Ikoq0hI3/tr0m7ecvfM75TKh5jGAlLKSZUJpmCGaTToQ==
                      -----END PUBLIC KEY-----

                    ctlog:
                      ignoreSCT: true

                    rekor:
                      ignoreTlog: true
Explanation of YAML fields
  • spec.rules[].match.any[].resources: The resources to be matched and validated.
    • kinds: The kinds of the resources to be matched and validated.
      • Pod: The Pod resources.
    • namespaces: The namespaces of the resources to be matched and validated.
      • policy: The resources in the policy namespace will be matched and validated.
  • spec.rules[].verifyImages: The verify images
    • imageReferences: The image references to be verified.
      • *: all image references will be verified.
      • <registry>/test/*: only image references in the <registry>/test registry will be verified.
    • skipImageReferences: The image references to be skipped.
      • ghcr.io/trusted/*: only image references in the ghcr.io/trusted registry will be skipped.
    • imageRegistryCredentials:
      • allowInsecureRegistry: Whether to allow insecure registry.
      • secrets: The secrets to be used for the image registry credentials.
        • registry-credentials: The name of the secret. The secret needs to exist in the namespace where kyverno is deployed.
    • attestors: The attestors to be used for the image verification.
      • count: The count of the attestors need to be matched.
      • entries: The entries of the attestors.
        • keys: The keys of the attestors.
          • publicKeys: The public keys of the attestors.
            • This public key is the same as the public key cosign.pub in the signing-secrets secret.
          • ctlog: The ctlog of the attestors.
            • ignoreSCT: Whether to ignore the SCT.
              • In isolated network environments, ignore the SCT first.
          • rekor: The rekor of the attestors.
            • ignoreTlog: Whether to ignore the Tlog.
              • In isolated network environments, ignore the Tlog first.

Need to adjust the configuration

  • spec.rules[].attestors[].entries[].keys.publicKeys: The public key of the signer.
    • This public key is the same as the public key cosign.pub in the signing-secrets secret.
    • The public key can be obtained from the Get the Signing Public Key section.

Save into a yaml file named kyverno.only-cosign-image-deploy.yaml and apply it with:

$ kubectl apply -f kyverno.only-cosign-image-deploy.yaml

clusterpolicy.kyverno.io/only-cosign-image-deploy configured

Step 8.2: Verify the policy

In the policy namespace where the policy is defined, create a Pod to verify the policy.

Use the signed image created by the pipeline to create a Pod.

$ export NAMESPACE=<policy>
$ export IMAGE=<<registry>/test/chains/demo-1:latest@sha256:93635f39cb31de5c6988cdf1f10435c41b3fb85570c930d51d41bbadc1a90046>

$ kubectl run -n $NAMESPACE signed --image=${IMAGE} -- sleep 3600

pod/signed created

The Pod will be created successfully.

$ export NAMESPACE=<policy>
$ kubectl get pod -n $NAMESPACE signed

NAME      READY   STATUS    RESTARTS   AGE
signed   1/1     Running   0          10s

Use the unsigned image to create a Pod.

$ export NAMESPACE=<policy>
$ export IMAGE=<<registry>/test/chains/unsigned:latest>

$ kubectl run -n $NAMESPACE unsigned --image=${IMAGE} -- sleep 3600

Receive the output like this, means the Pod is blocked by the policy.

Error from server: admission webhook "mutate.kyverno.svc-fail" denied the request:

resource Pod/policy/unsigned was blocked due to the following policies

only-cosign-image-deploy:
  check-image: 'failed to verify image ubuntu:latest:
    .attestors[0].entries[0].keys: no signatures found'

Step 9: Clean up the resources

Delete the Pods created in the previous steps.

$ export NAMESPACE=<policy>
$ kubectl delete pod -n $NAMESPACE signed

pod "signed" deleted

Delete the policy.

$ kubectl delete clusterpolicy only-cosign-image-deploy

Expected Results

After completing this guide:

  • You have a working setup with Tekton Chains for image signing and Kyverno for signature verification
  • Your container images are automatically signed during the build process
  • Only signed images can be deployed in the specified namespace
  • Unsigned images are automatically blocked by Kyverno policies
  • You have implemented a basic supply chain security control for your container images

This guide provides a foundation for implementing supply chain security in your CI/CD pipelines. In a production environment, you should:

  1. Configure proper namespace isolation and access controls
  2. Implement secure key management for signing keys
  3. Set up monitoring and alerting for policy violations
  4. Regularly rotate signing keys and update security policies
  5. Consider implementing additional security controls like vulnerability scanning

References