基础镜像和SBOM验证
如果我们只允许部署特定类型的基础镜像,
可以在获取镜像后将该信息保存到镜像认证中。
在漏洞扫描与验证中,cosign-vuln
格式的认证已经包含了基础镜像信息。
但这里我们将采用不同的方法,使用 syft
为镜像生成 SBOM。
SBOM 信息同样包含基础镜像信息。
在 ACP(Alauda Container Platform)中,可以使用 Tekton Pipeline 中的 trivy
或 syft
任务为镜像生成 SBOM。
这里我们使用 syft 任务生成 SBOM。
目录
功能概述
该方法使用类似 syft 的工具为镜像生成 SBOM,然后使用 Kyverno 验证 SBOM:
- 使用
syft
Tekton 任务为镜像生成 SBOM 并附加到镜像。
- 配置 Kyverno 规则验证 SBOM。
- 使用该镜像创建 Pod 以验证 SBOM。
使用场景
以下场景需要参考本文档的指导:
- 在 Kubernetes 集群中使用 Kyverno 实现基础镜像验证
- 强制安全策略,仅允许特定基础镜像部署
- 在 CI/CD 流水线中设置自动 SBOM 生成和验证
- 确保生产环境中基础镜像的合规性
- 通过验证容器镜像的基础镜像信息,实现供应链安全控制
前提条件
- 已安装 Tekton Pipelines、Tekton Chains 和 Kyverno 的 Kubernetes 集群
- 支持镜像推送的镜像仓库
- 已安装并配置好访问集群的
kubectl
CLI
- 已安装
cosign
CLI 工具
- 已安装
jq
CLI 工具
流程概览
步骤 | 操作 | 说明 |
---|
1 | 生成签名密钥 | 使用 cosign 创建用于签名工件的密钥对 |
2 | 设置认证 | 配置镜像推送的仓库凭证 |
3 | 配置 Tekton Chains | 配置 Chains 使用 OCI 存储和签名,禁用 TaskRun SLSA Provenance |
4 | 创建示例流水线 | 创建包含 syft 任务生成 SBOM 的流水线定义 |
5 | 运行示例流水线 | 创建并运行配置正确的 PipelineRun |
6 | 等待签名 | 等待 PipelineRun 被 Chains 签名 |
7 | 获取镜像信息 | 从 PipelineRun 中提取镜像 URI 和摘要 |
8 | (可选)获取 SBOM 认证 | 获取并验证 SBOM 认证 |
9 | 使用 Kyverno 验证 | 创建并应用 Kyverno 策略验证基础镜像信息 |
10 | 清理 | 删除测试资源和策略 |
详细步骤说明
步骤 1-3:基础设置
这些步骤与快速开始:签名溯源指南相同。请按照该指南完成:
步骤 4:创建示例流水线
这是一个 Pipeline 资源,用于构建镜像并生成 SBOM。
apiVersion: tekton.dev/v1
kind: Pipeline
metadata:
name: chains-demo-5
spec:
params:
- default: |-
echo "Generate a Dockerfile for building an image."
cat << 'EOF' > Dockerfile
FROM ubuntu:latest
ENV TIME=1
EOF
echo -e "\nDockerfile contents:"
echo "-------------------"
cat Dockerfile
echo "-------------------"
echo -e "\nDockerfile generated successfully!"
description: A script to generate a Dockerfile for building an image.
name: generate-dockerfile
type: string
- default: <registry>/test/chains/demo-5:latest
description: The target image address built
name: image
type: string
results:
- description: first image artifact output
name: first_image_ARTIFACT_OUTPUTS
type: object
value:
digest: $(tasks.build-image.results.IMAGE_DIGEST)
uri: $(tasks.build-image.results.IMAGE_URL)
tasks:
- name: generate-dockerfile
params:
- name: script
value: $(params.generate-dockerfile)
taskRef:
params:
- name: kind
value: task
- name: catalog
value: catalog
- name: name
value: run-script
- name: version
value: "0.1"
resolver: hub
timeout: 30m0s
workspaces:
- name: source
workspace: source
- name: build-image
params:
- name: IMAGES
value:
- $(params.image)
- name: TLS_VERIFY
value: "false"
runAfter:
- generate-dockerfile
taskRef:
params:
- name: kind
value: task
- name: catalog
value: catalog
- name: name
value: buildah
- name: version
value: "0.9"
resolver: hub
timeout: 30m0s
workspaces:
- name: source
workspace: source
- name: dockerconfig
workspace: dockerconfig
- name: syft-sbom
params:
- name: COMMAND
value: |-
set -x
mkdir -p .git
echo "Generate sbom.json"
syft scan $(tasks.build-image.results.IMAGE_URL)@$(tasks.build-image.results.IMAGE_DIGEST) -o cyclonedx-json=.git/sbom.json > /dev/null
echo -e "\n\n"
cat .git/sbom.json
echo -e "\n\n"
echo "Generate and Attestation sbom"
syft attest $(tasks.build-image.results.IMAGE_URL)@$(tasks.build-image.results.IMAGE_DIGEST) -o cyclonedx-json
runAfter:
- build-image
taskRef:
params:
- name: kind
value: task
- name: catalog
value: catalog
- name: name
value: syft
- name: version
value: "0.1"
resolver: hub
timeout: 30m0s
workspaces:
- name: source
workspace: source
- name: dockerconfig
workspace: dockerconfig
- name: signkey
workspace: signkey
workspaces:
- name: source
description: The workspace for source code.
- name: dockerconfig
description: The workspace for Docker configuration.
- name: signkey
description: The workspace for private keys and passwords used for image signatures.
TIP
本教程通过在流水线内联生成 Dockerfile
和 git-clone
任务输出,演示了简化的工作流。
在生产环境中,通常会:
- 使用
git-clone
任务从代码仓库拉取源代码
- 使用源代码中的 Dockerfile 构建镜像
- 这样可以确保版本控制的正确性,并保持代码与流水线配置的分离
YAML 字段说明
- 与步骤 4:创建示例流水线相同,但增加了以下内容:
workspaces
:
signkey
: 用于镜像签名的私钥和密码的工作区。
tasks
:
syft-sbom
: 生成镜像 SBOM 并上传认证的任务。
保存为 chains-demo-5.yaml
文件并应用:
$ export NAMESPACE=<default>
# 在命名空间中创建流水线
$ kubectl create -n $NAMESPACE -f chains-demo-5.yaml
pipeline.tekton.dev/chains-demo-5 created
步骤 5:运行示例流水线
这是一个 PipelineRun 资源,用于运行流水线。
apiVersion: tekton.dev/v1
kind: PipelineRun
metadata:
generateName: chains-demo-5-
spec:
pipelineRef:
name: chains-demo-5
taskRunTemplate:
serviceAccountName: <default>
workspaces:
- name: dockerconfig
secret:
secretName: <registry-credentials>
- name: source
volumeClaimTemplate:
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
storageClassName: <nfs>
YAML 字段说明
- 与步骤 5:运行示例流水线相同,以下仅介绍差异。
workspaces
signkey
:签名密钥的 Secret 名称。
secret.secretName
:前一步获取签名密钥中准备的签名 Secret。但需要创建一个与流水线运行命名空间相同的 Secret。
保存为 chains-demo-5.pipelinerun.yaml
文件并应用:
$ export NAMESPACE=<default>
# 在命名空间中创建流水线运行
$ kubectl create -n $NAMESPACE -f chains-demo-5.pipelinerun.yaml
等待 PipelineRun 完成。
$ kubectl get pipelinerun -n $NAMESPACE -w
chains-demo-5-<xxxxx> True Succeeded 2m 2m
步骤 6:等待 PipelineRun 签名完成
等待 PipelineRun 带有 chains.tekton.dev/signed: "true"
注解。
$ export NAMESPACE=<default>
$ export PIPELINERUN_NAME=<chains-demo-5-xxxxx>
$ kubectl get pipelinerun -n $NAMESPACE $PIPELINERUN_NAME -o yaml | grep "chains.tekton.dev/signed"
chains.tekton.dev/signed: "true"
当 PipelineRun 带有 chains.tekton.dev/signed: "true"
注解时,表示镜像已签名。
步骤 7:从 PipelineRun 获取镜像信息
# 获取镜像 URI
$ export IMAGE_URI=$(kubectl get pipelinerun -n $NAMESPACE $PIPELINERUN_NAME -o jsonpath='{.status.results[?(@.name=="first_image_ARTIFACT_OUTPUTS")].value.uri}')
# 获取镜像摘要
$ export IMAGE_DIGEST=$(kubectl get pipelinerun -n $NAMESPACE $PIPELINERUN_NAME -o jsonpath='{.status.results[?(@.name=="first_image_ARTIFACT_OUTPUTS")].value.digest}')
# 组合镜像 URI 和摘要,形成完整镜像引用
$ export IMAGE=$IMAGE_URI@$IMAGE_DIGEST
# 打印镜像引用
$ echo $IMAGE
<registry>/test/chains/demo-5:latest@sha256:a6c727554be7f9496e413a789663060cd2e62b3be083954188470a94b66239c7
该镜像将用于验证 SBOM。
步骤 8:(可选)获取 SBOM 认证
根据获取签名公钥章节获取签名公钥。
# 禁用 tlog 上传并启用私有基础设施
$ export COSIGN_TLOG_UPLOAD=false
$ export COSIGN_PRIVATE_INFRASTRUCTURE=true
$ export IMAGE=<<registry>/test/chains/demo-5:latest@sha256:a6c727554be7f9496e413a789663060cd2e62b3be083954188470a94b66239c7>
$ cosign verify-attestation --key cosign.pub --type cyclonedx $IMAGE | jq -r '.payload | @base64d' | jq -s
输出类似如下,包含镜像的组件信息。
cyclonedx SBOM 认证
{
"_type": "https://in-toto.io/Statement/v0.1",
"predicateType": "https://cyclonedx.org/bom",
"predicate": {
"$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",
"bomFormat": "CycloneDX",
"components": [
{
"bom-ref": "os:ubuntu@24.04",
"licenses": [
{
"license": {
"name": "GPL"
}
}
],
"description": "Ubuntu 24.04.2 LTS",
"name": "ubuntu",
"type": "operating-system",
"version": "24.04"
}
],
"metadata": {
"timestamp": "2025-06-07T09:56:05Z",
"tools": {
"components": [
{
"author": "anchore",
"name": "syft",
"type": "application",
"version": "1.23.1"
}
]
}
}
}
}
字段说明
predicateType
:谓词类型。
predicate
:
components
:镜像组件。
bom-ref
:组件的 BOM 引用。
licenses
:组件的许可证。
name
:组件名称。
type
:组件类型。
version
:组件版本。
metadata
:镜像元数据。
timestamp
:时间戳。
tools
:
components
:工具组件。
author
:工具作者。
name
:工具名称。
type
:工具类型。
version
:工具版本。
步骤 9:验证基础镜像信息
步骤 9.1:创建 Kyverno 策略验证基础镜像信息
策略如下:
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-base-image
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://cyclonedx.org/bom
attestors:
- entries:
- attestor:
keys:
publicKeys: |- # <- 签名者的公钥
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEFZNGfYwn7+b4uSdEYLKjxWi3xtP3
UkR8hQvGrG25r0Ikoq0hI3/tr0m7ecvfM75TKh5jGAlLKSZUJpmCGaTToQ==
-----END PUBLIC KEY-----
ctlog:
ignoreSCT: true
rekor:
ignoreTlog: true
conditions:
- any:
- key: "{{ components[?type=='operating-system'] | [?name=='ubuntu' && (version=='22.04' || version=='24.04')] | length(@) }}"
operator: GreaterThan
value: 0
message: "操作系统必须是 Ubuntu 22.04 或 24.04,而非 {{ components[?type=='operating-system'].name[] }} {{ components[?type=='operating-system'].version[] }}"
- key: "{{ components[?type=='operating-system'] | [?name=='alpine' && (version=='3.18' || version=='3.20')] | length(@) }}"
operator: GreaterThan
value: 0
message: "操作系统必须是 Alpine 3.18 或 3.20,而非 {{ components[?type=='operating-system'].name[] }} {{ components[?type=='operating-system'].version[] }}"
PkgIDs: {{ scanner.result.Results[].Vulnerabilities[?CVSS.redhat.V3Score > `1.0`].PkgID[] }}.
YAML 字段说明
- 策略与镜像签名验证中的大致相同。
spec.rules[0].verifyImages[].attestations[0].conditions
type
:cyclonedx SBOM 认证类型为 https://cyclonedx.org/bom
attestors
:同上。
conditions
:验证条件。
any
:满足任一条件即可。
key: "{{ components[?type=='operating-system'] | [?name=='ubuntu' && (version=='22.04' || version=='24.04')] | length(@) }}"
:操作系统必须是 Ubuntu 22.04 或 24.04。
key: "{{ components[?type=='operating-system'] | [?name=='alpine' && (version=='3.18' || version=='3.20')] | length(@) }}"
:操作系统必须是 Alpine 3.18 或 3.20。
保存策略为 kyverno.verify-base-image.yaml
并应用:
$ kubectl create -f kyverno.verify-base-image.yaml
clusterpolicy.kyverno.io/verify-base-image created
步骤 9.2:验证策略
在定义策略的 policy
命名空间中,创建 Pod 以验证策略。
使用构建好的镜像创建 Pod。
$ export NAMESPACE=<policy>
$ export IMAGE=<<registry>/test/chains/demo-5:latest@sha256:a6c727554be7f9496e413a789663060cd2e62b3be083954188470a94b66239c7>
$ kubectl run -n $NAMESPACE base-image --image=${IMAGE} -- sleep 3600
如果基础镜像是 Ubuntu 22.04 或 24.04,Pod 将成功创建。
将 ClusterPolicy
中的条件修改为只允许 Alpine 3.18 或 3.20。
conditions:
- any:
- key: "{{ components[?type=='operating-system'] | [?name=='alpine' && (version=='3.18' || version=='3.20')] | length(@) }}"
operator: GreaterThan
value: 0
message: "操作系统必须是 Alpine 3.18 或 3.20,而非 {{ components[?type=='operating-system'].name[] }} {{ components[?type=='operating-system'].version[] }}"
然后创建 Pod 验证策略。
$ kubectl run -n $NAMESPACE deny-base-image --image=${IMAGE} -- sleep 3600
将收到如下输出:
Error from server: admission webhook "mutate.kyverno.svc-fail" denied the request:
resource Pod/policy/deny-base-image was blocked due to the following policies
verify-base-image:
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-5:latest and predicate https://cyclonedx.org/bom:
The operating system must be Alpine 3.18 or 3.20, not ["ubuntu"] ["24.04"]'
步骤 10:清理资源
删除前面步骤中创建的 Pod。
$ export NAMESPACE=<policy>
$ kubectl delete pod -n $NAMESPACE base-image
删除策略。
$ kubectl delete clusterpolicy verify-base-image
预期结果
完成本指南后:
- 您已成功搭建 Tekton Chains 生成 SBOM 和 Kyverno 验证基础镜像的环境
- 容器镜像自动包含 SBOM 信息的认证
- 仅允许符合要求的基础镜像在指定命名空间中部署
- 不合规基础镜像的镜像被 Kyverno 策略自动阻止
- 实现了通过验证容器镜像基础镜像信息的基础供应链安全控制
本指南为在 CI/CD 流水线中实现供应链安全提供了基础。在生产环境中,您应当:
- 配置合理的命名空间隔离和访问控制
- 实现安全的签名密钥管理
- 设置策略违规的监控和告警
- 定期轮换签名密钥并更新安全策略
参考资料