使用 Cloud Build 和 GKE 实现 Binary Authorization

本教程介绍如何为 Google Kubernetes Engine (GKE) 设置、配置和使用 Binary Authorization 来进行二进制授权。二进制授权是在容器映像上创建证明的过程,目的是为了在将映像部署到 GKE 之前验证映像是否满足特定条件。

例如,Binary Authorization 可以验证应用是否通过了单元测试,或者应用是否通过使用一组特定系统构建的。如需了解详情并查看用例,请参阅确保 Google Kubernetes Engine 中软件供应链的安全

本教程面向想要深入了解容器漏洞扫描和 Binary Authorization 及其在 CI/CD 流水线中的实现和应用情况的从业者。

本教程假定您熟悉以下主题和技术:

  • 持续集成和持续部署
  • 常见漏洞和披露 (CVE) 漏洞扫描
  • GKE
  • Container Registry
  • Cloud Build
  • Cloud Key Management Service (Cloud KMS)

目标

  • 为预演和生产环境部署 GKE 集群。
  • 创建多个证明者和证明。
  • 使用 Cloud Build 部署 CI/CD 流水线。
  • 测试部署流水线。
  • 开发一个紧急访问权限流程。

费用

本教程使用 Google Cloud 的以下收费组件:

您可使用价格计算器根据您的预计使用量来估算费用。 Google Cloud 新用户可能有资格申请免费试用

完成本教程后,您可以删除所创建的资源以避免继续计费。如需了解详情,请参阅清理

准备工作

  1. 登录您的 Google 帐号。

    如果您还没有 Google 帐号,请注册一个新帐号

  2. 在 Google Cloud Console 的项目选择器页面上,选择或创建一个 Google Cloud 项目。

    转到项目选择器页面

  3. 确保您的 Cloud 项目已启用结算功能。 了解如何确认您的项目是否已启用结算功能

  4. 启用 Binary Authorization, Cloud Build, Cloud KMS, GKE, Container Registry, Container Analysis, Resource Manager, and Cloud Source Repositories API。

    启用 API

  5. 在 Cloud Console 中,激活 Cloud Shell。

    激活 Cloud Shell

    Cloud Shell 会话随即会在 Cloud Console 的底部启动,并显示命令行提示符。Cloud Shell 是一个已安装 Cloud SDK 的 Shell 环境,其中包括 gcloud 命令行工具以及已为当前项目设置的值。该会话可能需要几秒钟时间来完成初始化。

  6. 本教程中的所有命令都在 Cloud Shell 中运行。

CI/CD 流水线的架构

确保并强制应用部署按照您组织批准的流程操作是软件开发生命周期 (SDLC) 的一个重要环节。在 GKE 上使用 Binary Authorization 是建立这些检查和平衡的方法之一。首先,Binary Authorization 将备注附加到容器映像。然后,GKE 会在您部署应用之前验证必需的备注是否存在。

这些备注或证明可做出与映像有关的陈述。证明是完全可配置的,但以下是一些常见示例:

  • 应用已通过单元测试。
  • 该应用已通过质量保证 (QA) 团队的验证。
  • 该应用已进行漏洞扫描,从未发现任何问题。

下图描绘了一个 SDLC,说明在漏洞扫描完成后未找到已知漏洞时应用了单个证明。

应用单个证明的 SDLC 架构

在本教程中,您将使用 Cloud Source Repositories、Cloud Build、Container Registry 和 GKE 创建一个 CI/CD 流水线。下图演示了这一 CI/CD 流水线。

具有三个 Google Cloud 产品的 CI/CD 流水线架构

此 CI/CD 流水线包含以下步骤:

  1. 构建一个包含应用源代码的容器映像。

  2. 将该容器映像推送到 Container Registry。

  3. Container Analysis 会扫描容器映像以查找已知的安全漏洞或 CVE

如果映像不包含严重性分数大于 5 的 CVE,则证明该映像没有严重 CVE,并将其自动部署到模拟环境。分数大于 5 表示发现中等到严重程度的漏洞,因此未得到证明或部署。

质量保证团队会在模拟集群中检查该应用。如果该应用满足要求,则团队会手动应用证明,确认该容器映像达到足够高的质量,可以部署到生产环境。系统将更新生产清单,且应用会部署到生产 GKE 集群中。

GKE 集群已配置为检查容器映像以进行证明,并拒绝没有所需证明的任何部署。模拟 GKE 集群只需要漏洞扫描证明,但生产 GKE 集群同时需要漏洞扫描和质量保证证明。

在本教程中,您在 CI/CD 流水线中引入了失败机制,以测试和验证此机制的实施情况。最后,您可以实现紧急访问权限过程,一旦有紧急情况,就可以在 GKE 中绕过这些部署检查。

设置环境

本教程使用以下环境变量。您可以更改这些值以满足您的要求,但本教程中的所有步骤都假定这些环境变量已存在并包含有效值。

  1. 在 Cloud Shell 中,设置要在其中部署和管理本教程中使用的所有资源的 Cloud 项目:

    export PROJECT_ID="${DEVSHELL_PROJECT_ID}"
    
  2. 设置要在其中部署这些资源的地区:

    export REGION="us-central1"
    

    GKE 集群和 Cloud KMS 密钥位于此地区中。在本教程中,地区为 us-central1。如需详细了解地区,请参阅地理和地区

  3. 设置 Cloud Build 项目编号:

    export PROJECT_NUMBER="$(gcloud projects describe "${PROJECT_ID}" \
      --format='value(projectNumber)')"
    
  4. 设置 Cloud Build 服务帐号电子邮件:

    export CLOUD_BUILD_SA_EMAIL="${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com"
    

创建 GKE 集群

创建两个 GKE 集群并向 Cloud Build 授予 Identity and Access Management (IAM) 权限,以将应用部署到 GKE。创建 GKE 集群可能需要几分钟时间。

  1. 在 Cloud Shell 中,创建 GKE 集群以用于模拟环境:

    gcloud container clusters create "staging-cluster" \
      --project "${PROJECT_ID}" \
      --machine-type "n1-standard-1" \
      --region "${REGION}" \
      --num-nodes "1" \
      --enable-binauthz
    
  2. 创建 GKE 集群以用于生产环境:

    gcloud container clusters create "prod-cluster" \
      --project "${PROJECT_ID}" \
      --machine-type "n1-standard-1" \
      --region "${REGION}" \
      --num-nodes "1" \
      --enable-binauthz
    
  3. 授予 Cloud Build 服务帐号部署到 GKE 的权限:

    gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
      --member "serviceAccount:${CLOUD_BUILD_SA_EMAIL}" \
      --role "roles/container.developer"
    

创建签名密钥

创建两个 Cloud KMS 非对称密钥以给证明签名。

  1. 在 Cloud Shell 中,创建一个名为 binauthz 的 Cloud KMS 密钥环:

    gcloud kms keyrings create "binauthz" \
      --project "${PROJECT_ID}" \
      --location "${REGION}"
    
  2. 创建一个名为 vulnz-signer 的非对称 Cloud KMS 密钥,该密钥将用来给漏洞扫描证明签名并进行验证:

    gcloud kms keys create "vulnz-signer" \
      --project "${PROJECT_ID}" \
      --location "${REGION}" \
      --keyring "binauthz" \
      --purpose "asymmetric-signing" \
      --default-algorithm "rsa-sign-pkcs1-4096-sha512"
    
  3. 创建一个名为 qa-signer 的非对称 Cloud KMS 密钥以给质量保证证明签名并进行验证:

    gcloud kms keys create "qa-signer" \
      --project "${PROJECT_ID}" \
      --location "${REGION}" \
      --keyring "binauthz" \
      --purpose "asymmetric-signing" \
      --default-algorithm "rsa-sign-pkcs1-4096-sha512"
    

配置证明

您可以创建附加到容器映像的备注,并使用上述步骤中的密钥向 Cloud Build 服务帐号授予查看备注、附加备注以及创建证明者的权限。

创建漏洞扫描工具证明

  1. 在 Cloud Shell 中,创建名为 vulnz-note 的 Container Analysis 备注:

    curl "https://containeranalysis.googleapis.com/v1/projects/${PROJECT_ID}/notes/?noteId=vulnz-note" \
      --request "POST" \
      --header "Content-Type: application/json" \
      --header "Authorization: Bearer $(gcloud auth print-access-token)" \
      --header "X-Goog-User-Project: ${PROJECT_ID}" \
      --data-binary @- <<EOF
        {
          "name": "projects/${PROJECT_ID}/notes/vulnz-note",
          "attestation": {
            "hint": {
              "human_readable_name": "Vulnerability scan note"
            }
          }
        }
    EOF
    
  2. 授予 Cloud Build 服务帐号查看 vulnz-note 备注并将其附加到容器映像的权限:

    curl "https://containeranalysis.googleapis.com/v1/projects/${PROJECT_ID}/notes/vulnz-note:setIamPolicy" \
      --request POST \
      --header "Content-Type: application/json" \
      --header "Authorization: Bearer $(gcloud auth print-access-token)" \
      --header "X-Goog-User-Project: ${PROJECT_ID}" \
      --data-binary @- <<EOF
        {
          "resource": "projects/${PROJECT_ID}/notes/vulnz-note",
          "policy": {
            "bindings": [
              {
                "role": "roles/containeranalysis.notes.occurrences.viewer",
                "members": [
                  "serviceAccount:${CLOUD_BUILD_SA_EMAIL}"
                ]
              },
              {
                "role": "roles/containeranalysis.notes.attacher",
                "members": [
                  "serviceAccount:${CLOUD_BUILD_SA_EMAIL}"
                ]
              }
            ]
          }
        }
    EOF
    
  3. 创建漏洞扫描证明者:

    gcloud container binauthz attestors create "vulnz-attestor" \
      --project "${PROJECT_ID}" \
      --attestation-authority-note-project "${PROJECT_ID}" \
      --attestation-authority-note "vulnz-note" \
      --description "Vulnerability scan attestor"
    
  4. 为证明者的签名密钥添加公钥:

    gcloud beta container binauthz attestors public-keys add \
      --project "${PROJECT_ID}" \
      --attestor "vulnz-attestor" \
      --keyversion "1" \
      --keyversion-key "vulnz-signer" \
      --keyversion-keyring "binauthz" \
      --keyversion-location "${REGION}" \
      --keyversion-project "${PROJECT_ID}"
    
  5. 授予 Cloud Build 服务帐号验证 vulnz-attestor 所做证明的权限:

    gcloud container binauthz attestors add-iam-policy-binding "vulnz-attestor" \
      --project "${PROJECT_ID}" \
      --member "serviceAccount:${CLOUD_BUILD_SA_EMAIL}" \
      --role "roles/binaryauthorization.attestorsViewer"
    
  6. 授予 Cloud Build 服务帐号使用 vulnz-signer 密钥给对象签名的权限:

    gcloud kms keys add-iam-policy-binding "vulnz-signer" \
      --project "${PROJECT_ID}" \
      --location "${REGION}" \
      --keyring "binauthz" \
      --member "serviceAccount:${CLOUD_BUILD_SA_EMAIL}" \
      --role 'roles/cloudkms.signerVerifier'
    

创建质量保证证明

  1. 在 Cloud Shell 中,创建名为 qa-note 的 Container Analysis 备注:

    curl "https://containeranalysis.googleapis.com/v1/projects/${PROJECT_ID}/notes/?noteId=qa-note" \
      --request "POST" \
      --header "Content-Type: application/json" \
      --header "Authorization: Bearer $(gcloud auth print-access-token)" \
      --header "X-Goog-User-Project: ${PROJECT_ID}" \
      --data-binary @- <<EOF
        {
          "name": "projects/${PROJECT_ID}/notes/qa-note",
          "attestation": {
            "hint": {
              "human_readable_name": "QA note"
            }
          }
        }
    EOF
    
  2. 授予 Cloud Build 服务帐号查看 qa-note 备注并将其附加到容器映像的权限:

    curl "https://containeranalysis.googleapis.com/v1/projects/${PROJECT_ID}/notes/qa-note:setIamPolicy" \
      --request POST \
      --header "Content-Type: application/json" \
      --header "Authorization: Bearer $(gcloud auth print-access-token)" \
      --header "X-Goog-User-Project: ${PROJECT_ID}" \
      --data-binary @- <<EOF
        {
          "resource": "projects/${PROJECT_ID}/notes/qa-note",
          "policy": {
            "bindings": [
              {
                "role": "roles/containeranalysis.notes.occurrences.viewer",
                "members": [
                  "serviceAccount:${CLOUD_BUILD_SA_EMAIL}"
                ]
              },
              {
                "role": "roles/containeranalysis.notes.attacher",
                "members": [
                  "serviceAccount:${CLOUD_BUILD_SA_EMAIL}"
                ]
              }
            ]
          }
        }
    EOF
    
  3. 创建质量保证证明者:

    gcloud container binauthz attestors create "qa-attestor" \
      --project "${PROJECT_ID}" \
      --attestation-authority-note-project "${PROJECT_ID}" \
      --attestation-authority-note "qa-note" \
      --description "QA attestor"
    
  4. 为证明者的签名密钥添加公钥:

    gcloud beta container binauthz attestors public-keys add \
      --project "${PROJECT_ID}" \
      --attestor "qa-attestor" \
      --keyversion "1" \
      --keyversion-key "qa-signer" \
      --keyversion-keyring "binauthz" \
      --keyversion-location "${REGION}" \
      --keyversion-project "${PROJECT_ID}"
    
  5. 授予 Cloud Build 服务帐号验证 qa-attestor 所做证明的权限:

    gcloud container binauthz attestors add-iam-policy-binding "qa-attestor" \
      --project "${PROJECT_ID}" \
      --member "serviceAccount:${CLOUD_BUILD_SA_EMAIL}" \
      --role "roles/binaryauthorization.attestorsViewer"
    
  6. 授予您的质量保证团队给证明签名的权限:

    gcloud kms keys add-iam-policy-binding "qa-signer" \
      --project "${PROJECT_ID}" \
      --location "${REGION}" \
      --keyring "binauthz" \
      --member "group:qa-team@example.com" \
      --role 'roles/cloudkms.signerVerifier'
    

设置 Binary Authorization 政策

即使您使用 --enable-binauthz 创建了 GKE 集群,您也必须编写一项政策,就二进制文件在集群中运行所需的证明,向 GKE 发出指示。Binary Authorization 政策存在于项目级层,但包含集群级层的配置。

以下政策会通过以下方式更改默认政策:

  • 将默认 evaluationMode 更改为 ALWAYS_DENY。只有豁免映像或具有所需证明的映像才能在集群中运行。

  • 启用 globalPolicyEvaluationMode,从而将默认许可名单更改为仅包含 Google 提供的系统映像。

  • 定义以下集群准入规则:

    • staging-cluster 要求 vulnz-attestor 提供证明。

    • prod-cluster 要求 vulnz-attestorqa-attestor 提供证明。

如需详细了解 Binary Authorization 政策,请参阅 YAML 格式政策参考文档

  1. 在 Cloud Shell 中,创建一个 YAML 文件来描述 Cloud 项目的 Binary Authorization 政策:

    cat > ./binauthz-policy.yaml <<EOF
    admissionWhitelistPatterns:
    - namePattern: docker.io/istio/*
    defaultAdmissionRule:
      enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
      evaluationMode: ALWAYS_DENY
    globalPolicyEvaluationMode: ENABLE
    clusterAdmissionRules:
      # Staging cluster
      ${REGION}.staging-cluster:
        evaluationMode: REQUIRE_ATTESTATION
        enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
        requireAttestationsBy:
        - projects/${PROJECT_ID}/attestors/vulnz-attestor
    
      # Production cluster
      ${REGION}.prod-cluster:
        evaluationMode: REQUIRE_ATTESTATION
        enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
        requireAttestationsBy:
        - projects/${PROJECT_ID}/attestors/vulnz-attestor
        - projects/${PROJECT_ID}/attestors/qa-attestor
    EOF
    
  2. 将新政策上传到 Cloud 项目:

    gcloud container binauthz policy import ./binauthz-policy.yaml \
      --project "${PROJECT_ID}"
    

创建漏洞扫描检查工具并启用 API

创建在 Cloud Build 的构建步骤中使用的容器映像。此容器会将检测到的任何漏洞的严重性分数与配置的阈值进行比较。如果分数未超出阈值,则 Cloud Build 会在容器上创建证明。如果分数超出阈值,则构建失败,而不创建任何证明。

  1. 在 Cloud Shell 中,克隆 Binary Authorization 工具和示例应用源代码:

    git clone https://github.com/GoogleCloudPlatform/gke-binary-auth-tools ~/binauthz-tools
    
  2. 构建名为 cloudbuild-attestor 的漏洞扫描证明者容器并将其推送到 Container Registry:

    gcloud builds submit \
      --project "${PROJECT_ID}" \
      --tag "gcr.io/${PROJECT_ID}/cloudbuild-attestor" \
      ~/binauthz-tools
    
  3. 启用 Vulnerability Scanner API:

    gcloud services enable containerscanning.googleapis.com
    

设置 Cloud Build 流水线

为示例应用和 Kubernetes 清单创建 Cloud Source Repositories 代码库和 Cloud Build 触发器。

创建 hello-app Cloud Source Repositories 代码库

  1. 在 Cloud Shell 中,为示例应用创建 Cloud Source Repositories 代码库:

    gcloud source repos create hello-app \
      --project "${PROJECT_ID}"
    
  2. 在本地克隆代码库:

    gcloud source repos clone hello-app ~/hello-app \
      --project "${PROJECT_ID}"
    
  3. 将示例代码复制到代码库中:

    cp -R ~/binauthz-tools/examples/hello-app/* ~/hello-app
    

创建 hello-app Cloud Build 触发器

  1. 在 Cloud Console 中,转到触发器页面。

    转到“触发器”

  2. 点击创建触发器

  3. 触发器设置窗口中,输入以下详细信息:

    • 代码库字段中,从菜单中选择 hello-app
    • 名称字段中,输入 build-vulnz-deploy
    • 对于触发器类型,选择分支
    • 分支(正则表达式)字段中,输入 master
    • 构建配置部分,选择 Cloud Build 配置文件
    • Cloud Build 配置文件位置中,输入默认值 /cloudbuild.yaml
  4. 添加以下替代变量对:

    • _COMPUTE_REGION,值为 us-central1(或者您在开始时选择的地区)。
    • _KMS_KEYRING,值为 binauthz
    • _KMS_LOCATION,值为 us-central1(或者您在开始时选择的地区)。
    • _PROD_CLUSTER,值为 prod-cluster
    • _QA_ATTESTOR,值为 qa-attestor
    • _QA_KMS_KEY,值为 qa-signer
    • _QA_KMS_KEY_VERSION,值为 1
    • _STAGING_CLUSTER,值为 staging-cluster
    • _VULNZ_ATTESTOR,值为 vulnz-attestor
    • _VULNZ_KMS_KEY,值为 vulnz-signer
    • _VULNZ_KMS_KEY_VERSION,值为 1
  5. 点击创建触发器

测试 Cloud Build 流水线

如需测试 CI/CD 流水线,请提交示例应用并将其推送到 Cloud Source Repositories 代码库。Cloud Build 会检测更改、构建应用并将应用部署到 staging-cluster。流水线会等待长达 10 分钟的时间以进行质量保证验证。质量保证团队验证完部署之后,该过程会继续进行,系统将会更新生产 Kubernetes 清单,且 Cloud Build 会将应用部署到 prod-cluster

  1. 在 Cloud Shell 中,提交 hello-app 文件并将其推送到 Cloud Source Repositories 代码库以触发构建:

    cd ~/hello-app
    
    git add .
    git commit -m "Initial commit"
    git push origin master
    
  2. 在 Cloud Console 中,转到历史记录页面。

    转到“历史记录”页面

  3. 如需查看构建进度,请点击 Cloud Build 的最近一次运行。

    构建信息

  4. 完成在 staging-cluster 中的部署之后,请转到服务页面。

    转到“服务”页面

  5. 为验证应用是否正常工作,请点击该应用的端点链接。

  6. 转到映像页面。

    转到“映像”页面

  7. 点击 hello-app

  8. 点击您在模拟部署中验证过的映像。

    已验证映像的名称

  9. Digest 详细信息页面上,从映像详细信息复制 Digest 值。下一步需要用到此信息。

    映像的 Digest 值

  10. 如需应用手动质量保证证明,请将 ... 替换为您从映像详情中复制的值。DIGEST 变量的格式应该为 sha256:hash-value

    Await QA attestation 构建步骤还将输出一个可复制并粘贴的命令,如下所示。

    DIGEST="sha256:..." # Replace with your value
    
    gcloud beta container binauthz attestations sign-and-create \
      --project "${PROJECT_ID}" \
      --artifact-url "gcr.io/${PROJECT_ID}/hello-app@${DIGEST}" \
      --attestor "qa-attestor" \
      --attestor-project "${PROJECT_ID}" \
      --keyversion "1" \
      --keyversion-key "qa-signer" \
      --keyversion-location "${REGION}" \
      --keyversion-keyring "binauthz" \
      --keyversion-project "${PROJECT_ID}"
    
  11. 如需验证应用已经部署,请转到服务页面。

    转到“服务”页面

  12. 如需查看应用,请点击端点链接。

部署未经证明的映像

到目前为止,示例应用没有任何漏洞。更新该应用以输出其他消息并更改基础映像。

  1. 在 Cloud Shell 中,将输出从 Hello World 更改为 Binary Authorization,并将基础映像从 distroless 更改为 debian

    cd ~/hello-app
    sed -i "s/Hello World/Binary Authorization/g" main.go
    sed -i "s/FROM gcr\.io\/distroless\/static/FROM debian/g" Dockerfile
    
  2. 提交并推送更改:

    git add .
    git commit -m "Change message and base image"
    git push origin master
    
  3. 如需监控 CI/CD 流水线的状态,请在 Cloud Console 中转到历史记录页面。

    转到“历史记录”页面

    由于在映像中检测到 CVE,导致此构建失败。

  4. 如需检查发现的 CVE,请转到映像页面。

    转到“映像”页面

  5. 点击 hello-app

  6. 如需查看发现的 CVE,请点击最新映像的漏洞摘要。

    最近映像的漏洞摘要链接。

  7. 在 Cloud Shell 中,尝试在不通过漏洞扫描进行证明的情况下,将新映像部署到生产环境:

    export SHA_DIGEST="[SHA_DIGEST_VALUE]"
    
    cd ~/hello-app
    sed "s/GOOGLE_CLOUD_PROJECT/${DEVSHELL_PROJECT_ID}/g" \
        kubernetes/deployment.yaml.tpl | sed -e  \
        "s/DIGEST/${SHA_DIGEST}/g" > kubernetes/deployment.yaml
    
    gcloud container clusters get-credentials \
        --project=${DEVSHELL_PROJECT_ID} \
        --region="${REGION}" prod-cluster
    
    kubectl apply -f kubernetes
    
  8. 在 Cloud Console 中,转到工作负载页面。

    转到“工作负载”页面

    映像故障状态。

    映像部署失败,因为它未由 vulnz-attestorqa-attestor 进行签名。

紧急访问权限过程

有时,您需要允许在正常工作流程范围之外进行更改。若要部署不含必需证明的映像,pod 定义需要使用紧急访问权限政策标志进行注释。如果启用此标志,则 GKE 仍会检查是否存在必需的证明,但会允许部署容器映像并记录违规行为。

如需详细了解如何绕过证明检查,请参阅覆盖政策

  1. 在 Cloud Shell 中,取消注释 Kubernetes 清单中的紧急访问权限注释:

    sed -i "31s/^#//" kubernetes/deployment.yaml
    
  2. 使用 kubectl 应用更改:

    kubectl apply -f kubernetes
    
  3. 如需验证更改是否已部署到 prod-cluster,请转到 Cloud Console 中的工作负载页面。

    转到“工作负载”页面

    现在,部署错误消息消失了。

  4. 如需验证应用已经部署,请转到服务页面。

    转到“服务”页面

  5. 如需查看应用,请点击端点链接。

清理

为避免因本教程中使用的资源而导致您的 Google Cloud Platform 帐号产生费用,请执行以下操作:

删除项目

  1. 在 Cloud Console 中,转到管理资源页面。

    转到“管理资源”页面

  2. 在项目列表中,选择要删除的项目,然后点击删除
  3. 在对话框中输入项目 ID,然后点击关闭以删除项目。

后续步骤