Step 5 - Build and Sign Image
- - 5 min read
Finally, after pulling and checking the code, we are going to create the image. During this process the image will be signed and uploaded to the public registry Quay.io.
Goals
The goals of this step are:
Build the image
Sign the image
Upload it Quay.io
You can use any registry you like. |
Prerequisites
You need to have an account in Quay.io and create a repository there.
I have created the repository https://quay.io/repository/tjungbau/secure-supply-chain-demo which will contain the images and the signatures.
Steps
Create a Robot account in Quay.io and get the authentication json. Go to "Account Settings > Robot Accounts" and create a new account.
Figure 1. Quay Robot AccountAllow this account WRITE permissions to the repository
Figure 2. Quay Robot Account PermissionsSelect the just-created account and go to Kubernetes Secret.
Here you can view or download the Secret. It should look like the following:
apiVersion: v1 kind: Secret metadata: name: tjungbau-secure-supply-chain-demo-pull-secret data: .dockerconfigjson: <SECURE> type: kubernetes.io/dockerconfigjson
Copy the content and store it in your ci Namespace.
The name will be probably different in other examples. To be able for the ServiceAccount, which is executing the Pipelines, to use this Secret, we need to modify the ServiceAccount object.
In our case the ServiceAccount is called pipeline. Modify it and add the following lines:
secrets: ... - name: tjungbau-secure-supply-chain-demo-pull-secret (1) imagePullSecrets: - name: tjungbau-secure-supply-chain-demo-pull-secret (2)
1 Use your appropriate name for the secret 2 Use your appropriate name for the secret, required to be able to sign the image Update the Pipeline object and add the following block
This time we do not need to add a Task object, because we are using a ClusterTask "buildah" that takes care of the building process. As workspace "shared-data" is used again, which has all the source code stored:
- name: build-sign-image params: - name: TLSVERIFY value: $(params.TLSVERIFY) - name: BUILD_EXTRA_ARGS value: >- --label=io.openshift.build.commit.author='$(params.COMMIT_AUTHOR)' --label=io.openshift.build.commit.date='$(params.COMMIT_DATE)' --label=io.openshift.build.commit.id='$(params.COMMIT_SHA)' --label=io.openshift.build.commit.message='$(params.COMMIT_MESSAGE)' --label=io.openshift.build.commit.ref='$(params.GIT_REF)' --ulimit=nofile=4096:4096 - name: IMAGE value: '$(params.IMAGE_REPO):$(params.IMAGE_TAG)' retries: 1 runAfter: - scan-source - verify-commit-signature taskRef: kind: ClusterTask name: buildah workspaces: - name: source workspace: shared-data
Update the TriggerBinding globex-ui and define the URL for your registry respectively for your image repository
spec: parameters: - name: imageRepo value: quay.io/tjungbau/secure-supply-chain-demo
With this configuration, we could already build the image and push it into the registry. However, we also would like to sign it. |
Prepare Tekton for Image Signing
To automatically sign images, we use TektonChains which is a controller that allows you to manage your supply chain security by signing TaskRuns and/or OCI images. Once the build-Task has pushed the images to Quay, Tekton will detect this event (by monitoring specific values of Task results) and then creates and pushes the signature for that image.
By default, Tekton Chains observes all task run executions. When the task runs complete, Tekton Chains signs and stores all artefacts.
Tekton Chain is part of OpenShift Pipelines and has the following main features:
You can sign task runs, task run results, and OCI registry images with cryptographic keys that are generated by tools such as cosign and skopeo.
You can use attestation formats such as in-toto.
You can securely store signatures and signed artefacts using OCI repository as a storage backend.
To activate image signing add the following to the TektonConfig object by removing the "chain{}" entry.
chain:
artifacts.oci.storage: oci (1)
artifacts.taskrun.format: in-toto (2)
artifacts.taskrun.storage: oci (3)
transparency.enabled: true (4)
1 | The storage backend for storing OCI signatures. |
2 | The format for storing TaskRun payloads. Can be in-toto or slsa/v1. |
3 | The storage backend for TaskRun signatures. Multiple can be defined. |
4 | Enable or disable automatic binary transparency uploads. The URL for uploading binary transparency attestations is https://rekor.sigstore.dev by default. |
CoSign - Signing the Image
We will use CoSign to sign our image. To do so we need to download the cosign binary and generate a key pair:
Be sure that you are logged into the OpenShift cluster. |
cosign generate-key-pair k8s://openshift-pipelines/signing-secrets
Enter password for private key:
Enter password for private key again:
Successfully created secret signing-secrets in namespace openshift-pipelines
oc get secrets signing-secrets -n openshift-pipelines
NAME TYPE DATA AGE
signing-secrets Opaque 3 46h
Cosign will ask you to enter a password and will then create a Kubernetes secret in the Namespace openshift-pipelines.
When OpenShift Pipelines now execute a Task that is pushing an image, this Secret will be used to sign the image.
Let’s update our source code. The image will be built and pushed into Quay. The small black shield indicates that this image has been signed.
Besides the actual image the files: *.sig and *.att can be seen. The first one is the signature, the second one shows metadata about the attestation retrieved from Rekor which can be compared with the Rekor URL.
Rekor
Rekor’s goals are to provide an immutable tamper resistant ledger of metadata generated within a software projects supply chain. Rekor will enable software maintainers and build systems to record signed metadata to an immutable record. Other parties can then query said metadata to enable them to make informed decisions on trust and non-repudiation of an object’s lifecycle. For more details visit the sigstore website.
The Rekor project provides a restful API based server for validation and a transparency log for storage. A CLI application is available to make and verify entries, query the transparency log for inclusion proof, integrity verification of the transparency log or retrieval of entries by either public key or artifact. Rekor Git
An important feature of Tekton Chains is that it integrates seamlessly with an application called Rekor. The configuration transparency.enabled: true
enables the call to the Rekor API that can be found by default at: https://rekor.sigstore.dev. This will create a transparency log which can be used for verification purposes later.
Of course, it would be possible to install your own server. Learn in the official Docs how this can be achieved. |
This means every time our build-Tasks successfully completes a URL to the transparency logs will be attached to the Task.
After our PipelineRuns were successful, we can verify if the image has an entry in the Rekor transparency log. This is important to ensure that the image went through a valid signing process. At a later step (before we create the pull request for the production update) we will verify if the log exists.
The following annotations should have been added to the TaskRun or PipelineRun secure-supply-chain-XXX-build-sign-image automatically:
metadata:
annotations:
chains.tekton.dev/signed: 'true'
chains.tekton.dev/transparency: 'https://rekor.sigstore.dev/api/v1/log/entries?logIndex=25593495'
This created the following transparency logs:
{
"24296fb24b8ad77a7ae84f4b950336d1872a066aeb9463dfbc231a7d3b73dd040dd5faefb3c013a7": {
"body": "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiJmYzE2ODM3Mzc1ODUyNjc0MTQ4MjIyMDJhMWU2Y2RkZDkxNWI4NWUzYzhhNDVhZmI0YzEyYmE2YmNhMGNmNDQyIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FWUNJUUNRMnlnU2ZGTllHS2pzbXRIYjVielAwdlRNMFgyNHVlNXlKbjJxdWFWSyt3SWhBSmhoSWpweEFybDJERjFsVmpMT0ZzVnRhMzJmbXJXRmxXWkdRQ015b2xrayIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCUVZVSk1TVU1nUzBWWkxTMHRMUzBLVFVacmQwVjNXVWhMYjFwSmVtb3dRMEZSV1VsTGIxcEplbW93UkVGUlkwUlJaMEZGVEc0clduaENNMHNyS3pWSk1pOWlNWFJqVFVKcVZXODRjWGx6YVFwNlQzUlpVbGRQUTBwTVRWQlNWVGR4WTFJeVMyWlZZazAzYlVwUlNtbHZjbXR6VW1KUmNHMTBTekV4WjJNM1pIVkZObGg0WmxOdlpWUkJQVDBLTFMwdExTMUZUa1FnVUZWQ1RFbERJRXRGV1MwdExTMHRDZz09In19fX0=",
"integratedTime": 1688060588,
"logID": "c0d23d6ad406973f9559f3ba2d1ca01f84147d8ffc5b8445c224f98b9591801d",
"logIndex": 25593495,
"verification": {
"inclusionProof": {
"checkpoint": "rekor.sigstore.dev - 2605736670972794746\n21430853\n10sx5mwekx2c8jltWbt0WeJu+tNM9FOBNQoyPiC3CPQ=\nTimestamp: 1688061275289390813\n\n— rekor.sigstore.dev wNI9ajBFAiEAyzFYKKghB+RQsbMIIzjywdA9vFjsusQoMWMUJPRjw3ECIDVeYSGKn7zwXWxGLhQlIGAI1Ca0Gu7ZuLJ64xR3SrY0\n",
"hashes": [
"5cc5dba1f5f420acc9ed049c77b04c4fdd66cf6ef1ebd46b66e26039bb9ba06c",
"3041520c9fec6177225063053503f6d20b09e86b72968b737e3211a233dab6ce",
"ddb57132bba122df920d4816af7ac02bef03aec533cc7b45f8552ce19c023d10",
"8a510da356706a0a822daf3c3a935176d3634c6e0cce6830fe90a8771a3bc83d",
"7d254234192620827306cc5024ffb8ae699bfe2725c8e6aa13757f5471d416d4",
"648a4e45da960f4ad286dfa22ae93721c9ba82ee05edd5a7134d9b19e35735e0",
"9dcf0f21690ec420bffbc1d10c383b7d3dc568d26bd8fd9e8771abb01f5976dc",
"86575ffec011d78ad393e7af267693c0c59360e2299b308369951d8362032733",
"0088ef1f6ec1e992fa6c91837287f2bd7eea238e5af7467e21e6df0dc8efe516",
"b149d95903a4e554ac1c381f1afff31bb62b6e12b6b2fc95f36b8e198e1a42cd",
"de73a694862cebd660ef1cecdd1bb66e273ab0651ca939bf7fa6f5f567bafe93",
"a3c84734ddae3102952584443dea70e3dec2cc085af7cf0ba3530751966861ec",
"0be5c7bbcf481d1efcfc63a27fce447cf6345f7bb3155cf5de39de592c16d52d",
"f597f4bae8df3d6fc6eebfe3eabd7d393e08781f6f16b42398eca8512398fff1",
"4e35fcb3c0a59e7f329994002b38db35f5d511f499ba009e10b31f5d27563607",
"47044b7ac3aab820e44f0010538d7de71e17a11f4140cbbe9eeb37f78b77cc7d",
"eee63677e2591eefe06ab537d6dd1b4060770682744c8287879b5dcd3365a5b2",
"ff41aa21106dbe03996b4335dd158c7ffafd144e45022193de19b2b9136c3e42",
"e6ebdeef2e23335d8d7049ba5a0049a90593efdfe9c1b4548946b44a19d7214f",
"dd51e840e892d70093ad7e1db1e2dea3d50334c7345d360e444d22fc49ed9f5e",
"ad712c98424de0f1284d4f144b8a95b5d22c181d4c0a246518e7a9a220bdf643"
],
"logIndex": 21430064,
"rootHash": "d74b31e66c1e931d9cf2396d59bb7459e26efad34cf45381350a323e20b708f4",
"treeSize": 21430853
},
"signedEntryTimestamp": "MEYCIQDqmqb4k95FjiBNogOAmjTskkIPaGslgvrSND4pQdUALwIhAMt85yLsa5Ei+3NsmJ906/T9Hx1YZDDHQfHlTEYqqcaE"
}
}
}
Summary
Finally, we have a signed image uploaded to Quay. In addition, a transparency log has been created. Now let us add some security checks in the next steps.
Copyright © 2020 - 2024 Toni Schmidbauer & Thomas Jungbauer