go

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:

  1. A running Kubernetes cluster (Docker Desktop Kubernetes, k3d, minikube)
  2. 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:

  1. Run the following command to install Tekton Pipelines and its dependencies:
kubectl apply --filename https://storage.googleapis.com/tekton-releases/pipeline/latest/release.yaml
  1. 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
  1. 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
  1. 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:

tekton_dashboard

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!

Reference:

(1) https://github.com/tektoncd/pipeline

(2) https://github.com/tektoncd/chains