安全构建和使用 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 创建部署政策,以仅允许部署来自受信任注册表的合规映像。

学习内容

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

2. 设置和要求

自定进度的环境设置

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

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.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. 访问 https://console.cloud.google.com 以访问您的 Google 项目。
  2. 点击右上角的 Cloud Shell 编辑器图标

8560cc8d45e8c112.png

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

环境设置

在 Cloud Shell 中,设置项目 ID 和项目编号。将它们保存为 PROJECT_IDPROJECT_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,以便在访问 Artifact Registry 时使用您的 gcloud 凭据。

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. On-Demand Scanning

在很多情况下,您可能需要先运行扫描,然后才能将映像推送到代码库。例如,容器开发者可能会先扫描映像并修复问题,然后再将代码推送到源代码控制系统。在以下示例中,您将在本地构建和分析图片,然后再根据结果采取行动。

构建映像

在此步骤中,您将使用本地 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) 

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

举报严重问题

人类很少直接使用报告中存储的数据。通常,自动化流程会使用这些结果。使用以下命令读取报告详情,并记录是否发现了任何严重漏洞

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 需要有权访问按需扫描 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

运行持续集成流水线

提交 build 以进行处理,以验证在发现严重性为 CRITICAL 的漏洞时 build 是否会中断。

gcloud builds submit

审核 build 失败

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

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

修复漏洞

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

使用以下命令覆盖 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 以进行处理,以验证在未发现严重漏洞的情况下 build 是否会成功。

gcloud builds submit

审核 build 成功

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

Cloud Build 历史记录页面中查看 build 是否成功

查看扫描结果

在 Artifact Registry 中查看正常的映像

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

6. 为映像签名

创建证明者备注

认证者备注只是一小部分数据,可用作所应用签名类型的标签。例如,一条备注可能用于指明漏洞扫描,而另一条备注可能用于质量检查签收。签名流程中会引用该备注。

创建记事

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-attestorbuilt-by-cloud-build。成功构建映像后,Cloud Build 会自动为其签名并创建证明。

添加 IAM 角色

二进制授权服务账号需要拥有查看认证备注的权限。使用以下 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

准备自定义 build Cloud Build 步骤

您将在 Cloud Build 中使用“自定义 build”步骤来简化认证流程。Google 提供了此“自定义 build”步骤,其中包含用于简化流程的辅助函数。在使用前,自定义构建步骤的代码必须构建到容器中并推送到 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 历史记录中查看 build

打开 Cloud 控制台,前往 Cloud Build 记录页面,查看最新的 build 以及构建步骤的成功执行情况。

8. 准入控制政策

二进制授权是 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!

所学内容:

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

后续步骤:

清理

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

删除项目

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