镜像签名验证

在 Tekton Chains 中,可以自动为构建的镜像签名,并将签名记录到 SLSA Provenance 中。

功能概述

此方法使用 Tekton Chains 自动为构建的镜像签名,然后使用 cosign 或 Kyverno 验证签名:

  1. 配置 Tekton Chains 自动为构建的镜像签名。
  2. 使用 buildah Tekton Task 构建镜像。
  3. (可选)使用 cosign cli 验证签名。
  4. 配置 Kyverno 规则,仅允许已签名的镜像。
  5. 使用该镜像创建 Pod 以验证签名。
TIP

快速开始:签名 Provenance 相比,此方法仅增加了更多验证步骤。

使用场景

以下场景需要参考本文档中的指导:

  • 使用 Kyverno 在 Kubernetes 集群中实现镜像签名验证
  • 强制执行安全策略,仅允许已签名的镜像部署
  • 在 CI/CD 流水线中设置自动镜像签名验证
  • 确保生产环境中的镜像完整性和真实性
  • 为容器镜像实现供应链安全控制

前提条件

  • 已安装 Tekton Pipelines、Tekton Chains 和 Kyverno 的 Kubernetes 集群
  • 已启用镜像推送的 registry
  • 已安装并配置好可访问集群的 kubectl CLI
  • 已安装 cosign CLI 工具
  • 已安装 jq CLI 工具

流程概览

步骤操作描述
1生成签名密钥使用 cosign 创建用于签名制品的密钥对
2设置认证配置用于镜像推送的 registry 凭证
3配置 Tekton Chains设置 Chains 使用 OCI 存储并配置签名
4创建示例流水线创建包含必要任务和 workspace 的流水线定义
5运行示例流水线使用正确的配置创建并运行 PipelineRun
6等待签名等待 Chains 为 PipelineRun 签名
7获取镜像信息从 PipelineRun 中提取镜像 URI 和 digest
8使用 Kyverno 验证签名使用 Kyverno 策略配置并验证镜像签名
9清理资源删除测试 Pod 和策略

分步说明

步骤 1-7:基本设置

这些步骤与 快速开始:签名 Provenance 指南相同。请按照该指南中的说明完成以下内容:

步骤 8:使用 Kyverno 验证签名

步骤 8:验证镜像和 Attestation 中,我们使用 cosign CLI 验证签名。
这里我们使用 Kyverno 验证签名。

步骤 8.1:创建 Kyverno 策略,仅允许已签名的镜像部署

TIP

此步骤需要集群管理员权限。

有关 Kyverno ClusterPolicy 的更多详细信息,请参阅 Kyverno ClusterPolicy

策略如下:

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: only-cosign-image-deploy
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:
              # The credential needs to exist in the namespace where kyverno is deployed
              - registry-credentials

          attestors:
            - count: 1
              entries:
                - keys:
                    publicKeys: |- # <- The public key of the signer
                      -----BEGIN PUBLIC KEY-----
                      MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEFZNGfYwn7+b4uSdEYLKjxWi3xtP3
                      UkR8hQvGrG25r0Ikoq0hI3/tr0m7ecvfM75TKh5jGAlLKSZUJpmCGaTToQ==
                      -----END PUBLIC KEY-----

                    ctlog:
                      ignoreSCT: true

                    rekor:
                      ignoreTlog: true

:::details {title="YAML 字段说明"}

  • spec.rules[].match.any[].resources:要匹配和验证的资源。
    • kinds:要匹配和验证的资源类型。
      • Pod:Pod 资源。
    • namespaces:要匹配和验证的资源命名空间。
      • policypolicy 命名空间中的资源将被匹配和验证。
  • spec.rules[].verifyImages:要验证的镜像。
    • imageReferences:要验证的镜像引用。
      • *:将验证所有镜像引用。
      • <registry>/test/*:仅验证 <registry>/test registry 中的镜像引用。
    • skipImageReferences:要跳过的镜像引用。
      • ghcr.io/trusted/*:仅跳过 ghcr.io/trusted registry 中的镜像引用。
    • imageRegistryCredentials
      • allowInsecureRegistry:是否允许不安全的 registry。
      • secrets:用于镜像 registry 凭证的 Secret。
        • registry-credentials:Secret 的名称。该 Secret 需要存在于 kyverno 所部署的命名空间中。
    • attestors:用于镜像验证的 attestors。
      • count:需要匹配的 attestors 数量。
      • entries:attestors 的条目。
        • keys:attestors 的密钥。
          • publicKeys:attestors 的公钥。
            • 该公钥与 signing-secrets Secret 中的 cosign.pub 公钥相同。
          • ctlog:attestors 的 ctlog。
            • ignoreSCT:是否忽略 SCT。
              • 在隔离网络环境中,请先忽略 SCT。
          • rekor:attestors 的 rekor。
            • ignoreTlog:是否忽略 Tlog。
              • 在隔离网络环境中,请先忽略 Tlog。 :::

需要调整的配置

  • spec.rules[].attestors[].entries[].keys.publicKeys:签名者的公钥。
    • 该公钥与 signing-secrets Secret 中的 cosign.pub 公钥相同。
    • 可以从 获取签名公钥 部分获取该公钥。

保存为名为 kyverno.only-cosign-image-deploy.yaml 的 yaml 文件,并使用以下命令应用:

$ kubectl apply -f kyverno.only-cosign-image-deploy.yaml

clusterpolicy.kyverno.io/only-cosign-image-deploy configured

步骤 8.2:验证策略

在定义该策略的 policy 命名空间中,创建一个 Pod 来验证该策略。

使用流水线创建的已签名镜像创建 Pod。

$ export NAMESPACE=<policy>
$ export IMAGE=<<registry>/test/chains/demo-1:latest@sha256:93635f39cb31de5c6988cdf1f10435c41b3fb85570c930d51d41bbadc1a90046>

$ kubectl run -n $NAMESPACE signed --image=${IMAGE} -- sleep 3600

pod/signed created

Pod 将成功创建。

$ export NAMESPACE=<policy>
$ kubectl get pod -n $NAMESPACE signed

NAME      READY   STATUS    RESTARTS   AGE
signed   1/1     Running   0          10s

使用未签名镜像创建 Pod。

$ export NAMESPACE=<policy>
$ export IMAGE=<<registry>/test/chains/unsigned:latest>

$ kubectl run -n $NAMESPACE unsigned --image=${IMAGE} -- sleep 3600

如果收到如下输出,说明该 Pod 被策略阻止。

Error from server: admission webhook "mutate.kyverno.svc-fail" denied the request:

resource Pod/policy/unsigned was blocked due to the following policies

only-cosign-image-deploy:
  check-image: 'failed to verify image ubuntu:latest:
    .attestors[0].entries[0].keys: no signatures found'

步骤 9:清理资源

删除前面步骤创建的 Pod。

$ export NAMESPACE=<policy>
$ kubectl delete pod -n $NAMESPACE signed

pod "signed" deleted

删除该策略。

$ kubectl delete clusterpolicy only-cosign-image-deploy

预期结果

完成本指南后:

  • 你已经完成了 Tekton Chains 镜像签名和 Kyverno 签名验证的可用配置
  • 你的容器镜像会在构建过程中自动签名
  • 只有已签名的镜像可以部署到指定命名空间
  • 未签名镜像会被 Kyverno 策略自动阻止
  • 你已经为容器镜像实现了基础的供应链安全控制

本指南为在 CI/CD 流水线中实现供应链安全提供了基础。在生产环境中,你应该:

  1. 配置正确的命名空间隔离和访问控制
  2. 为签名密钥实现安全的密钥管理
  3. 为策略违规设置监控和告警
  4. 定期轮换签名密钥并更新安全策略
  5. 考虑实施其他安全控制,例如漏洞扫描

参考文档