13 minutes
Tekton Golang pipeline with Signed Provenance - SLSA Level 2
Tekton Pipelines and Tekton Chains provide many benefits when creating cloud native CD/CD systems. In my previous secure supply chain post, I spoke about what are the different components of Tekton and how we can combine pipelines and chains to create a SLSA level 2 compliant CI/CD pipeline. Today we will dive in deeper and create a more complex GO pipeline that will help us clone, build, run unit tests, create the image and scan the image. Tekton Chains will run along side pipelines in the end to provide us with a signed provenance that will be stored with the image in an OCI registry!
Overview of Tekton Pipelines and Chains
Be sure to check out my previous secure supply chain webinar to get some details on Tekton and all the tools that it provides. Lets have a quick recap of Tekton Pipelines and Chains before we get started.
Tekton Pipelines is an open-source framework for creating Cloud Native CI/CD systems, allowing developers to build, test, and deploy across cloud providers and on-premise systems.
Tekton Chains is a Kubernetes Custom Resource Definition (CRD) controller that allows you to manage your supply chain security in Tekton. It works together with Tekton Pipelines and provides the following captabilities (more to be added in the future)
- Signing TaskRun results with user provided cryptographic keys, including TaskRuns themselves and OCI Images
- Attestation formats like intoto (SLSA Provenance)
- Signing with a variety of cryptograhic key types and services (x509, KMS)
- Support for multiple storage backends for signatures
Deploy and Configure Tekton
Currently the SPIRE agent is deployed as a DaemonSets
where each node has workload API exposed. Each workload would mount the workload API as a hostPath
volume. The motivation for the CSI driver creation was to remove the need for the workload pods to mount the workload API. Thus, only the SPIRE agent pod that contains the CSI driver containers would require the hostPath
volume mounts (to interact with the Kubelet). This is the only limitation of this driver as using an emptyDir
volume would result in the backing directory to be removed if the SPIFFE CSI Driver pod is restarted, invalidating the mount into workload containers.
Pre-requisite:
- A running Kubernetes cluster (Docker Desktop Kubernetes, k3d, minikube)
- Cosign Installed (this will be used for sigining the image)
- If you have Go 1.16+, you can directly install by running:
go install github.com/sigstore/cosign/cmd/cosign@latest
- If you are using Homebrew (or Linuxbrew), you can install cosign by running:
brew install cosign
Deploying Tekton:
Note: You can find a script and configs of this example on my github: tekton-golang-pipeline
First we will install and run Tekton Pipeline and Tekton Chains locally in our cluster before we can create our pipeline. This can be done by:
- Run the following command to install Tekton Pipelines and its dependencies:
kubectl apply --filename https://storage.googleapis.com/tekton-releases/pipeline/latest/release.yaml
- Monitor the installation using the following command until all components show a Running status:
kubectl rollout status -n tekton-pipelines deployment/tekton-pipelines-controller
kubectl rollout status -n tekton-pipelines deployment/tekton-pipelines-webhook
- To install the latest version of Chains to your Kubernetes cluster, run:
kubectl apply --filename https://storage.googleapis.com/tekton-releases/chains/latest/release.yaml
- To verify that installation was successful, wait until all Pods have Status Running:
kubectl rollout status -n tekton-chains deployment/tekton-chains-controller
Note: Tekton Chains current requires the following results to be output from a task that creates an OCI Image: *IMAGE_URL - The URL to the built OCI image *IMAGE_DIGEST - The Digest of the built OCI image
where * indicates any expression. For example, if both MYIMAGE_IMAGE_URL AND MYIMAGE_IMAGE_DIGEST are correctly formatted to point to an OCI image, then chains will pick up on it and try to sign the image.
Configuring Chains
Tekton Chains requires a key pair that will be used for signing. We will use cosign to create this private/public key pair for us and store it as a secret in the tekton-chains
namespace called sigining-secrets
. Tekton Chains will automatically pick up this secret and sign the taskruns and images with it.
cosign generate-key-pair k8s://tekton-chains/signing-secrets
Next we will need to change the default configuration of Tekton Chains to use the in-toto format (which is the current SLSA format) and store the payload and signature into an OCI registry. This can be modified using the below commands.
kubectl patch configmap chains-config -n tekton-chains -p='{"data":{"artifacts.taskrun.format": "in-toto"}}'
kubectl patch configmap chains-config -n tekton-chains -p='{"data":{"artifacts.taskrun.storage": "oci"}}'
Add in Tekton Tasks
We will be utilizing some of the pre-defied tasks in the Tekton Catalog. This is a great starting place for new users to start using tasks, modifying existing to fit their needs or creating their own from pipelines. Specifically we will be using the following task to create out pipeline:
- git-clone
- Clone down the repo to build
-
kubectl apply -f https://raw.githubusercontent.com/tektoncd/catalog/main/task/git-clone/0.5/git-clone.yaml
- golang-test
- Run unit tests on the golang project before building
-
kubectl apply -f https://raw.githubusercontent.com/tektoncd/catalog/main/task/golang-test/0.2/golang-test.yaml
- golang-build
- Build the golang project
-
kubectl apply -f https://raw.githubusercontent.com/tektoncd/catalog/main/task/golang-build/0.3/golang-build.yaml
- trivy-scanner
- Trivy (tri pronounced like trigger, vy pronounced like envy) is a simple and comprehensive scanner for vulnerabilities in container images, file systems, and Git repositories, as well as for configuration issues.
-
kubectl apply -f https://raw.githubusercontent.com/tektoncd/catalog/main/task/trivy-scanner/0.1/trivy-scanner.yaml
- kankio
- Kaniko is used to build container images from a Dockerfile, inside a container or Kubernetes cluster. This is chains compliant and contains the *IMAGE_URL and *IMAGE_DIGEST in the results.
-
kubectl apply -f https://raw.githubusercontent.com/tektoncd/catalog/main/task/kaniko/0.6/kaniko.yaml
Creating our Golang Pipeline
Now that all the Tekton tasks are loading into our kubernetes cluster, we can create the pipelie and pipelinerun to stage and execute the task with specific parameters.
First we need need a pesistent volume claim, that will be used as a shared workspace for the tasks as they run in the pipeline:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pipelinerun-go-test-source-ws-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 500Mi
Next it will be the pipeline itself. This will take the tasks we loaded before and create a pipeline. In the tasks
section below you can see we reference: git-clone, golang-test, golang-build, kaniko and trivy-scanner (once for filesystem scan and the other image scan). The PVC (persistent volume claim) is used as a workspaces
that is mounted with the tasks as a shared work space. The params
section will be used to pass in the variables into the specific tasks. This allows for the pipeline (and subsequently the tasks) to be reusable.
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
name: pipeline-go-test
spec:
workspaces:
- name: pipeline-pvc
optional: false
params:
- name: image
description: reference of the image to build
- name: ARGS
type: array
description: The Arguments to be passed to Trivy command for file system scan
- name: IMAGEARGS
type: array
description: The Arguments to be passed to Trivy command for image scan
- name: package
type: string
default: https://github.com/orchestrait/go-rest-api-test
results:
- name: commit-sha
description: the sha of the commit that was used
value: $(tasks.clone.results.commit)
description: Test Pipeline for Trivy
tasks:
- name: clone
taskRef:
name: git-clone
workspaces:
- name: output
workspace: pipeline-pvc
params:
- name: url
value: $(params.package)
- name: subdirectory
value: ""
- name: deleteExisting
value: "true"
- name: run-test
taskRef:
name: golang-test
runAfter:
- clone
workspaces:
- name: source
workspace: pipeline-pvc
params:
- name: package
value: $(params.package)
- name: GOARCH
value: ""
- name: run-build
taskRef:
name: golang-build
runAfter:
- clone
workspaces:
- name: source
workspace: pipeline-pvc
params:
- name: package
value: $(params.package)
- name: packages
value: ./
- name: kaniko
taskRef:
name: kaniko
runAfter:
- trivy-scan-local-fs
workspaces:
- name: source
workspace: pipeline-pvc
params:
- name: IMAGE
value: $(params.image)
- name: EXTRA_ARGS
value:
- --skip-tls-verify
- name: trivy-scan-local-fs
taskRef:
name: trivy-scanner
kind: Task
runAfter:
- clone
params:
- name: ARGS
value:
- $(params.ARGS[*])
- name: IMAGE_PATH
value: .
workspaces:
- name: manifest-dir
workspace: pipeline-pvc
- name: trivy-scan-image
taskRef:
name: trivy-scanner
kind: Task
runAfter:
- kaniko
params:
- name: ARGS
value:
- $(params.IMAGEARGS[*])
- name: IMAGE_PATH
value: $(params.image)
workspaces:
- name: manifest-dir
workspace: pipeline-pvc
Testing our Golang Pipeline
Now that all the pieces (task, pipeline, pipelinerun) are in place, we can test out our pipeline and see what it outputs.
Visualization
To view the results of the pipelinerun and taskruns, you can use the usual kubectl
commands and interact with the CRDs (kubectl get taskruns
or kubectl get pipelinerun
). Tekton provides two other methods to view the results: Tekton Dashboard and Tekton CLI. This are both optional and not needed for Tekton Pipeline and Chains to work.
Tekton Dashboard
You can install Tekton Dashboard by:
kubectl apply --filename https://storage.googleapis.com/tekton-releases/dashboard/latest/tekton-dashboard-release.yaml
Once the dashboard is running you can run the following command to port-forward the dashboard. This will allow you to view it in your web browser:
kubectl --namespace tekton-pipelines port-forward svc/tekton-dashboard 9097:9097
Browse http://localhost:9097 to access your Dashboard.
Tekton CLI
To install the Tekton CLI, there are multiple methods based on your OS. Navigate to the Tekton CLI repo to install.
Running the demo
In order to instantiate the pipeline and the tasks within it we need to create the pipelinerun. We pass in the specific params
that we will be using for the test run. pipelineRef
references the pipeline-go-test
we created above. Workspaces
maps to the PVC we created pipelinerun-go-test-source-ws-pvc
.
Note: The below PipelineRun is missing the <IMAGE_NAME>
. Be sure to replace this with a repository that you want kaniko to push the image to. This can be docker hub or some other private or public repo that you have access to. In the example, I am using ttl.sh (anonymous & ephemeral Docker image registry). Specifically I used ttl.sh/example-123/go-build-test
but you can anything name for your repo example-123
and image name go-build-test
.
apiVersion: tekton.dev/v1beta1
kind: PipelineRun
metadata:
generateName: pipelinerun-go-test-
labels:
app.kubernetes.io/description: PipelineRun
spec:
params:
- name: image
value: <IMAGE_NAME>
- name: ARGS
value:
- fs
- --exit-code
- "1"
- name: IMAGEARGS
value:
- image
- --exit-code
- "0"
pipelineRef:
name: pipeline-go-test
timeout: 1h0m0s
workspaces:
- name: pipeline-pvc
persistentVolumeClaim:
claimName: pipelinerun-go-test-source-ws-pvc
Save the file accordingly and run kubectl create -f
for the pipelinerun as it gernerateName
a name when its instantiated.
If you initialized the tekton dashboard you can view the pipelinerun from your browser:
You can also view via Tekton CLI using the following command tkn pr describe
:
Name: pipelinerun-go-test-p2tq8
Namespace: default
Pipeline Ref: pipeline-go-test
Service Account: default
Timeout: 1h0m0s
Labels:
app.kubernetes.io/description=PipelineRun
tekton.dev/pipeline=pipeline-go-test
🌡️ Status
STARTED DURATION STATUS
27 minutes ago 2 minutes Succeeded
📦 Resources
No resources
⚓ Params
NAME VALUE
∙ image ttl.sh/example-123/go-build-test
∙ ARGS [fs --exit-code 1]
∙ IMAGEARGS [image --exit-code 0]
📝 Results
NAME VALUE
∙ commit-sha ed7abef5f69b3035a9e741396b7789f21163a71b
📂 Workspaces
NAME SUB PATH WORKSPACE BINDING
∙ pipeline-pvc --- PersistentVolumeClaim (claimName=pipelinerun-go-test-source-ws-pvc)
🗂 Taskruns
NAME TASK NAME STARTED DURATION STATUS
∙ pipelinerun-go-test-p2tq8-trivy-scan-image trivy-scan-image 24 minutes ago 16 seconds Succeeded
∙ pipelinerun-go-test-p2tq8-kaniko kaniko 25 minutes ago 1 minute Succeeded
∙ pipelinerun-go-test-p2tq8-run-build run-build 26 minutes ago 47 seconds Succeeded
∙ pipelinerun-go-test-p2tq8-run-test run-test 26 minutes ago 50 seconds Succeeded
∙ pipelinerun-go-test-p2tq8-trivy-scan-local-fs trivy-scan-local-fs 26 minutes ago 55 seconds Succeeded
∙ pipelinerun-go-test-p2tq8-clone clone 27 minutes ago 16 seconds Succeeded
⏭️ Skipped Tasks
No Skipped Tasks
Note: If you are having trouble getting things to run, you can visit our github to find the full example and script to get it running quickly: tekton-golang-pipeline
Conclusion
Viewing the Results:
Once the pipeline finishes, you can view the logs from each task. For example, you can view the unit tests that have been run either via the tekton dashboard or tekton CLI.
Note: Replace with your pipelinerun name
➜ tkn tr logs pipelinerun-go-test-p2tq8-run-test
[unit-test] ? github.com/chmouel/go-rest-api-test [no test files]
[unit-test] === RUN TestNoConfig
[unit-test] --- PASS: TestNoConfig (0.00s)
[unit-test] === RUN TestNewRouterSingle
[unit-test] 2022/03/16 01:00:19 Adding route: /hello/moto.txt
[unit-test] 2022/03/16 01:00:19 Adding route:
[unit-test] 2022/03/16 01:00:19 GET
[unit-test] 2022/03/16 01:00:19 Adding route: /hello/moto.txt
[unit-test] 2022/03/16 01:00:19 Adding route:
[unit-test] 2022/03/16 01:00:19 GET
[unit-test] 2022/03/16 01:00:19 Adding route: /hello/moto.txt
[unit-test] 2022/03/16 01:00:19 Adding route:
[unit-test] 2022/03/16 01:00:19 PATCH
[unit-test] --- PASS: TestNewRouterSingle (0.00s)
[unit-test] === RUN TestNewRouterDoubles
[unit-test] 2022/03/16 01:00:19 Adding route: /1.txt
[unit-test] 2022/03/16 01:00:19 Adding route: /2.txt
[unit-test] 2022/03/16 01:00:19 GET
[unit-test] 2022/03/16 01:00:19 Adding route: /1.txt
[unit-test] 2022/03/16 01:00:19 Adding route: /2.txt
[unit-test] 2022/03/16 01:00:19 GET
[unit-test] --- PASS: TestNewRouterDoubles (0.01s)
[unit-test] === RUN TestResponseFile
[unit-test] 2022/03/16 01:00:19 Adding route: /testme.txt
[unit-test] 2022/03/16 01:00:19 Adding route:
[unit-test] 2022/03/16 01:00:19 GET
[unit-test] --- PASS: TestResponseFile (0.00s)
[unit-test] PASS
[unit-test] coverage: 88.6% of statements
[unit-test] ok github.com/chmouel/go-rest-api-test/pkg/reflector 0.051s coverage: 88.6% of statements
We can also view the trivy image scans that ran after the image was created:
Note: Replace with your pipelinerun name
➜ tkn tr logs pipelinerun-go-test-p2tq8-trivy-scan-image
[trivy-scan] Running trivy task with command below
[trivy-scan] trivy image --exit-code 0 ttl.sh/example-123/go-build-test
[trivy-scan] 2022-03-16T01:01:33.659Z INFO Need to update DB
[trivy-scan] 2022-03-16T01:01:33.659Z INFO Downloading DB...
[trivy-scan] 2022-03-16T01:01:40.629Z INFO Number of language-specific files: 0
[trivy-scan] 348.05 KiB / 26.43 MiB [>____________________________________________________________] 1.29% ? p/s ?828.02 KiB / 26.43 MiB [->___________________________________________________________] 3.06% ? p/s ?1.36 MiB / 26.43 MiB [--->___________________________________________________________] 5.13% ? p/s ?2.03 MiB / 26.43 MiB [--->_______________________________________________] 7.67% 2.81 MiB p/s ETA 8s2.75 MiB / 26.43 MiB [----->____________________________________________] 10.39% 2.81 MiB p/s ETA 8s3.54 MiB / 26.43 MiB [------>___________________________________________] 13.40% 2.81 MiB p/s ETA 8s4.42 MiB / 26.43 MiB [-------->_________________________________________] 16.71% 2.89 MiB p/s ETA 7s5.42 MiB / 26.43 MiB [---------->_______________________________________] 20.50% 2.89 MiB p/s ETA 7s6.57 MiB / 26.43 MiB [------------>_____________________________________] 24.87% 2.89 MiB p/s ETA 6s7.75 MiB / 26.43 MiB [-------------->___________________________________] 29.30% 3.06 MiB p/s ETA 6s8.98 MiB / 26.43 MiB [---------------->_________________________________] 33.97% 3.06 MiB p/s ETA 5s10.29 MiB / 26.43 MiB [------------------->_____________________________] 38.94% 3.06 MiB p/s ETA 5s11.65 MiB / 26.43 MiB [--------------------->___________________________] 44.08% 3.28 MiB p/s ETA 4s13.15 MiB / 26.43 MiB [------------------------>________________________] 49.76% 3.28 MiB p/s ETA 4s14.71 MiB / 26.43 MiB [--------------------------->_____________________] 55.67% 3.28 MiB p/s ETA 3s16.34 MiB / 26.43 MiB [------------------------------>__________________] 61.82% 3.58 MiB p/s ETA 2s18.21 MiB / 26.43 MiB [--------------------------------->_______________] 68.91% 3.58 MiB p/s ETA 2s19.16 MiB / 26.43 MiB [----------------------------------->_____________] 72.49% 3.58 MiB p/s ETA 2s21.03 MiB / 26.43 MiB [-------------------------------------->__________] 79.56% 3.85 MiB p/s ETA 1s22.43 MiB / 26.43 MiB [----------------------------------------->_______] 84.87% 3.85 MiB p/s ETA 1s24.00 MiB / 26.43 MiB [-------------------------------------------->____] 90.78% 3.85 MiB p/s ETA 0s26.14 MiB / 26.43 MiB [------------------------------------------------>] 98.88% 4.15 MiB p/s ETA 0s26.43 MiB / 26.43 MiB [----------------------------------------------------] 100.00% 6.23 MiB p/s 4s
Verifying the Image and Attestation:
We can use cosign to verify the image and the attestation created by Tekton Chains are signed and stored in OCI registry.
Replace <IMAGE_NAME>
with what was used when running the pipelinerun above.
cosign verify --key k8s://tekton-chains/signing-secrets <IMAGE_NAME>
The output from the verify
will be similar to the below. This shows us that the image is indeed signed by our private key we created at the start with cosign. This also allows us to trust that the image was created by our pipeline.:
➜ cosign verify --key k8s://tekton-chains/signing-secrets ttl.sh/example-123/go-build-test
Verification for ttl.sh/example-123/go-build-test:latest --
The following checks were performed on each of these signatures:
- The cosign claims were validated
- The signatures were verified against the specified public key
- Any certificates were verified against the Fulcio roots.
[{"critical":{"identity":{"docker-reference":"ttl.sh/example-123/go-build-test"},"image":{"docker-manifest-digest":"sha256:96a951e2732c0ea3198e0b83d8a617f79de62cd271dff00a9a832a7b173139d9"},"type":"cosign container image signature"},"optional":null}]
Next we will verify the attestation being created and stored in OCI by Tekton Chains.
Replace <IMAGE_NAME>
with what was used when running the pipelinerun above.
cosign verify-attestation --key k8s://tekton-chains/signing-secrets <IMAGE_NAME>
The output from the verify-attestation
will be similar to the below. The attestation is once again signed by our private key. The payload is base64 encoded and can be decoded to view the attestation for the image created by the pipeline.:
➜ cosign verify-attestation --key k8s://tekton-chains/signing-secrets ttl.sh/example-123/go-build-test
Verification for ttl.sh/example-123/go-build-test --
The following checks were performed on each of these signatures:
- The cosign claims were validated
- The signatures were verified against the specified public key
- Any certificates were verified against the Fulcio roots.
{"payloadType":"application/vnd.in-toto+json","payload":"eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjAuMSIsInByZWRpY2F0ZVR5cGUiOiJodHRwczovL3Nsc2EuZGV2L3Byb3ZlbmFuY2UvdjAuMiIsInN1YmplY3QiOlt7Im5hbWUiOiJ0dGwuc2gvZXhhbXBsZS0xMjMvZ28tYnVpbGQtdGVzdCIsImRpZ2VzdCI6eyJzaGEyNTYiOiI5NmE5NTFlMjczMmMwZWEzMTk4ZTBiODNkOGE2MTdmNzlkZTYyY2QyNzFkZmYwMGE5YTgzMmE3YjE3MzEzOWQ5In19XSwicHJlZGljYXRlIjp7ImJ1aWxkZXIiOnsiaWQiOiJodHRwczovL3Rla3Rvbi5kZXYvY2hhaW5zL3YyIn0sImJ1aWxkVHlwZSI6Imh0dHBzOi8vdGVrdG9uLmRldi9hdHRlc3RhdGlvbnMvY2hhaW5zQHYyIiwiaW52b2NhdGlvbiI6eyJjb25maWdTb3VyY2UiOnt9LCJwYXJhbWV0ZXJzIjp7IkJVSUxERVJfSU1BR0UiOiJnY3IuaW8va2FuaWtvLXByb2plY3QvZXhlY3V0b3I6djEuNS4xQHNoYTI1NjpjNjE2NjcxN2Y3ZmUwYjdkYTQ0OTA4Yzk4NjEzN2VjZmVhYjIxZjMxZWMzOTkyZjZlMTI4ZmZmOGE5NGJlOGE1IiwiQ09OVEVYVCI6Ii4vIiwiRE9DS0VSRklMRSI6Ii4vRG9ja2VyZmlsZSIsIkVYVFJBX0FSR1MiOiJbXSIsIklNQUdFIjoie3N0cmluZyB0dGwuc2gvZXhhbXBsZS0xMjMvZ28tYnVpbGQtdGVzdCBbXX0ifX0sImJ1aWxkQ29uZmlnIjp7InN0ZXBzIjpbeyJlbnRyeVBvaW50IjoiIiwiYXJndW1lbnRzIjpbIiQocGFyYW1zLkVYVFJBX0FSR1MpIiwiLS1kb2NrZXJmaWxlPSQocGFyYW1zLkRPQ0tFUkZJTEUpIiwiLS1jb250ZXh0PSQod29ya3NwYWNlcy5zb3VyY2UucGF0aCkvJChwYXJhbXMuQ09OVEVYVCkiLCItLWRlc3RpbmF0aW9uPSQocGFyYW1zLklNQUdFKSIsIi0tZGlnZXN0LWZpbGU9JChyZXN1bHRzLklNQUdFX0RJR0VTVC5wYXRoKSJdLCJlbnZpcm9ubWVudCI6eyJjb250YWluZXIiOiJidWlsZC1hbmQtcHVzaCIsImltYWdlIjoiZG9ja2VyLXB1bGxhYmxlOi8vZ2NyLmlvL2thbmlrby1wcm9qZWN0L2V4ZWN1dG9yQHNoYTI1NjpjNjE2NjcxN2Y3ZmUwYjdkYTQ0OTA4Yzk4NjEzN2VjZmVhYjIxZjMxZWMzOTkyZjZlMTI4ZmZmOGE5NGJlOGE1In0sImFubm90YXRpb25zIjpudWxsfSx7ImVudHJ5UG9pbnQiOiJzZXQgLWVcbmltYWdlPVwiJChwYXJhbXMuSU1BR0UpXCJcbmVjaG8gXCIke2ltYWdlfVwiIHwgdGVlIFwiJChyZXN1bHRzLklNQUdFX1VSTC5wYXRoKVwiXG4iLCJhcmd1bWVudHMiOm51bGwsImVudmlyb25tZW50Ijp7ImNvbnRhaW5lciI6IndyaXRlLXVybCIsImltYWdlIjoiZG9ja2VyLXB1bGxhYmxlOi8vYmFzaEBzaGEyNTY6YjIwODIxNWE0NjU1NTM4YmU2NTJiMjc2OWQ4MmU1NzZiYzRkMGEyYmIxMzIxNDRjMDYwZWZjNWJlOGMzZjVkNiJ9LCJhbm5vdGF0aW9ucyI6bnVsbH1dfSwibWV0YWRhdGEiOnsiYnVpbGRTdGFydGVkT24iOiIyMDIyLTAzLTE2VDAxOjAwOjI1WiIsImJ1aWxkRmluaXNoZWRPbiI6IjIwMjItMDMtMTZUMDE6MDE6MjVaIiwiY29tcGxldGVuZXNzIjp7InBhcmFtZXRlcnMiOmZhbHNlLCJlbnZpcm9ubWVudCI6ZmFsc2UsIm1hdGVyaWFscyI6ZmFsc2V9LCJyZXByb2R1Y2libGUiOmZhbHNlfX19","signatures":[{"keyid":"SHA256:672YQKcmhEgbhFfvShGrIfhHisvkLNz6z/9YSKXuWhc","sig":"MEUCIDUs1vh+i4lBDl7O6HkLo4ivEFBOGdYlMtUdN/5aTOhhAiEAub+D9tdK/hdfJkz5VLhZdzr+pMFpgeD+i/QIM3VIykk="}]}
All done and verified! All the configs and examples for this blog are located on my github page: tekton-golang-pipeline
Stay tuned for more posts in the future for more tools that combine both automation and security!