Tekton Chains logo

关于 Tekton Chain

Tekton Chain 是一个 Kubernetes CRD controller,可以用来帮助用户管理 Tekton 的 supply chain security(供应链安全)。简单点说,就是更安全的获取 CI/CD Pipeline 执行的元数据,以确保利用 Tekton 构建的 CI/CD Pipeline 执行流程是安全的。

Tekton Chain 的工作原理是:观测 Tekton 集群中所有 TaskRuns 的执行。当 TaskRuns 完成执行,Chains 就会为其做一个快照,随后快照会被转换成一些标准的负载(payload)格式,并在将它们签名后进行存储。目前支持 x509、KMS 等对 TaskRun 以及 OCI 镜像进行签名。

大概原理如下图所示:

tekton-chain-get-started

Tekton Chains 安装

Tekton Chains 依赖Tekton Pipelines。使用如下命令即可完成最新版本的安装:

1
$ kubectl apply --filename https://storage.googleapis.com/tekton-releases/pipeline/latest/release.yaml

可以在 tekton-pipelines namespace 下面看到有 pod 生成:

1
2
3
4
$ kubectl -n tekton-pipelines get pods
NAME                                          READY   STATUS        RESTARTS   AGE
tekton-pipelines-controller-99b764966-bzm9p   1/1     Running       0          5m20s
tekton-pipelines-webhook-55c9dd7446-ssvbw     1/1     Running       0          5m20s

用同样的方式可以完成 Tekton Chains 的安装。执行如下命令即可完成:

1
$ kubectl apply --filename https://storage.googleapis.com/tekton-releases/chains/latest/release.yaml

安装完毕,会在 tekton-chains namespace 下面生成一个 pods:

1
2
3
4
$ kubectl -n tekton-chains get pods -w
NAME                                        READY   STATUS              RESTARTS   AGE
tekton-chains-controller-5948c9fcc8-6gxk5   0/1     ContainerCreating   0          11s
tekton-chains-controller-5948c9fcc8-6gxk5   1/1     Running             0          34s

Tekton Chains 的设置

Tekton Chains 的设置有以下三个部分:

  • 为 Chains controller 添加认证(方便将签名后的镜像推送至 OCI 镜像仓库)
  • 生成一个加密密钥,并配置 Chains 使用此密钥进行签名
  • 其他的额外配置(以 configmap 的形式存储在 tekton-chains namespace 下面,主要包括 TaskRunOCIKMSStorage等的一些配置)

支持的签名密钥

目前支持 x509、Cosign、KMS 以及处于实验阶段的 Keyless signing。本文选择使用 Cosign。

cosign是一个开源项目,主要针对 OCS registry 做容器签名、验证和存储。目的是让签名成为无形的基础设施。

cosign 生成的 key 是以 secret 的方式存储在 Kubernetes 集群上的,在 signing-secrets namespace 下面,可以用如下的命令生成 cosign.keycosigh.pub

1
$ cosign generate-key-pair k8s://tekton-chains/signing-secrets

整个过程会让你输入一个 password,此 password 是用来解密 cosign.key 的。整个过程会在 tekton-chains namespace 下面生成一个名为 signing-secretssecret

1
2
3
$ kubectl -n tekton-chains get secret
NAME                                   TYPE                                  DATA   AGE
signing-secrets                        Opaque                                3      6d3h

可以看到里面有三条数据,其实就是 cosign.keycosign.pubpassword

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
$ apiVersion: v1
data:
  cosign.key: LS0tLS1CRUdJTiBFTkNSWVBURUQgQ09TSUdOIFBSSVZBVEUgS0VZLS0tLS0KZXlKclpHWWlPbnNpYm1GdFpTSTZJbk5qY25sd2RDSXNJbkJoY21GdGN5STZleUpPSWpvek1qYzJPQ3dpY2lJNgpPQ3dpY0NJNk1YMHNJbk5oYkhRaU9pSllVeXRxYlhGYVJWQlVUVXhtYkhkbU9IVkljbTV4ZWpSVE5GVmhXblpSClJVOXVhR3AzU1RVME1XSnpQU0o5TENKamFYQm9aWElpT25zaWJtRnRaU0k2SW01aFkyd3ZjMlZqY21WMFltOTQKSWl3aWJtOXVZMlVpT2lKUlpUSmtVazF1UjJsSlVEZE5hVkEzTURkVloyTlBUR2c1VDFOU1NqUTRkaUo5TENKagphWEJvWlhKMFpYaDBJam9pYTJ4Q1drWXlRMGhLTkdSMVJIQmplQzlQYUVoQ05FbHdNM1ZIYzJWbVJFSnNSVEZ3CldsWmhkR0pOY0djNFlVTkhOM1JZVmxBNFIwdG1SWFU0Y2pFMlkyWnBkVmRKYmsxSVFUVnJNV0ZtUldobU5WVkwKY0dFcmIzSkVVell2UzJzcmFtVjFWMnQxZFdoeWJIWm1TRzFyWkV0MGMzVldWU3MzTkhWbVMzcFNaM0IxYzJWTApPSGN3ZWtWWWNYWkVVazV0T1dWbmRsZGliVTU2VFhsWFp6Vk5ZWGRzZW1oeVNUWlNXak4yUW5JNWFEWjJkMHBXCmNsVlRXVWd4ZFVOVU9HZFNXbGxUZEZaWmVGVkRRM0l3VkVFOVBTSjkKLS0tLS1FTkQgRU5DUllQVEVEIENPU0lHTiBQUklWQVRFIEtFWS0tLS0tCg==
  cosign.password: cGFzc3cwcmQ=
  cosign.pub: LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFdzJVTy95V2VlVi90REcvczZDbDlPVzdWRktxUQpDaUgyZFhPVlc2N3dVV1hoSjhJTTEvSUo4Q1UwdmFGM3kvQ25kZDJUQ1Y5NmVCempxUEdxQlNOZDdBPT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg==
immutable: true
kind: Secret
metadata:
  name: signing-secrets
  namespace: tekton-chains
type: Opaque

Tekton Chain 的使用

首先需要对 artifacts.oci.storage 参数做一些修改,目的是为了告诉 Chains 使用默认的 tekton artifact 并且禁用 OCI artifact。执行如下命令即可完成设置:

1
$ kubectl patch configmap chains-config -n tekton-chains -p='{"data":{"artifacts.oci.storage": ""}}'

接下来使用 https://github.com/majinghe/tekton-chains-demo 中代码创建一个 taksrun,执行步骤都在此代码 repo 里面。这是一个拉取源代码并且构建容器镜像的 taskrun。创建 taskrun 的最后两步为执行如下命令:

1
2
3
4
5
$ kubectl -n tekton-demo apply -f task.yaml
task.tekton.dev/build-docker-image created

kubectl -n tekton-demo apply -f taskrun.yaml
taskrun.tekton.dev/build-docker-image-run created

查询 taskruntask

1
2
3
4
5
6
7
$ tkn -n tekton-demo task list
NAME                 DESCRIPTION   AGE
build-docker-image                 1 minute ago

$ tkn -n tekton-demo tr list
NAME                     STARTED        DURATION   STATUS
build-docker-image-run   1 minute ago   ---        Running

可以看到名为 build-docker-image-runtaskrun 以及名为 build-docker-imagetask 已经创建成功,并且在运行了(STATUS 为 Running)。可以在 tekton-demo namespace 下面看到相应的 pod

1
2
3
4
5
$ kubectl -n tekton-demo get pods -w
NAME                         READY   STATUS     RESTARTS   AGE
build-docker-image-run-pod   2/4     NotReady   0          2m47s
build-docker-image-run-pod   1/4     NotReady   0          3m7s
build-docker-image-run-pod   0/4     Completed   0          3m8s

可以看到 taksrun 已经执行结束(pod STATUS 为 Completed)。可以再次查看:

1
2
3
4
5
6
7
$ tkn -n tekton-demo task list
NAME                 DESCRIPTION   AGE
build-docker-image                 4 minutes ago

$ tkn -n tekton-demo tr list
NAME                     STARTED         DURATION    STATUS
build-docker-image-run   4 minutes ago   3 minutes   Succeeded

接下来就是 Tekton Chains 上场的时间了。在上述 TaskRuns 执行结束后,Chains 会对 TaskRuns 做一个快照,并将快照转换成标准的负载格式,然后将其进行签名,最后将签名结果进行存储(诸如存储在 metadata.annotations 中)。

TaskRun 的名称以环境变量的形式注入:

1
$ export TASKRUN=build-docker-image-run

获取 TaskRun 的具体信息

1
2
3
$ kubectl -n tekton-demo get taskrun $TASKRUN
NAME                     SUCCEEDED   REASON      STARTTIME   COMPLETIONTIME
build-docker-image-run   True        Succeeded   18m         15m

接下来就可以从 TaskRuns 获取签名信息和负载了(signature 和 payload,以 base64 的形式存储在 annotations 中了):

1
2
3
$ export TASKRUN_UID=$(kubectl -n tekton-demo get taskrun $TASKRUN -o=json | jq -r '.metadata.uid')
$ kubectl -n tekton-demo get taskrun $TASKRUN -o=json | jq  -r ".metadata.annotations[\"chains.tekton.dev/payload-taskrun-$TASKRUN_UID\"]" | base64 --decode > payload
$ kubectl -n tekton-demo get taskrun $TASKRUN -o=json | jq  -r ".metadata.annotations[\"chains.tekton.dev/signature-taskrun-$TASKRUN_UID\"]" | base64 --decode > signature

上述操作将 payload 和 signature 存储在了两个文件中:

1
2
-rw-r--r--  1 xiaomage  staff     3 Dec 18 20:14 payload
-rw-r--r--  1 xiaomage  staff     3 Dec 18 20:14 signature

最后,使用 cosign 命令进行校验即可:

1
2
$ cosign verify-blob --key cosign.pub --signature ./signature ./payload
Verified OK

可以看到验证结果是成功的,证明此次 TaskRun 的构建过程没有遭受篡改。这就是 Tekton Chains 功能的核心原理。