Skip to content

Commit

Permalink
Add SBOM tutorial
Browse files Browse the repository at this point in the history
Signed-off-by: Luiz Carvalho <lucarval@redhat.com>
  • Loading branch information
lcarva committed Aug 11, 2023
1 parent bfbe6e5 commit 33625bb
Show file tree
Hide file tree
Showing 3 changed files with 343 additions and 0 deletions.
175 changes: 175 additions & 0 deletions docs/tutorials/signed-sbom.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
<!--
---
linkTitle: "Tutorial: Signed SBOMs"
weight: 300
---
-->

# Chains Signed SBOM Tutorial

This tutorial details the steps required to use Tekton Chains to sign an SBOM for an image. It adds
SBOM generation to a minimal clone-build-push Pipeline.

At the end of this tutorial, the built image will contain a signed SBOM attestation. NOTE: This is
different than an SBOM attachment. SBOM attestations have a securer link to the described image as
well as the flexibility to provide multiple SBOMs for the same image.

## 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](https://github.com/sigstore/cosign) and run the following:

```shell
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 push signatures and attestations to an OCI registry using the
credentials linked to your `TaskRun`'s service account. See our [authentication
doc](../authentication.md)
2. The build and sbom Tasks will build and push content to the OCI registry.

Both of those can be setup by creating a `docker-registry` secret and linking it to the
ServiceAccount used by the TaskRuns. This tutorial assumes the ServiceAccount is `default`.

```shell
# Create a secret based on your local docker config.
kubectl create secret docker-registry tutorial-secret \
--from-file=.dockerconfigjson=$HOME/.docker/config.json

# Link secret to service account
kubectl patch serviceaccount default -p '{"imagePullSecrets": [{"name": "tutorial-secret"}]}'
kubectl patch serviceaccount default -p '{"secrets": [{"name": "tutorial-secret"}]}'
```

## Configure Tekton Chains

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

* `artifacts.sbom.format=in-toto`
* `artifacts.sbom.storage=oci`
* `transparency.enabled=true`

You can set these fields by running:

```shell
kubectl patch configmap chains-config -n tekton-chains -p='{"data":{"artifacts.sbom.format": "in-toto"}}'
kubectl patch configmap chains-config -n tekton-chains -p='{"data":{"artifacts.sbom.storage": "oci"}}'
kubectl patch configmap chains-config -n tekton-chains -p='{"data":{"transparency.enabled": "true"}}'
```

This tells Chains to generate an in-toto attestation for the SBOM and store it in the image's OCI
registry. The SBOM signature will also be stored in [rekor](https://github.com/sigstore/rekor) since
transparency is enabled.

## Create SBOM Task

Similar to image signatures, Tekton Chains requires that the task producing the SBOM emits results
[named in a certain way](https://tekton.dev/docs/chains/config/#chains-type-hinting). This section
creates a sample Tekton Task that meets these requirements.

The [sample sbom Task](../../examples/sbom/sbom-task.yaml) uses
[syft](https://github.com/anchore/syft) to generate an SBOM in the CycloneDX format for a given
image. It then stores the generated SBOM as blob in the OCI registry. When the Task completes,
Tekton Chains downloads the SBOM blob and uses it as the payload of a new signed attestation that is
then attached to the image. To create it:

```shell
kubectl apply -f examples/sbom/sbom-task.yaml
```

It is possible to run this Task to see SBOM signing in action. But let's use it in a Pipeline for a
more realistic example.

## Build Pipeline

The [sample build pipeline](../../examples/sbom/sbom-pipeline.yaml) uses git to clone an application
repository, buildah to build an application container image, and syft to create an SBOM for the
image. To create it:

```shell
kubectl apply -f examples/sbom/sbom-pipeline.yaml
```

## Running

Let's use the tkn CLI to run the build pipeline. To do so, you'll need an OCI registry to push the
image to and a git repository that contains a valid Dockerfile in its root. For example:

```shell
# Set these accordingly
GIT_REPO=https://github.com/user/example
GIT_REVISION=main
OCI_REPO=quay.io/user/example

tkn -n minimal-container pipeline start simple-build \
--param git-repo=${GIT_REPO} --param git-revision=${GIT_REVISION} \
--param output-image=${OCI_REPO}:latest --param sbom-repo=${OCI_REPO} \
--workspace name=shared,pvc,claimName="tekton-build" \
--showlog
```

NOTE: The above assumes you have an existing PVC named `tekton-build`.

Towards the end of the log, there should a line like this:

```text
[sbom : store-sbom-blob] Digest: sha256:bb19013e908abf6d0d024d82c8990c30e84bea29796a361d828f78499b7ddf12
```

Make a note of the digest. This is the expected digest of the contents of the SBOM.

The digest can also be retrieved from the TaskRun's result:

```shell
kubectl get taskrun $TASK_RUN_NAME -o yaml | \
yq '.status.results[] | select(.name == "IMAGE_SBOM_URL") | .value'
```

## Verification

At this point, the image is built and should have an SBOM attestation attached to it. Use cosign to
inspect it:

```shell
cosign download attestation ${OCI_REPO}:latest | \
jq '.payload | @base64d | fromjson | select(.predicateType == "https://cyclonedx.org/schema") | .predicate'
```

Pipe the command above through `sha256sum`. The digest should match the digest of the SBOM retrieved
in the previous section.

You can also use cosign to verify that the SBOM is in fact signed:

```shell
cosign verify-attestation --key cosign.pub \
--type 'https://cyclonedx.org/schema' ${OCI_REPO}:latest | \
jq '.payload | @base64d | fromjson | .predicate' | sha256sum
```

NOTE: The above assumes you still have the `cosign.pub` file from the previous step. If you don't,
you can load the public key directly from Kubernetes by using `k8s://tekton-chains/signing-secrets`
as the value for the `--key` flag.

NOTE: If you do not set `transparency.enabled` to `true` in `chains-config`, you must use the flag
`--insecure-ignore-tlog` in the command above.

## Large SBOMs

Because Tekton Chains has to read the SBOM in order to sign it, it has a default maxium size of
10MB. This can be adjusted via the `artifacts.sbom.maxbytes` property in `chains-config`.
98 changes: 98 additions & 0 deletions examples/sbom/sbom-pipeline.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Copyright 2023 The Tekton Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: simple-build
spec:
params:
- description: Repository URL to clone from.
name: git-repo
type: string
- default: main
description: Revision to checkout. (branch, tag, sha, ref, etc...)
name: git-revision
type: string
- description: Reference of the image the pipeline will produce.
name: output-image
type: string
- description: OCI repo to temporarily store the SBOM blob.
name: sbom-repo
type: string
results:
- description: Reference of the image the pipeline will produce.
name: IMAGE_URL
value: $(tasks.build.results.IMAGE_URL)
- description: Digest of the image the pipeline will produce.
name: IMAGE_DIGEST
value: $(tasks.build.results.IMAGE_DIGEST)
- description: Repository URL used for buiding the image.
name: CHAINS-GIT_URL
value: $(tasks.clone.results.url)
- description: Repository commit used for building the image.
name: CHAINS-GIT_COMMIT
value: $(tasks.clone.results.commit)
tasks:
- name: clone
taskRef:
resolver: git
params:
- name: url
value: https://github.com/tektoncd/catalog.git
- name: revision
value: main
- name: pathInRepo
value: task/git-clone/0.9/git-clone.yaml
params:
- name: url
value: $(params.git-repo)
- name: revision
value: $(params.git-revision)
workspaces:
- name: output
workspace: shared
- name: build
runAfter:
- clone
taskRef:
resolver: git
params:
- name: url
value: https://github.com/tektoncd/catalog.git
- name: revision
value: main
- name: pathInRepo
value: task/buildah/0.5/buildah.yaml
params:
- name: IMAGE
value: $(params.output-image)
workspaces:
- name: source
workspace: shared
- name: sbom
runAfter:
- build
taskRef:
kind: Task
name: sbom
params:
- name: IMAGE_URL
value: $(params.output-image)
- name: IMAGE_DIGEST
value: $(tasks.build.results.IMAGE_DIGEST)
- name: SBOM_REPO
value: $(params.sbom-repo)
workspaces:
- name: shared
70 changes: 70 additions & 0 deletions examples/sbom/sbom-task.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Copyright 2023 The Tekton Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
name: sbom
spec:
params:
- name: IMAGE_URL
description: Reference to the image the SBOM will be generated for.
- name: IMAGE_DIGEST
description: Digest of the image the SBOM will be generated for.
- name: SBOM_REPO
description: OCI repo to temporarily push the SBOM blob to.
- name: HOMEDIR
type: string
description: Value for the HOME environment variable.
default: /tekton/home
results:
- name: IMAGE_URL
description: Reference to the image the SBOM will be generated for.
- name: IMAGE_DIGEST
description: Digest of the image the SBOM will be generated for.
- name: IMAGE_SBOM_URL
description: Reference, including digest, to the SBOM blob.
- name: IMAGE_SBOM_FORMAT
description: The SBOM format.
type: string
stepTemplate:
env:
- name: HOME
value: "$(params.HOMEDIR)"
steps:
- name: make-sbom
image: docker.io/anchore/syft:v0.86.1
args:
- $(params.IMAGE_URL)@$(params.IMAGE_DIGEST)
- --output
- cyclonedx-json
- --file
- $(params.HOMEDIR)/sbom.json
- name: emit-results
image: docker.io/busybox:1.36
script: |
sbom_digest="$(sha256sum $(params.HOMEDIR)/sbom.json | cut -d' ' -f1)"
echo -n "$(params.SBOM_REPO)@sha256:${sbom_digest}" | tee $(results.IMAGE_SBOM_URL.path)
echo -n 'https://cyclonedx.org/schema' | tee $(results.IMAGE_SBOM_FORMAT.path)
echo -n '$(params.IMAGE_URL)' | tee $(results.IMAGE_URL.path)
echo -n '$(params.IMAGE_DIGEST)' | tee $(results.IMAGE_DIGEST.path)
- name: store-sbom-blob
image: docker.io/bitnami/oras:latest
args:
- blob
- push
- --registry-config
- /tekton/creds/.docker/config.json
- $(params.SBOM_REPO)
- $(params.HOMEDIR)/sbom.json

0 comments on commit 33625bb

Please sign in to comment.