镜像签名验证

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

功能概述

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

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

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

使用场景

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

  • 在 Kubernetes 集群中使用 Kyverno 实现镜像签名验证
  • 强制执行安全策略,只允许部署已签名的镜像
  • 在 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等待签名等待 PipelineRun 被 Chains 签名
7获取镜像信息从 PipelineRun 中提取镜像 URI 和 digest
8使用 Kyverno 验证签名使用 Kyverno 策略配置并验证镜像签名
9清理资源删除测试 Pod 和策略

分步说明

步骤 1-7:基础设置

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

步骤 8:使用 Kyverno 验证签名

步骤 8:验证镜像和证明 中,我们使用 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

  • spec.rules[].match.any[].resources: 要匹配和验证的资源。
    • kinds: 要匹配和验证的资源类型。
      • Pod: Pod 资源。
    • namespaces: 要匹配和验证的资源命名空间。
      • policy: policy 命名空间中的资源将被匹配和验证。
  • 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. 考虑实施额外的安全控制,例如漏洞扫描

参考资料