Build System Provenance Verification
在 SLSA provenance 中,有一个 builder.id
字段,用于指示镜像的构建环境。
本文档将使用该 builder.id
字段来验证镜像。
TIP
由于 Tekton Chains 已经在准备阶段处理了镜像签名和 SLSA provenance 的生成,我们可以直接复用快速开始:签名溯源中的流程和镜像。
本文档重点关注验证 SLSA provenance。
目录
功能概述
该方法使用 Chains 自动为构建的镜像生成 SLSA Provenance,然后使用 Kyverno 验证该溯源:
- 配置 Tekton Chains 自动为构建的镜像生成 SLSA Provenance。
- 使用
buildah
Tekton 任务构建镜像。
- (可选)使用
cosign
CLI 验证签名。
- 配置 Kyverno 规则验证签名。
- 使用该镜像创建 Pod 以验证签名。
使用场景
以下场景需要参考本文档的指导:
- 使用 SLSA provenance 验证容器镜像的构建环境
- 使用 CUE 或 Rego 策略实现构建系统溯源验证
- 强制执行安全策略,仅允许特定构建环境构建的镜像
- 在 CI/CD 流水线中设置自动化构建系统溯源验证
- 确保生产环境中构建系统溯源的完整性和真实性
前提条件
- 已安装 Tekton Pipelines、Tekton Chains 和 Kyverno 的 Kubernetes 集群
- 支持镜像推送的镜像仓库
- 已安装并配置好访问集群的
kubectl
CLI
- 已安装
cosign
CLI 工具
- 已安装
jq
CLI 工具
流程概览
步骤 | 操作 | 说明 |
---|
1 | 生成签名密钥 | 使用 cosign 创建用于签名工件的密钥对 |
2 | 设置认证 | 配置镜像仓库凭证以支持镜像推送 |
3 | 配置 Tekton Chains | 配置 Chains 使用 OCI 存储并设置签名 |
4 | 创建示例流水线 | 创建包含必要任务和工作空间的流水线定义 |
5 | 运行示例流水线 | 创建并运行配置正确的 PipelineRun |
6 | 等待签名 | 等待 PipelineRun 被 Chains 签名 |
7 | 获取镜像信息 | 从 PipelineRun 中提取镜像 URI 和摘要 |
8 | (可选)使用 cosign 验证签名 | 使用 cosign CLI 验证镜像签名 |
9 | 使用 Kyverno 验证签名 | 配置并使用 Kyverno 策略验证镜像签名 |
10 | 清理资源 | 删除测试 Pod 和策略 |
逐步操作指南
步骤 1-7:(可选)基础设置
NOTE
如果更改了 builder.id
字段,需要重新运行流水线生成镜像。
因为旧镜像未使用新的 builder.id
签名,会被策略阻止。
否则,可以跳过此步骤,使用旧镜像验证策略。
这些步骤与快速开始:签名溯源指南完全相同。请按照该指南完成:
步骤 8:(可选)使用 cosign 验证 builder 信息
TIP
此步骤为可选,适用于需要使用 cosign 验证镜像构建者真实性的场景。
如果想了解如何使用 CUE
或 Rego
验证 builder 信息,可继续阅读以下内容。
根据获取签名公钥章节获取签名公钥。
Cosign 提供两种方式来验证签名声明:
以下展示这两种方式的验证方法。
方式一:使用 CUE 验证
生成用于验证 builder 信息的 CUE 文件。
// 断言必须满足以下约束。
predicate: {
builder: {
id: "https://alauda.io/builders/tekton/v1"
}
}
将该 CUE 文件保存为 builder.cue
使用 cosign 验证 builder 信息。
# 禁用 tlog 上传并启用私有基础设施
$ export COSIGN_TLOG_UPLOAD=false
$ export COSIGN_PRIVATE_INFRASTRUCTURE=true
$ export IMAGE=<<registry>/test/chains/demo-1:latest@sha256:93635f39cb31de5c6988cdf1f10435c41b3fb85570c930d51d41bbadc1a90046>
$ cosign verify-attestation --key cosign.pub --type slsaprovenance --policy builder.cue $IMAGE
若输出如下,表示 builder 信息验证成功。
will be validating against CUE policies: [builder.cue]
will be validating against CUE policies: [builder.cue]
Verification for <registry>/test/chains/demo-1:latest@sha256:8ac1af8dd89652bf32abbbd0c5f667ae9fe6d92c91972617e70b5398303c8e27 --
The following checks were performed on each of these signatures:
- The cosign claims were validated
- The signatures were verified against the specified public key
{"payloadType":"application/vnd.in-toto+json","payload":"","signatures":[]}
将 builder.cue
文件中的 builder id 改为另一个值 https://alauda.io/builders/tekton/v2
,再次验证。
$ cosign verify-attestation --key cosign.pub --type slsaprovenance --policy builder.cue $IMAGE
若输出如下,表示 builder 信息验证失败。
will be validating against CUE policies: [builder.cue]
will be validating against CUE policies: [builder.cue]
There are 2 number of errors occurred during the validation:
- predicate.builder.id: conflicting values "https://alauda.io/builders/tekton/v1" and "https://alauda.io/builders/tekton/v2"
- predicate.builder.id: conflicting values "https://alauda.io/builders/tekton/v1" and "https://alauda.io/builders/tekton/v2"
Error: 2 validation errors occurred
error during command execution: 2 validation errors occurred
方式二:使用 Rego 验证
生成用于验证 builder 信息的 Rego 文件。
package signature
default allow = false
# 定义允许的 builder.id
allowed_builder_id = "https://alauda.io/builders/tekton/v1"
# 验证 builder.id
allow {
# 检查断言中的 builder.id 是否等于允许值
input.predicate.builder.id == allowed_builder_id
}
# 不匹配时返回错误信息
deny[msg] {
input.predicate.builder.id != allowed_builder_id
msg := sprintf("unexpected builder.id: %v, expected: %v", [input.predicate.builder.id, allowed_builder_id])
}
将该 Rego 文件保存为 builder.rego
使用 cosign 验证 builder 信息。
# 禁用 tlog 上传并启用私有基础设施
$ export COSIGN_TLOG_UPLOAD=false
$ export COSIGN_PRIVATE_INFRASTRUCTURE=true
$ export IMAGE=<<registry>/test/chains/demo-1:latest@sha256:93635f39cb31de5c6988cdf1f10435c41b3fb85570c930d51d41bbadc1a90046>
$ cosign verify-attestation --key cosign.pub --type slsaprovenance --policy builder.rego $IMAGE
若输出如下,表示 builder 信息验证成功。
will be validating against Rego policies: [builder.rego]
will be validating against Rego policies: [builder.rego]
Verification for <registry>/test/chains/demo-1: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
{"payloadType":"application/vnd.in-toto+json","payload":"","signatures":[]}
将 builder.rego
文件中的 builder id 改为另一个值 https://alauda.io/builders/tekton/v2
,再次验证。
$ cosign verify-attestation --key cosign.pub --type slsaprovenance --policy builder.rego $IMAGE
若输出如下,表示 builder 信息验证失败。
will be validating against Rego policies: [builder.rego]
will be validating against Rego policies: [builder.rego]
There are 2 number of errors occurred during the validation:
- expression value, false, is not true
- expression value, false, is not true
Error: 2 validation errors occurred
error during command execution: 2 validation errors occurred
步骤 9:使用 Kyverno 验证签名
溯源内容大致如下,我们将使用 builder.id
字段验证构建环境。
{
"_type": "https://in-toto.io/Statement/v0.1",
"predicateType": "https://slsa.dev/provenance/v0.2",
"predicate": {
"buildType": "tekton.dev/v1beta1/TaskRun",
"builder": {
"id": "https://alauda.io/builders/tekton/v1"
},
"materials": [
{
"digest": {
"sha256": "8d5ea9ecd9b531e798fecd87ca3b64ee1c95e4f2621d09e893c58ed593bfd4c4"
},
"uri": "oci://<registry>/devops/tektoncd/hub/buildah"
}
],
"metadata": {
"buildFinishedOn": "2025-06-06T10:21:27Z",
"buildStartedOn": "2025-06-06T10:20:55Z"
}
}
}
步骤 9.1:创建 Kyverno 策略,仅允许特定构建环境构建的镜像部署
策略如下:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-tekton-built-images
spec:
webhookConfiguration:
failurePolicy: Fail
timeoutSeconds: 30
background: false
rules:
- name: check-image
match:
any:
- resources:
kinds:
- Pod
namespaces:
- policy
verifyImages:
- imageReferences:
- "*"
# - "<registry>/test/*"
skipImageReferences:
- "ghcr.io/trusted/*"
failureAction: Enforce
verifyDigest: false
required: false
useCache: false
imageRegistryCredentials:
allowInsecureRegistry: true
secrets:
# 凭证需存在于 Kyverno 部署的命名空间
- registry-credentials
attestations:
- type: https://slsa.dev/provenance/v0.2
attestors:
- entries:
- keys:
publicKeys: |- # <- 签名者的公钥
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEFZNGfYwn7+b4uSdEYLKjxWi3xtP3
UkR8hQvGrG25r0Ikoq0hI3/tr0m7ecvfM75TKh5jGAlLKSZUJpmCGaTToQ==
-----END PUBLIC KEY-----
ctlog:
ignoreSCT: true
rekor:
ignoreTlog: true
conditions:
- all:
- key: "{{ builder.id }}"
operator: Equals
value: "https://alauda.io/builders/tekton/v1"
message: "The builder.id must be equal to https://alauda.io/builders/tekton/v1, not {{ builder.id }}"
YAML 字段说明
- 该策略与镜像签名验证中的策略大致相同,仅介绍差异部分。
spec.rules[0].verifyImages[].attestations[0].conditions
type
:slsa 溯源类型为 https://slsa.dev/provenance/v0.2
或 https://slsa.dev/provenance/v1
。
attestors
:同上。
conditions
:需要验证的条件。
all
:所有条件必须满足。
key: "{{ builder.id }}"
:检查签名声明中的 builder.id
字段是否等于 https://alauda.io/builders/tekton/v1
需要调整的配置
spec.rules[].attestors[].entries[].keys.publicKeys
:签名者的公钥。
- 该公钥与
signing-secrets
Secret 中的公钥 cosign.pub
相同。
- 公钥可从获取签名公钥章节获得。
将策略保存为 kyverno.verify-tekton-built-images.yaml
文件,并应用:
$ kubectl apply -f kyverno.verify-tekton-built-images.yaml
clusterpolicy.kyverno.io/verify-tekton-built-images configured
步骤 9.2:验证策略
在定义策略的 policy
命名空间中,创建 Pod 以验证策略。
使用构建好的镜像创建 Pod。
$ export NAMESPACE=<policy>
$ export IMAGE=<<registry>/test/chains/demo-1:latest@sha256:93635f39cb31de5c6988cdf1f10435c41b3fb85570c930d51d41bbadc1a90046>
$ kubectl run -n $NAMESPACE built --image=${IMAGE} -- sleep 3600
pod/built created
Pod 会成功创建。
$ kubectl get pod -n $NAMESPACE built
NAME READY STATUS RESTARTS AGE
built 1/1 Running 0 10s
将 ClusterPolicy
中的 builder id 改为另一个值 https://alauda.io/builders/tekton/v2
,再次验证。
conditions:
- all:
- key: "{{ builder.id }}"
operator: Equals
value: "https://alauda.io/builders/tekton/v2"
message: "The builder.id must be equal to https://alauda.io/builders/tekton/v2, not {{ builder.id }}"
$ kubectl run -n $NAMESPACE unbuilt --image=${IMAGE} -- sleep 3600
若输出如下,表示 Pod 被策略阻止。
Error from server: admission webhook "mutate.kyverno.svc-fail" denied the request:
resource Pod/policy/unbuilt was blocked due to the following policies
verify-tekton-built-images:
check-image: 'image attestations verification failed, verifiedCount: 0, requiredCount:
1, error: .attestations[0].attestors[0].entries[0].keys: attestation checks failed
for <registry>/test/chains/demo-1@sha256:93635f39cb31de5c6988cdf1f10435c41b3fb85570c930d51d41bbadc1a90046
and predicate https://slsa.dev/provenance/v0.2: The builder.id must be equal to
https://alauda.io/builders/tekton/v2, not https://alauda.io/builders/tekton/v1'
步骤 10:清理资源
删除前面步骤中创建的 Pod。
$ export NAMESPACE=<policy>
$ kubectl delete pod -n $NAMESPACE built
删除策略。
$ kubectl delete clusterpolicy verify-tekton-built-images
预期结果
完成本指南后:
- 已成功搭建 Tekton Chains 生成 SLSA provenance 的环境
- 容器镜像在构建过程中自动带有构建系统溯源签名
- 可使用 CUE 或 Rego 策略验证镜像的构建环境
- 仅允许指定构建环境构建的镜像在指定命名空间部署
- 非授权构建环境构建的镜像会被 Kyverno 策略自动阻止
- 实现了容器镜像的基础构建系统溯源验证控制
本指南为在 CI/CD 流水线中实现构建系统溯源验证提供了基础。在生产环境中,建议:
- 配置合理的命名空间隔离和访问控制
- 实施安全的签名密钥管理
- 设置策略违规的监控和告警
- 定期轮换签名密钥并更新安全策略
- 考虑增加漏洞扫描等额外安全控制
参考资料