安全构建和使用 Cloud Build、Artifact Registry 和 GKE 进行部署

1. 简介

Container Analysis 为容器提供漏洞扫描和元数据存储服务。扫描服务会对 Artifact Registry 和 Container Registry 中的映像执行漏洞扫描,然后存储生成的元数据并通过 API 使其可供使用。元数据存储可让您存储来自不同来源(包括漏洞扫描、Google Cloud 服务和第三方提供商)的信息。

漏洞扫描可自动或按需执行:

  • 启用自动扫描后,您每次将新映像推送到 Artifact Registry 或 Container Registry 时,都会自动触发扫描。漏洞信息会随着新漏洞的发现而持续更新。
  • 启用按需扫描后,您必须运行一个命令来扫描本地映像或 Artifact Registry 或 Container Registry 中的映像。按需扫描可让您灵活地选择扫描容器的时间。例如,您可以扫描本地构建的映像并修复漏洞,然后再将其存储在注册表中。扫描结果最长可能需要 48 小时才能完成,而且扫描后漏洞信息不会更新。

将 Container Analysis 集成到 CI/CD 流水线中之后,您可以根据该元数据做出决策。例如,您可以使用 Binary Authorization 创建部署政策,以仅允许部署来自受信任注册表的合规映像。

学习内容

  • 如何启用自动扫描
  • 如何执行按需扫描
  • 如何在 build 流水线中集成扫描
  • 如何对已获批的映像进行签名
  • 如何使用 GKE 准入控制器来屏蔽映像
  • 如何配置 GKE 以仅允许已签名的获批映像

2. 设置和要求

自定进度的环境设置

  1. 登录 Google Cloud 控制台,然后创建一个新项目或重复使用现有项目。如果您还没有 Gmail 或 Google Workspace 账号,则必须创建一个。

b35bf95b8bf3d5d8.pnga99b7ace416376c4.pngbd84a6d3004737c5.png

  • 项目名称是此项目参与者的显示名称。它是 Google API 尚未使用的字符串。您可以随时更新。
  • 项目 ID 在所有 Google Cloud 项目中是唯一的,并且是不可变的(一经设置便无法更改)。Cloud 控制台会自动生成一个唯一字符串;通常情况下,您无需关注该字符串。在大多数 Codelab 中,您都需要引用项目 ID(通常用 PROJECT_ID 标识)。如果您不喜欢生成的 ID,可以再随机生成一个 ID。或者,您也可以尝试自己的项目 ID,看看是否可用。完成此步骤后便无法更改该 ID,并且此 ID 在项目期间会一直保留。
  • 此外,还有第三个值,即部分 API 使用的项目编号,供您参考。如需详细了解所有这三个值,请参阅文档。
  1. 接下来,您需要在 Cloud 控制台中启用结算功能,以便使用 Cloud 资源/API。运行此 Codelab 应该不会产生太多的费用(如果有费用的话)。若要关闭资源以避免产生超出本教程范围的结算费用,您可以删除自己创建的资源或删除整个项目。Google Cloud 的新用户符合参与 $300 USD 免费试用计划的条件。

启动 Cloudshell Editor

本实验经过设计和测试,可与 Google Cloud Shell 编辑器搭配使用。如需访问编辑器,请执行以下操作:

  1. 访问您的 Google 项目:https://console.cloud.google.com。
  2. 点击右上角的 Cloud Shell 编辑器图标

8560cc8d45e8c112.png

  1. 系统会在窗口底部打开一个新窗格

环境设置

在 Cloud Shell 中,为您的项目设置项目 ID 和项目编号。将它们保存为 PROJECT_ID 和 PROJECT_ID 变量。

export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID \
    --format='value(projectNumber)')

启用服务

启用所有必要的服务:

gcloud services enable \
  cloudkms.googleapis.com \
  cloudbuild.googleapis.com \
  container.googleapis.com \
  containerregistry.googleapis.com \
  artifactregistry.googleapis.com \
  containerscanning.googleapis.com \
  ondemandscanning.googleapis.com \
  binaryauthorization.googleapis.com 

创建 Artifact Registry 代码库

在本实验中,您将使用 Artifact Registry 来存储和扫描映像。使用以下命令创建代码库。

gcloud artifacts repositories create artifact-scanning-repo \
  --repository-format=docker \
  --location=us-central1 \
  --description="Docker repository"

将 Docker 配置为使用您的 gcloud 凭证访问 Artifact Registry。

gcloud auth configure-docker us-central1-docker.pkg.dev

3. 自动扫描

您每次将新映像推送到 Artifact Registry 或 Container Registry 时,都会自动触发制品扫描。漏洞信息会随着新漏洞的发现而持续更新。在本部分中,您将向 Artifact Registry 推送映像并查看结果。

创建并切换到工作目录

mkdir vuln-scan && cd vuln-scan

定义示例映像

创建一个名为 Dockerfile 且包含以下内容的文件。

cat > ./Dockerfile << EOF
FROM gcr.io/google-appengine/debian9@sha256:ebffcf0df9aa33f342c4e1d4c8428b784fc571cdf6fbab0b31330347ca8af97a

# System
RUN apt update && apt install python3-pip -y

# App
WORKDIR /app
COPY . ./

RUN pip3 install Flask==1.1.4
RUN pip3 install gunicorn==20.1.0

CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 main:app

EOF

创建一个名为 main.py 且包含以下内容的文件

cat > ./main.py << EOF
import os
from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello_world():
    name = os.environ.get("NAME", "Worlds")
    return "Hello {}!".format(name)

if __name__ == "__main__":
    app.run(debug=True, host="0.0.0.0", port=int(os.environ.get("PORT", 8080)))
EOF

构建映像并将其推送到 AR

使用 Cloud Build 构建容器并自动将其推送到 Artifact Registry。请注意图片上的标记 bad。这有助于您在后续步骤中识别该指纹。

gcloud builds submit . -t us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image:bad

查看映像详情

构建流程完成后,在 Artifact Registry 信息中心查看映像和漏洞扫描结果。

  1. 在 Cloud 控制台中打开 Artifact Registry
  2. 点击 artifact-scanning-repo 以查看其内容
  3. 点击进入映像详情
  4. 点击进入映像的最新摘要
  5. 扫描完成后,点击相应映像的“漏洞”标签页

在“漏洞”标签页中,您会看到刚刚构建的映像的自动扫描结果。

361be7b3bf293fca.png

默认情况下,系统会启用自动扫描功能。查看 Artifact Registry 设置,了解如何开启/关闭自动扫描。

4. 按需扫描

在将映像推送到仓库之前,您可能需要在各种场景下运行扫描。例如,容器开发者可能会扫描映像并修复发现的问题,然后再将代码推送到源代码控制系统。在下面的示例中,您将先在本地构建和分析映像,然后再根据结果采取行动。

构建映像

在此步骤中,您将使用本地 Docker 将映像构建到本地缓存中。

docker build -t us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image .

扫描图片

映像构建完成后,请求扫描该映像。扫描结果将存储在元数据服务器中。作业完成后,元数据服务器中会显示结果的位置。

gcloud artifacts docker images scan \
    us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image \
    --format="value(response.scan)" > scan_id.txt

查看输出文件

花点时间查看存储在 scan_id.txt 文件中的上一步的输出。请注意元数据服务器中扫描结果的报告位置。

cat scan_id.txt

查看详细的扫描结果

如需查看实际扫描结果,请对输出文件中记录的报告位置使用 list-vulnerabilities 命令。

gcloud artifacts docker images list-vulnerabilities $(cat scan_id.txt) 

输出包含大量有关映像中所有漏洞的数据。

标记严重问题

人类用户很少直接使用报告中存储的数据。该结果通常供自动化流程使用。使用以下命令读取报告详细信息,并在发现任何严重程度为 CRITICAL 的漏洞时进行记录

export SEVERITY=CRITICAL

gcloud artifacts docker images list-vulnerabilities $(cat scan_id.txt) --format="value(vulnerability.effectiveSeverity)" | if grep -Fxq ${SEVERITY}; then echo "Failed vulnerability check for ${SEVERITY} level"; else echo "No ${SEVERITY} Vulnerabilities found"; fi

此命令的输出将为

Failed vulnerability check for CRITICAL level

5. 构建流水线扫描

在本部分中,您将创建一个自动化构建流水线来构建容器映像、扫描该映像,然后评估结果。如果未发现严重漏洞,则会将映像推送到仓库。如果发现严重漏洞,构建将失败并退出。

为 Cloud Build 服务账号提供访问权限

Cloud Build 需要有权访问 On-Demand Scanning API。使用以下命令授予访问权限。

gcloud projects add-iam-policy-binding ${PROJECT_ID} \
        --member="serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" \
        --role="roles/iam.serviceAccountUser"
        
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
        --member="serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" \
        --role="roles/ondemandscanning.admin"

创建 Cloud Build 流水线

以下命令将在您的目录中创建一个 cloudbuild.yaml 文件,该文件将用于自动化流程。此示例中的步骤仅限于容器构建流程。不过,在实际操作中,除容器步骤外,您还需要添加特定于应用的说明和测试。

使用以下命令创建文件。

cat > ./cloudbuild.yaml << EOF
steps:

# build
- id: "build"
  name: 'gcr.io/cloud-builders/docker'
  args: ['build', '-t', 'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image', '.']
  waitFor: ['-']

#Run a vulnerability scan at _SECURITY level
- id: scan
  name: 'gcr.io/cloud-builders/gcloud'
  entrypoint: 'bash'
  args:
  - '-c'
  - |
    (gcloud artifacts docker images scan \
    us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image \
    --location us \
    --format="value(response.scan)") > /workspace/scan_id.txt

#Analyze the result of the scan
- id: severity check
  name: 'gcr.io/cloud-builders/gcloud'
  entrypoint: 'bash'
  args:
  - '-c'
  - |
      gcloud artifacts docker images list-vulnerabilities \$(cat /workspace/scan_id.txt) \
      --format="value(vulnerability.effectiveSeverity)" | if grep -Fxq CRITICAL; \
      then echo "Failed vulnerability check for CRITICAL level" && exit 1; else echo "No CRITICAL vulnerability found, congrats !" && exit 0; fi

#Retag
- id: "retag"
  name: 'gcr.io/cloud-builders/docker'
  args: ['tag',  'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image', 'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image:good']


#pushing to artifact registry
- id: "push"
  name: 'gcr.io/cloud-builders/docker'
  args: ['push',  'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image:good']

images:
  - us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image
EOF

运行 CI 流水线

提交 build 以供处理,验证在发现  CRITICAL 严重级别的漏洞时,构建会中断。

gcloud builds submit

查看构建失败

您刚刚提交的 build 将会失败,因为映像包含严重漏洞。

在 Cloud Build 历史记录页面中查看构建失败情况

修复漏洞

更新 Dockerfile 以使用不包含 CRITICAL 严重级别漏洞的基础映像。

使用以下命令覆盖 Dockerfile,以使用 Debian 10 映像

cat > ./Dockerfile << EOF
from python:3.8-slim  

# App
WORKDIR /app
COPY . ./

RUN pip3 install Flask==2.1.0
RUN pip3 install gunicorn==20.1.0

CMD exec gunicorn --bind :\$PORT --workers 1 --threads 8 main:app

EOF

使用良好的映像运行 CI 流程

提交 build 以供处理,验证在未发现  CRITICAL 严重级别的漏洞时,构建会成功。

gcloud builds submit

查看 build 是否成功

您刚刚提交的 build 将成功完成,因为更新后的映像不包含任何 CRITICAL 严重级别的漏洞。

在 Cloud Build 历史记录页面中查看构建成功情况

查看扫描结果

查看 Artifact Registry 中的良好映像

  1. 在 Cloud 控制台中打开 Artifact Registry
  2. 点击 artifact-scanning-repo 以查看其内容
  3. 点击进入映像详情
  4. 点击进入映像的最新摘要
  5. 点击映像的“漏洞”标签页

6. 为映像签名

创建证明者备注

证明者备注 (Attestor Note) 本质上是一小段数据,充当所添加签名类型的标签。例如,一个备注可能代表漏洞扫描,而另一个备注可能用于质量保证签核。在签名过程中,系统会引用相应备注。

创建记事

cat > ./vulnz_note.json << EOM
{
  "attestation": {
    "hint": {
      "human_readable_name": "Container Vulnerabilities attestation authority"
    }
  }
}
EOM

存储备注

NOTE_ID=vulnz_note

curl -vvv -X POST \
    -H "Content-Type: application/json"  \
    -H "Authorization: Bearer $(gcloud auth print-access-token)"  \
    --data-binary @./vulnz_note.json  \
    "https://containeranalysis.googleapis.com/v1/projects/${PROJECT_ID}/notes/?noteId=${NOTE_ID}"

验证备注

curl -vvv  \
    -H "Authorization: Bearer $(gcloud auth print-access-token)" \
    "https://containeranalysis.googleapis.com/v1/projects/${PROJECT_ID}/notes/${NOTE_ID}"

创建证明者

证明者负责执行实际的映像签名流程,并将备注的发生实例附加到映像,以供日后验证。创建证明者以供日后使用。

创建证明者

ATTESTOR_ID=vulnz-attestor

gcloud container binauthz attestors create $ATTESTOR_ID \
    --attestation-authority-note=$NOTE_ID \
    --attestation-authority-note-project=${PROJECT_ID}

验证证明者

gcloud container binauthz attestors list

请注意,最后一行显示 NUM_PUBLIC_KEYS: 0,您在后续步骤中需要提供密钥

另请注意,当您运行构建任务来生成映像时,Cloud Build 会自动在您的项目中创建 built-by-cloud-build 证明者。因此,上述命令会返回两个证明者,即 vulnz-attestor 和 built-by-cloud-build。成功构建映像后,Cloud Build 会自动为其签名并创建相应的证明。

添加 IAM 角色

Binary Authorization 服务账号需要拥有查看证明备注的权限。使用以下 API 调用提供访问权限

PROJECT_NUMBER=$(gcloud projects describe "${PROJECT_ID}"  --format="value(projectNumber)")

BINAUTHZ_SA_EMAIL="service-${PROJECT_NUMBER}@gcp-sa-binaryauthorization.iam.gserviceaccount.com"


cat > ./iam_request.json << EOM
{
  'resource': 'projects/${PROJECT_ID}/notes/${NOTE_ID}',
  'policy': {
    'bindings': [
      {
        'role': 'roles/containeranalysis.notes.occurrences.viewer',
        'members': [
          'serviceAccount:${BINAUTHZ_SA_EMAIL}'
        ]
      }
    ]
  }
}
EOM

使用该文件创建 IAM 政策

curl -X POST  \
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer $(gcloud auth print-access-token)" \
    --data-binary @./iam_request.json \
    "https://containeranalysis.googleapis.com/v1/projects/${PROJECT_ID}/notes/${NOTE_ID}:setIamPolicy"

添加 KMS 密钥

证明者需要加密密钥才能附加注释并提供可验证的签名。在此步骤中,您将在 KMS 中创建和存储密钥,以便 Cloud Build 稍后访问。

首先,添加一些环境变量来描述新密钥

KEY_LOCATION=global
KEYRING=binauthz-keys
KEY_NAME=codelab-key
KEY_VERSION=1

创建密钥环来保存一组密钥

gcloud kms keyrings create "${KEYRING}" --location="${KEY_LOCATION}"

为证明者创建新的非对称签名密钥对

gcloud kms keys create "${KEY_NAME}" \
    --keyring="${KEYRING}" --location="${KEY_LOCATION}" \
    --purpose asymmetric-signing   \
    --default-algorithm="ec-sign-p256-sha256"

您应该会在 Google Cloud 控制台的 KMS 页面上看到该密钥。

现在运行 gcloud binauthz 命令,将该密钥与您的证明者进行关联:

gcloud beta container binauthz attestors public-keys add  \
    --attestor="${ATTESTOR_ID}"  \
    --keyversion-project="${PROJECT_ID}"  \
    --keyversion-location="${KEY_LOCATION}" \
    --keyversion-keyring="${KEYRING}" \
    --keyversion-key="${KEY_NAME}" \
    --keyversion="${KEY_VERSION}"

如果您再次输出授权方列表,现在应该会看到已注册的密钥:

gcloud container binauthz attestors list

创建签名证明

至此,您已配置好了为映像进行签名所必需的各项功能。使用您之前创建的证明者,为您一直在处理的容器映像进行签名

CONTAINER_PATH=us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image

DIGEST=$(gcloud container images describe ${CONTAINER_PATH}:latest \
    --format='get(image_summary.digest)')

现在,您可以使用 gcloud 来创建证明。该命令只需要输入您要用于签名的密钥的详细信息,以及您要批准的特定容器映像

gcloud beta container binauthz attestations sign-and-create  \
    --artifact-url="${CONTAINER_PATH}@${DIGEST}" \
    --attestor="${ATTESTOR_ID}" \
    --attestor-project="${PROJECT_ID}" \
    --keyversion-project="${PROJECT_ID}" \
    --keyversion-location="${KEY_LOCATION}" \
    --keyversion-keyring="${KEYRING}" \
    --keyversion-key="${KEY_NAME}" \
    --keyversion="${KEY_VERSION}"

按照 Container Analysis 的术语,这将创建一个新的发生实例,并将其附加到证明者的备注。为确保一切运作都符合预期,您可以列出您的证明

gcloud container binauthz attestations list \
   --attestor=$ATTESTOR_ID --attestor-project=${PROJECT_ID}

7. 使用 Cloud Build 进行签名

您已经启用了映像签名功能,并手动使用证明者为您的示例映像进行了签名。但在实践中,您可能希望在 CI/CD 流水线等自动化流程中应用证明。

在本部分中,您将配置 Cloud Build 以实现自动为映像进行证明

角色

将 Binary Authorization Attestor Viewer 角色添加到 Cloud Build 服务账号:

gcloud projects add-iam-policy-binding ${PROJECT_ID} \
  --member serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com \
  --role roles/binaryauthorization.attestorsViewer

将 Cloud KMS CryptoKey Signer/Verifier 角色添加到 Cloud Build 服务账号(基于 KMS 的签名):

gcloud projects add-iam-policy-binding ${PROJECT_ID} \
  --member serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com \
  --role roles/cloudkms.signerVerifier

将 Container Analysis Notes Attacher 角色添加到 Cloud Build 服务账号:

gcloud projects add-iam-policy-binding ${PROJECT_ID} \
  --member serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com \
  --role roles/containeranalysis.notes.attacher

准备 Cloud Build 自定义构建步骤

在 Cloud Build 中,您将使用自定义构建步骤来简化证明流程。Google 提供了此自定义构建步骤,其中包含一些辅助函数,可用于简化流程。在使用前,必须将自定义构建步骤的代码构建到容器中,并推送到 Cloud Build。为此,请运行以下命令:

git clone https://github.com/GoogleCloudPlatform/cloud-builders-community.git
cd cloud-builders-community/binauthz-attestation
gcloud builds submit . --config cloudbuild.yaml
cd ../..
rm -rf cloud-builders-community

向您的 cloudbuild.yaml 中添加签名步骤

在此步骤中,您将向之前构建的 Cloud Build 流水线添加证明步骤。

  1. 查看您将要添加的新步骤。

仅供查看。请勿复制

#Sign the image only if the previous severity check passes
- id: 'create-attestation'
  name: 'gcr.io/${PROJECT_ID}/binauthz-attestation:latest'
  args:
    - '--artifact-url'
    - 'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image'
    - '--attestor'
    - 'projects/${PROJECT_ID}/attestors/$ATTESTOR_ID'
    - '--keyversion'
    - 'projects/${PROJECT_ID}/locations/$KEY_LOCATION/keyRings/$KEYRING/cryptoKeys/$KEY_NAME/cryptoKeyVersions/$KEY_VERSION'
  1. 使用更新后的完整流水线覆盖您的 cloudbuild.yaml 文件。
cat > ./cloudbuild.yaml << EOF
steps:

# build
- id: "build"
  name: 'gcr.io/cloud-builders/docker'
  args: ['build', '-t', 'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image', '.']
  waitFor: ['-']

#Run a vulnerability scan at _SECURITY level
- id: scan
  name: 'gcr.io/cloud-builders/gcloud'
  entrypoint: 'bash'
  args:
  - '-c'
  - |
    (gcloud artifacts docker images scan \
    us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image \
    --location us \
    --format="value(response.scan)") > /workspace/scan_id.txt

#Analyze the result of the scan
- id: severity check
  name: 'gcr.io/cloud-builders/gcloud'
  entrypoint: 'bash'
  args:
  - '-c'
  - |
      gcloud artifacts docker images list-vulnerabilities \$(cat /workspace/scan_id.txt) \
      --format="value(vulnerability.effectiveSeverity)" | if grep -Fxq CRITICAL; \
      then echo "Failed vulnerability check for CRITICAL level" && exit 1; else echo "No CRITICAL vulnerability found, congrats !" && exit 0; fi

#Retag
- id: "retag"
  name: 'gcr.io/cloud-builders/docker'
  args: ['tag',  'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image', 'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image:good']


#pushing to artifact registry
- id: "push"
  name: 'gcr.io/cloud-builders/docker'
  args: ['push',  'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image:good']


#Sign the image only if the previous severity check passes
- id: 'create-attestation'
  name: 'gcr.io/${PROJECT_ID}/binauthz-attestation:latest'
  args:
    - '--artifact-url'
    - 'us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image:good'
    - '--attestor'
    - 'projects/${PROJECT_ID}/attestors/$ATTESTOR_ID'
    - '--keyversion'
    - 'projects/${PROJECT_ID}/locations/$KEY_LOCATION/keyRings/$KEYRING/cryptoKeys/$KEY_NAME/cryptoKeyVersions/$KEY_VERSION'



images:
  - us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image:good
EOF

运行 build

gcloud builds submit

在 Cloud Build 历史记录中查看构建记录

打开 Cloud 控制台中的 Cloud Build 历史记录页面,查看最新构建记录,并确认各构建步骤均已成功执行。

8. 准入控制政策

Binary Authorization 是 GKE 和 Cloud Run 中的一项功能,用于在容器映像获准运行之前对其进行规则验证。无论请求是来自受信任的 CI/CD 流水线,还是来自用户的手动部署尝试,该验证机制会对每一项运行映像的请求执行验证。相比仅进行 CI/CD 流水线检查,此功能可让您更有效地保护运行时环境。

为了解此功能,您需要修改默认的 GKE 政策,以强制执行严格的授权规则。

创建 GKE 集群

创建 GKE 集群:

gcloud beta container clusters create binauthz \
    --zone us-central1-a  \
    --binauthz-evaluation-mode=PROJECT_SINGLETON_POLICY_ENFORCE

允许 Cloud Build 部署到此集群:

gcloud projects add-iam-policy-binding ${PROJECT_ID} \
        --member="serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" \
        --role="roles/container.developer"

“全部允许”政策

首先,验证默认政策状态以及您部署任何映像的能力

  1. 查看现有政策
gcloud container binauthz policy export
  1. 请注意,强制执行政策已设置为 ALWAYS_ALLOW

evaluationMode: ALWAYS_ALLOW

  1. 部署示例映像,以验证您可以部署任何映像
kubectl run hello-server --image gcr.io/google-samples/hello-app:1.0 --port 8080
  1. 验证部署是否成功
kubectl get pods

您将看到以下输出内容

161db370d99ffb13.png

  1. 删除部署
kubectl delete pod hello-server

“全部拒绝”政策

现在,将政策更新为禁止所有映像。

  1. 将当前政策导出到可修改的文件
gcloud container binauthz policy export  > policy.yaml
  1. 更改政策

在文本编辑器中,将 evaluationMode 从 ALWAYS_ALLOW 更改为 ALWAYS_DENY。

edit policy.yaml

YAML 格式的政策文件应如下所示:

globalPolicyEvaluationMode: ENABLE
defaultAdmissionRule:
  evaluationMode: ALWAYS_DENY
  enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
name: projects/PROJECT_ID/policy
  1. 打开终端并应用新政策,然后等待几秒钟,以便更改生效
gcloud container binauthz policy import policy.yaml
  1. 尝试部署示例工作负载
kubectl run hello-server --image gcr.io/google-samples/hello-app:1.0 --port 8080
  1. 部署失败,并显示以下消息
Error from server (VIOLATES_POLICY): admission webhook "imagepolicywebhook.image-policy.k8s.io" denied the request: Image gcr.io/google-samples/hello-app:1.0 denied by Binary Authorization default admission rule. Denied by always_deny admission rule

将政策还原为“全部允许”

在继续学习下一部分之前,请务必还原对政策所做的更改

  1. 更改政策

在文本编辑器中,将 evaluationMode 从 ALWAYS_DENY 更改为 ALWAYS_ALLOW。

edit policy.yaml

YAML 格式的政策文件应如下所示:

globalPolicyEvaluationMode: ENABLE
defaultAdmissionRule:
  evaluationMode: ALWAYS_ALLOW
  enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
name: projects/PROJECT_ID/policy
  1. 应用还原后的政策
gcloud container binauthz policy import policy.yaml

9. 在 GKE 中屏蔽漏洞

在本部分中,您将结合目前所学知识,通过 Cloud Build 实现 CI/CD 流水线,该流水线会扫描映像,然后在对映像进行签名并尝试部署之前检查是否存在漏洞。GKE 将使用 Binary Authorization 来验证映像是否具有来自漏洞扫描的签名,然后再允许映像运行。

d5c41bb89e22fd61.png

更新 GKE 政策以要求进行证明

向您的 GKE BinAuth 政策中添加 clusterAdmissionRules,要求映像必须由您的证明者签名。

运行以下命令,使用更新后的配置覆盖原有政策。

COMPUTE_ZONE=us-central1-a

cat > binauth_policy.yaml << EOM
defaultAdmissionRule:
  enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
  evaluationMode: ALWAYS_DENY
globalPolicyEvaluationMode: ENABLE
clusterAdmissionRules:
  ${COMPUTE_ZONE}.binauthz:
    evaluationMode: REQUIRE_ATTESTATION
    enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
    requireAttestationsBy:
    - projects/${PROJECT_ID}/attestors/vulnz-attestor
EOM

应用政策

gcloud beta container binauthz policy import binauth_policy.yaml

尝试部署未签名的映像

使用以下命令为您之前构建的应用创建部署描述符。此处使用的映像是您之前构建的包含严重漏洞且不包含已签名证明的映像。

GKE 准入控制器需要知道要部署的确切映像,以便始终如一地验证签名。为此,您需要使用映像摘要,而不是简单的代码。

获取不良映像的映像摘要

CONTAINER_PATH=us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image


DIGEST=$(gcloud container images describe ${CONTAINER_PATH}:bad \
    --format='get(image_summary.digest)')

在 Kubernetes 配置中使用该摘要

cat > deploy.yaml << EOM
apiVersion: v1
kind: Service
metadata:
  name: deb-httpd
spec:
  selector:
    app: deb-httpd
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deb-httpd
spec:
  replicas: 1
  selector:
    matchLabels:
      app: deb-httpd
  template:
    metadata:
      labels:
        app: deb-httpd
    spec:
      containers:
      - name: deb-httpd
        image: ${CONTAINER_PATH}@${DIGEST}
        ports:
        - containerPort: 8080
        env:
          - name: PORT
            value: "8080"

EOM

尝试将应用部署到 GKE

kubectl apply -f deploy.yaml

在控制台中查看工作负载,并记下指出部署被拒绝的错误:

No attestations found that were valid and signed by a key trusted by the attestor

部署已签名的映像

获取不良映像的映像摘要

CONTAINER_PATH=us-central1-docker.pkg.dev/${PROJECT_ID}/artifact-scanning-repo/sample-image


DIGEST=$(gcloud container images describe ${CONTAINER_PATH}:good \
    --format='get(image_summary.digest)')

在 Kubernetes 配置中使用该摘要

cat > deploy.yaml << EOM
apiVersion: v1
kind: Service
metadata:
  name: deb-httpd
spec:
  selector:
    app: deb-httpd
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deb-httpd
spec:
  replicas: 1
  selector:
    matchLabels:
      app: deb-httpd
  template:
    metadata:
      labels:
        app: deb-httpd
    spec:
      containers:
      - name: deb-httpd
        image: ${CONTAINER_PATH}@${DIGEST}
        ports:
        - containerPort: 8080
        env:
          - name: PORT
            value: "8080"

EOM

将应用部署到 GKE

kubectl apply -f deploy.yaml

在控制台中查看工作负载,并记下映像是否已成功部署。

10. 恭喜!

恭喜,您已完成此 Codelab!

所学内容:

  • 如何启用自动扫描
  • 如何执行按需扫描
  • 如何在 build 流水线中集成扫描
  • 如何对已获批的映像进行签名
  • 如何使用 GKE 准入控制器来屏蔽映像
  • 如何配置 GKE 以仅允许已签名的获批映像

后续步骤:

清理

为避免因本教程中使用的资源导致您的 Google Cloud 账号产生费用,请删除包含这些资源的项目,或者保留项目但删除各个资源。

删除项目

若要避免产生费用,最简单的方法是删除您为本教程创建的项目。