3.17. 为 OpenShift Pipelines 提供链安全使用 Tekton 链

Tekton Chains 是一个 Kubernetes 自定义资源定义(CRD)控制器。您可以使用它来管理使用 Red Hat OpenShift Pipelines 创建的任务和管道的供应链安全。

默认情况下,Tekton Chains 会观察 OpenShift Container Platform 集群中的所有任务运行执行。当任务运行完成时,Tekton Chains 会获取任务运行的快照。然后,它会将快照转换为一个或多个标准有效负载格式,最后签署并存储所有工件。

要捕获有关任务运行的信息,Tekton Chains 使用 Result 对象。当对象不可用时,Tekton 会链 OCI 镜像的 URL 和合格摘要。

3.17.1. 主要特性

  • 您可以使用如 cosignskopeo 等加密密钥为任务运行、任务运行结果和 OCI registry 镜像签名。
  • 您可以使用"测试"格式,如 in-toto
  • 您可以使用 OCI 存储库作为存储后端安全存储签名和签名工件。

3.17.2. 配置 Tekton 链

Red Hat OpenShift Pipelines Operator 默认安装 Tekton 链。您可以通过修改 TektonConfig 自定义资源来配置 Tekton 链;Operator 会自动应用您在此自定义资源中所做的更改。

要编辑自定义资源,请使用以下命令:

$ oc edit TektonConfig config

自定义资源包含一个 chain: 数组。您可以在这个阵列中添加任何支持的配置参数,如下例所示:

apiVersion: operator.tekton.dev/v1alpha1
kind: TektonConfig
metadata:
  name: config
spec:
  addon: {}
  chain:
    artifacts.taskrun.format: tekton
  config: {}

3.17.2.1. Tekton Chains 配置支持的参数

集群管理员可以使用各种支持的参数键和值来配置任务运行、OCI 镜像和存储的规格。

3.17.2.1.1. 任务运行工件支持的参数

表 3.19. 链配置:任务运行工件支持的参数

描述支持的值默认值

artifacts.taskrun.format

存储任务运行有效负载的格式。

in-toto, slsa/v1

in-toto

artifacts.taskrun.storage

任务运行签名的存储后端。您可以将多个后端指定为用逗号分开的列表,如 “tekton,oci”。要禁用存储任务运行工件,请提供空字符串 ""

tekton, oci, gcs, docdb, grafeas

tekton

artifacts.taskrun.signer

用于签名任务运行有效负载的签名后端。

x509,kms

x509

注意

slsa/v1in-toto 的别名,用于向后兼容。

3.17.2.1.2. 管道运行工件支持的参数

表 3.20. 链配置:管道运行工件支持的参数

参数描述支持的值默认值

artifacts.pipelinerun.format

存储管道运行有效负载的格式。

in-toto, slsa/v1

in-toto

artifacts.pipelinerun.storage

用于存储管道运行签名的存储后端。您可以将多个后端指定为用逗号分开的列表,如 “tekton,oci”。要禁用存储管道运行工件,请提供一个空字符串 ""

tekton, oci, gcs, docdb, grafeas

tekton

artifacts.pipelinerun.signer

用于签名管道运行有效负载的签名后端。

x509,kms

x509

注意
  • slsa/v1in-toto 的别名,用于向后兼容。
  • 对于 grafeas 存储后端,只支持 Container Analysis。您无法在 Tekton 链的当前版本中配置 grafeas 服务器地址。
3.17.2.1.3. OCI 工件支持的参数

表 3.21. 链配置: OCI 工件支持的参数

参数描述支持的值默认值

artifacts.oci.format

存储 OCI 有效负载的格式。

simplesigning

simplesigning

artifacts.oci.storage

用于存储 OCI 签名的存储后端。您可以将多个后端指定为用逗号分开的列表,如 “oci,tekton”。要禁用存储 OCI 工件,请提供一个空字符串 ""

tekton, oci, gcs, docdb, grafeas

oci

artifacts.oci.signer

用于签名 OCI 有效负载的签名后端。

x509,kms

x509

3.17.2.1.4. KMS 符号支持的参数

表 3.22. 链配置: KMS 符号支持的参数

参数描述支持的值默认值

signers.kms.kmsref

对要在 kms 符号器中使用的 KMS 服务的 URI 引用。

支持的方案:gcpkms://, awskms://, azurekms://, hashivault://.如需了解更多详细信息,请参阅 Sigstore 文档中的 KMS 支持

 
3.17.2.1.5. 支持的存储参数

表 3.23. 链配置:存储支持的参数

参数描述支持的值默认值

storage.gcs.bucket

存储的 GCS 存储桶

  

artifacts.oci.repository

用于存储 OCI 签名和测试的 OCI 存储库。

如果您将其中一个工件存储后端配置为 oci,且没有定义此密钥,Tekton Chains 会将 attestation 与存储的 OCI 工件本身一起存储。如果您定义了这个密钥,则 attestation 不会与 OCI 工件一起存储,而是存储在指定的位置。如需更多信息,请参阅 cosign 文档

 

builder.id

为 in-toto attestations 设置的构建器 ID

 

https://tekton.dev/chains/v2

如果您启用了 docdb 存储方法用于任何工件,请配置 docstore 存储选项。有关 go-cloud docstore URI 格式的更多信息,请参阅文档 存储软件包文档。Red Hat OpenShift Pipelines 支持以下 docstore 服务:

  • firestore
  • dynamodb

表 3.24. 链配置:docstore 存储支持的参数

参数描述支持的值默认值

storage.docdb.url

docstore 集合的 go-cloud URI 引用。在为任何工件启用了 docdb 存储方法时使用。

firestore://projects/[PROJECT]/databases/(default)/documents/[COLLECTION]?name_field=name

 

如果您为任何工件启用 grafeas 存储方法,请配置 Grafeas 存储选项。有关 Grafeas 备注和发生的更多信息,请参阅 Grafeas 概念

要创建并行,Red Hat OpenShift Pipelines 必须首先创建用于链接并行的备注。Red Hat OpenShift Pipelines 创建两种类型的发生: ATTESTATION Occurrence 和 BUILD Occurrence。

Red Hat OpenShift Pipelines 使用可配置的 noteid 作为备注名称的前缀。它附加 ATTESTATION 备注的后缀 -simplesigning,以及 BUILD 备注的后缀 -intoto。如果没有配置 noteid 字段,Red Hat OpenShift Pipelines 将使用 tekton-<NAMESPACE> 作为前缀。

表 3.25. 链配置:Grafeas 存储支持的参数

参数描述支持的值默认值

storage.grafeas.projectid

Grafeas 服务器用于存储发生的 OpenShift Container Platform 项目。

  

storage.grafeas.noteid

可选:用于所有创建备注的名称的前缀。

没有空格的字符串。

 

storage.grafeas.notehint

可选:Grafeas ATTESTATION 备注的 human_readable_name 字段。

 

此 attestation 备注由 Tekton 链生成

另外,您还可以在测试时启用额外的二进制透明度上传。

表 3.26. 链配置:支持参数,以获得透明测试存储

参数描述支持的值默认值

transparency.enabled

启用或禁用自动二进制透明度上传。

true,false,manual

false

transparency.url

如果启用,用于上传二进制透明度的 URL。

 

https://rekor.sigstore.dev

注意

如果将 transparency.enabled 设置为 manual,则只有带有以下注解的任务和管道运行上传到透明度日志中:

chains.tekton.dev/transparency-upload: "true"

如果配置 x509 签名后端,您可以选择使用 Fulcio 启用无密钥签名。

表 3.27. 链配置:使用 Fulcio 进行 x509 密钥无密钥签名的支持参数

参数描述支持的值默认值

signers.x509.fulcio.enabled

从 Fulcio 启用或禁用请求自动证书。

true, false

false

signers.x509.fulcio.address

请求证书的 Fulcio 地址(如果启用)。

 

https://v1.fulcio.sigstore.dev

signers.x509.fulcio.issuer

预期的 OIDC 签发者。

 

https://oauth2.sigstore.dev/auth

signers.x509.fulcio.provider

从中请求 ID 令牌的供应商。

Google,spiffe,github,filesystem

Red Hat OpenShift Pipelines 会尝试使用每个供应商

signers.x509.identity.token.file

包含 ID 令牌的文件的路径。

  

signers.x509.tuf.mirror.url

TUF 服务器的 URL。必须存在 $TUF_URL/root.json

 

https://sigstore-tuf-root.storage.googleapis.com

如果您配置 kms 签名后端,请根据需要设置 KMS 配置,包括 OIDC 和 Spire。

表 3.28. 链配置: KMS 签名支持的参数

参数描述支持的值默认值

signers.kms.auth.address

KMS 服务器的 URI (VAULT_ADDR 的值)。

signers.kms.auth.token

KMS 服务器的身份验证令牌 (VAULT_TOKEN 值)。

signers.kms.auth.oidc.path

OIDC 身份验证的路径(例如,Vault 为 jwt)。

signers.kms.auth.oidc.role

OIDC 身份验证的角色。

signers.kms.auth.spire.sock

KMS 令牌的 Spire 套接字的 URI (例如:unix:///tmp/spire-agent/public/api.sock)。

signers.kms.auth.spire.audience

从 Spire 请求 SVID 的受众。

3.17.3. 在 Tekton Chains 中签名数据的 secret

集群管理员可以生成密钥对,并使用 Tekton 链来使用 Kubernetes secret 为工件签名。要使 Tekton 链正常工作,加密的密钥和密码必须存在,作为 openshift-pipelines 命名空间中的 signing-secrets secret 的一部分。

目前,Tekton 链支持 x509cosign 签名方案。

注意

只使用一个受支持的签名方案。

要将 x509 签名方案与 Tekton Chains 搭配使用,请将 ed25519ecdsa 类型的 x509.pem 私钥存储在 signing-secrets Kubernetes secret 中。

3.17.3.1. 使用 cosign 进行签名

您可以使用 cosign 工具使用带有 Tekton 链的 cosign 签名方案。

先决条件

流程

  1. 运行以下命令,生成 cosign.keycosign.pub 密钥对:

    $ cosign generate-key-pair k8s://openshift-pipelines/signing-secrets

    Cosign 会提示您输入密码,然后创建一个 Kubernetes secret。

  2. 将加密的 cosign.key 私钥和 cosign.password 解密密码存储在 signing-secrets Kubernetes secret 中。确保私钥存储为 ENCRYPTED COSIGN PRIVATE KEY 类型的加密 PEM 文件。

3.17.3.2. 使用 skopeo 进行签名

您可以使用 skopeo 工具生成密钥,并在带有 Tekton 链的 cosign 签名方案中使用它们。

先决条件

流程

  1. 运行以下命令生成公钥/私钥对:

    $ skopeo generate-sigstore-key --output-prefix <mykey> 1
    1
    <mykey> 替换为您选择的密钥名称。

    Skopeo 会提示您输入私钥的密码短语,然后创建名为 <mykey>.private<mykey>.pub 的密钥文件。

  2. 运行以下命令,使用 base64 工具对 <mykey>.pub 文件进行编码:

    $ base64 -w 0 <mykey>.pub > b64.pub
  3. 运行以下命令,使用 base64 工具对 <mykey>.private 文件进行编码:

    $ base64 -w 0 <mykey>.private > b64.private
  4. 运行以下命令,使用 base64 工具对 passhprase 进行编码:

    $ echo -n '<passphrase>' | base64 -w 0 > b64.passphrase 1
    1
    <passphrase> 替换为用于密钥对的密码短语。
  5. 运行以下命令,在 openshift-pipelines 命名空间中创建 signing-secrets secret:

    $ oc create secret generic signing-secrets -n openshift-pipelines
  6. 运行以下命令来编辑 signing-secrets secret:

    $ oc edit secret -n openshift-pipelines signing-secrets

    使用以下方法在 secret 的数据中添加编码的密钥:

    apiVersion: v1
    data:
      cosign.key: <Encoded <mykey>.private> 1
      cosign.password: <Encoded passphrase> 2
      cosign.pub: <Encoded <mykey>.pub> 3
    immutable: true
    kind: Secret
    metadata:
      name: signing-secrets
    # ...
    type: Opaque
    1
    <Encoded <mykey>.private> 替换为 b64.private 文件的内容。
    2
    <Encoded passphrase> 替换为 b64.passphrase 文件的内容。
    3
    <Encoded <mykey>.pub> 替换为 b64.pub 文件的内容。

3.17.3.3. 解决 "secret already exists" 错误

如果 signing-secret secret 已经填充,则创建此 secret 的命令可能会输出以下出错信息:

Error from server (AlreadyExists): secrets "signing-secrets" already exists

您可以通过删除 secret 来解决这个问题。

流程

  1. 运行以下命令来删除 signing-secret secret:

    $ oc delete secret signing-secrets -n openshift-pipelines
  2. 重新创建密钥对,并使用您首选的签名方案将其存储在机密中。

3.17.4. 对 OCI registry 进行身份验证

在将签名推送到 OCI Registry 之前,集群管理员必须配置 Tekton 链,以便与 registry 进行身份验证。Tekton Chains 控制器使用与任务运行相同的服务帐户。要设置具有所需凭证(push)到 OCI registry 的服务帐户,请执行以下步骤:

流程

  1. 设置 Kubernetes 服务帐户的命名空间和名称。

    $ export NAMESPACE=<namespace> 1
    $ export SERVICE_ACCOUNT_NAME=<service_account> 2
    1
    与服务帐户关联的命名空间。
    2
    服务帐户的名称。
  2. 创建 Kubernetes secret。

    $ oc create secret registry-credentials \
      --from-file=.dockerconfigjson \ 1
      --type=kubernetes.io/dockerconfigjson \
      -n $NAMESPACE
    1
    使用 Docker 配置文件的路径替换。默认路径为 ~/.docker/config.json
  3. 授予服务帐户对 secret 的访问权限。

    $ oc patch serviceaccount $SERVICE_ACCOUNT_NAME \
      -p "{\"imagePullSecrets\": [{\"name\": \"registry-credentials\"}]}" -n $NAMESPACE

    如果对 Red Hat OpenShift Pipelines 分配到所有任务的默认 pipeline 服务帐户进行补丁,Red Hat OpenShift Pipelines Operator 将覆盖服务帐户。作为最佳实践,您可以执行以下步骤:

    1. 创建单独的服务帐户,以分配给用户的任务运行。

      $ oc create serviceaccount <service_account_name>
    2. 通过设置任务运行模板中的 serviceaccountname 字段的值,将服务帐户关联到运行任务。

      apiVersion: tekton.dev/v1beta1
      kind: TaskRun
      metadata:
      name: build-push-task-run-2
      spec:
      serviceAccountName: build-bot 1
      taskRef:
        name: build-push
      ...
      1
      使用新创建的服务帐户的名称替换。

3.17.5. 创建和验证任务运行签名而无需任何其他身份验证

要验证使用 Tekton 链与任何其他身份验证一起运行的任务的签名,请执行以下任务:

  • 创建加密的 x509 密钥对,并将它保存为 Kubernetes secret。
  • 配置 Tekton Chains 后端存储。
  • 创建任务运行,为它签名并将签名和有效负载存储为任务运行自身时的注解。
  • 从已签名任务运行中检索签名和有效负载。
  • 验证任务运行的签名。

先决条件

确保在集群中安装了以下组件:

  • Red Hat OpenShift Pipelines Operator
  • Tekton Chains
  • Cosign

流程

  1. 创建加密的 x509 密钥对,并将它保存为 Kubernetes secret。有关创建密钥对并将其保存为 secret 的更多信息,请参阅 "Signing secrets in Tekton Chains"。
  2. 在 Tekton Chains 配置中,禁用 OCI 存储,并将任务运行存储和格式设置为 tekton。在 TektonConfig 自定义资源中设置以下值:

    apiVersion: operator.tekton.dev/v1alpha1
    kind: TektonConfig
    metadata:
      name: config
    spec:
    # ...
        chain:
          artifacts.oci.storage: ""
          artifacts.taskrun.format: tekton
          artifacts.taskrun.storage: tekton
    # ...

    有关使用 TektonConfig 自定义资源配置 Tekton 链的更多信息,请参阅"配置 Tekton 链"。

  3. 要重启 Tekton Chains 控制器以确保应用了修改后的配置,请输入以下命令:

    $ oc delete po -n openshift-pipelines -l app=tekton-chains-controller
  4. 输入以下命令来创建任务运行:

    $ oc create -f https://raw.githubusercontent.com/tektoncd/chains/main/examples/taskruns/task-output-image.yaml 1
    1
    将示例 URI 替换为指向任务运行的 URI 或文件路径。

    输出示例

    taskrun.tekton.dev/build-push-run-output-image-qbjvh created

  5. 输入以下命令来检查步骤的状态。等待进程完成。

    $ tkn tr describe --last

    输出示例

    [...truncated output...]
    NAME                            STATUS
    ∙ create-dir-builtimage-9467f   Completed
    ∙ git-source-sourcerepo-p2sk8   Completed
    ∙ build-and-push                Completed
    ∙ echo                          Completed
    ∙ image-digest-exporter-xlkn7   Completed

  6. 要从存储为 base64 编码注解的对象检索签名,请输入以下命令:

    $ tkn tr describe --last -o jsonpath="{.metadata.annotations.chains\.tekton\.dev/signature-taskrun-$TASKRUN_UID}" | base64 -d > sig
    $ export TASKRUN_UID=$(tkn tr describe --last -o  jsonpath='{.metadata.uid}')
  7. 要使用您创建的公钥验证签名,请输入以下命令:
$ cosign verify-blob-attestation --insecure-ignore-tlog --key path/to/cosign.pub --signature sig --type slsaprovenance --check-claims=false /dev/null 1
1
path/to/cosign.pub 替换为公钥文件的路径名。

输出示例

Verified OK

3.17.5.1. 其他资源

3.17.6. 使用 Tekton 链来签名和验证镜像并证明

集群管理员可以通过执行以下任务来使用 Tekton 链来签名和验证镜像和验证镜像:

  • 创建加密的 x509 密钥对,并将它保存为 Kubernetes secret。
  • 为 OCI registry 设置身份验证,以在测试过程中存储镜像、镜像签名和签名的镜像。
  • 配置 Tekton 链以生成和签署认可。
  • 在任务运行中,使用 Kaniko 创建镜像。
  • 验证已签名的镜像及已签名证明。

先决条件

确保在集群中安装了以下内容:

  • Red Hat OpenShift Pipelines Operator
  • Tekton Chains
  • Cosign
  • Rekor
  • jq

流程

  1. 创建加密的 x509 密钥对,并将它保存为 Kubernetes secret:

    $ cosign generate-key-pair k8s://openshift-pipelines/signing-secrets

    提示时提供密码。Cosign 将生成的私钥作为 openshift-pipelines 命名空间中的 signing-secrets Kubernetes secret 的一部分存储在 openshift-pipelines 命名空间中,并将公钥写入 cosign.pub 本地文件。

  2. 为镜像 registry 配置身份验证。

    1. 要将 Tekton Chains 控制器配置为将签名推送到 OCI registry,请使用与任务运行服务帐户关联的凭证。如需更多信息,请参阅"授权到 OCI registry"部分。
    2. 要为构建并推送到 registry 的 Kaniko 任务配置身份验证,请创建一个包含所需凭证的 docker config.json 文件的 Kubernetes secret。

      $ oc create secret generic <docker_config_secret_name> \ 1
        --from-file <path_to_config.json> 2
      1
      使用 docker config secret 的名称替换。
      2
      使用 docker config.json 文件的路径替换。
  3. 通过在 chains-config 对象中设置 artifacts.taskrun.formatartifacts.taskrun.storagetransparency.enabled 参数来配置 Tekton 链 :

    $ oc patch configmap chains-config -n openshift-pipelines -p='{"data":{"artifacts.taskrun.format": "in-toto"}}'
    
    $ oc patch configmap chains-config -n openshift-pipelines -p='{"data":{"artifacts.taskrun.storage": "oci"}}'
    
    $ oc patch configmap chains-config -n openshift-pipelines -p='{"data":{"transparency.enabled": "true"}}'
  4. 启动 Kaniko 任务。

    1. 将 Kaniko 任务应用到集群。

      $ oc apply -f examples/kaniko/kaniko.yaml 1
      1
      使用 Kaniko 任务的 URI 或文件路径替换。
    2. 设置适当的环境变量。

      $ export REGISTRY=<url_of_registry> 1
      
      $ export DOCKERCONFIG_SECRET_NAME=<name_of_the_secret_in_docker_config_json> 2
      1
      使用您要推送镜像的 registry 的 URL 替换。
      2
      使用 docker config.json 文件中的 secret 名称替换。
    3. 启动 Kaniko 任务。

      $ tkn task start --param IMAGE=$REGISTRY/kaniko-chains --use-param-defaults --workspace name=source,emptyDir="" --workspace name=dockerconfig,secret=$DOCKERCONFIG_SECRET_NAME kaniko-chains

      观察此任务的日志,直到所有步骤都完成。身份验证成功后,最终镜像将推送到 $REGISTRY/kaniko-chains

  5. 等待一分钟,以允许 Tekton 链生成证据并对其进行签名,然后在任务运行时检查 chains.tekton.dev/signed=true 注解的可用性。

    $ oc get tr <task_run_name> \ 1
    -o json | jq -r .metadata.annotations
    
    {
      "chains.tekton.dev/signed": "true",
      ...
    }
    1
    使用任务运行的名称替换。
  6. 验证镜像和 attestation。

    $ cosign verify --key cosign.pub $REGISTRY/kaniko-chains
    
    $ cosign verify-attestation --key cosign.pub $REGISTRY/kaniko-chains
  7. 在 Rekor 中找到镜像的验证情况。

    1. 获取 $REGISTRY/kaniko-chains 镜像摘要。您可以搜索任务运行或拉取镜像以提取摘要。
    2. 搜索 Rekor 以查找与镜像 sha256 摘要匹配的所有条目。

      $ rekor-cli search --sha <image_digest> 1
      
      <uuid_1> 2
      <uuid_2> 3
      ...
      1
      使用镜像的 sha256 摘要替换。
      2
      第一个匹配通用唯一标识符(UUID)。
      3
      第二个匹配 UUID。

      搜索结果显示匹配条目的 UUID。其中其中一个 UUID 包含 attestation。

    3. 检查 attestation。

      $ rekor-cli get --uuid <uuid> --format json | jq -r .Attestation | base64 --decode | jq

3.17.7. 其他资源