1. 概览
此 Codelab 以机密空间 Codelab 为基础。支持签名容器映像,可以选择使用经过认证的公钥对容器进行身份验证,而不是在工作负载身份池 (WIP) 政策中指定映像摘要。
机密 Space 支持签名容器映像的变化如下:
提高易用性:随着已签名的容器映像功能的推出,我们现在可以将工作负载映像摘要方法转为容器签名方法,以供对映像授权的协作者/审核人员使用。
- 如果直接使用映像摘要,资源所有者每次授权新映像时都必须使用映像摘要更新政策。通过使用映像签名,该政策包含公钥指纹,其相应私钥由协作者/审核人员拥有,并用于对审核的映像进行签名。
- 对于某些安全模型,引用可信映像签名密钥比更新新映像摘要值列表更方便。
没有安全回归:与之前的映像摘要方法相比,此容器签名方法不会带来任何安全回归,因为信任边界保持不变。在容器签名方法中,资源所有者通过在 WIP 政策中指定可信公钥指纹来授权验证密钥,并且授权检查由证明验证程序服务和 WIP 执行;证明验证程序服务会验证签名是否与正在运行的工作负载相关联,而 WIP 政策会检查服务声明的公钥是否已获得政策的授权。
强大的安全性:使用容器映像签名可以将一定程度的信任委托给映像签名者。通过在证明政策中指定可信签名者的公钥指纹,资源所有者会授权该签名者对哪些容器映像符合政策进行背书。证明验证程序服务会验证签名是否与正在运行的工作负载相关联,并由政策检查创建签名的公钥是否已获得政策的授权。通过这种方式,映像签名提供的额外间接层可确保机密空间的强大安全保证。
这两种方法之间的唯一区别在于,后一种方法使用了一个额外的间接层,其中使用签名密钥授权工作负载映像。这不会引入任何新的安全漏洞,因为信任边界保持不变。
学习内容
在此 Codelab 中,您将学习如何利用容器映像签名来授予对受保护资源的访问权限:
- 如何使用
cosign
对审核的容器映像进行签名 - 如何将容器映像签名上传到 OCI 注册表以进行签名发现和存储
- 如何配置运行 Secret Space 所必需的云资源
- 如何在支持签名容器映像的机密空间中运行工作负载
此 Codelab 介绍了如何使用机密空间远程证明由在 Google Compute Engine 上运行的可信密钥签名的容器映像。
所需条件
- 完成机密空间 Codelab
- 一个 Google Cloud Platform 项目
- 浏览器,例如 Chrome 或 Firefox
- 熟悉标准的 Linux 文本编辑器,例如 Vim、Emacs 或 Nano
- 具备 Sigstore 协同签名的基础知识
- 具备 Google Compute Engine ( Codelab)、机密虚拟机、容器和远程代码库的基础知识
- 具备 Cloud KMS 基础知识 ( Codelab)
- 具备服务账号、工作负载身份联合和属性条件方面的基础知识。
- 有关 Artifact Registry 的基础知识
- 具备数字签名的基础知识
带有签名容器映像的机密空间中涉及的角色
在此 Codelab 中,Primus Bank 是审核人员和资源所有者,负责以下事项:
- 使用示例数据设置所需的资源。
- 审核工作负载代码。
- 使用
cosign
为工作负载映像签名。 - 将签名上传到代码库。
- 配置 WIP 政策以保护客户数据。
Secundus Bank 是工作负载的编写和运营者,并负责:
- 设置所需的资源以存储结果。
- 编写工作负载代码。
- 发布工作负载映像。
- 在支持签名容器映像的机密空间中运行工作负载。
Secundus Bank 将开发并发布一个工作负载,用于查询存储在 Cloud Storage 存储分区中且归 Primus Bank 所有的客户数据。Primus Bank 将审核工作负载、为容器映像签名,并配置 WIP 政策以允许已批准的工作负载访问其数据。此工作负载的执行结果将存储在 Secundus 银行所有的 Cloud Storage 存储分区中。
机密聊天室设置中涉及的资源
此 Codelab 引用了一些变量,您应该为 GCP 项目设置适当的值。此 Codelab 中的命令假定已设置这些变量。(例如,export PRIMUS_INPUT_STORAGE_BUCKET='my-input-bucket'
可用于设置 Primus bank 的输入存储分区的名称。)如果尚未设置资源名称变量,系统将根据 GCP project-id 生成该变量。
在 Primus 项目中配置以下内容:
$PRIMUS_INPUT_STORAGE_BUCKET
:存储客户数据文件的存储分区。$PRIMUS_WORKLOAD_IDENTITY_POOL
:用于验证声明的工作负载身份池 (WIP)。$PRIMUS_WIP_PROVIDER
:工作负载身份池提供方,其中包含用于由证明验证程序服务签名的令牌的授权条件。$PRIMUS_SERVICEACCOUNT
:$PRIMUS_WORKLOAD_IDENTITY_POOL
用于访问受保护资源的服务账号。在此步骤中,该账号有权查看存储在$PRIMUS_INPUT_STORAGE_BUCKET
存储分区中的客户数据。$PRIMUS_ENC_KEY
:用于加密$PRIMUS_INPUT_STORAGE_BUCKET
中存储的数据的 KMS 密钥。
本 Codelab 的新资源:
$PRIMUS_COSIGN_REPOSITORY
:用于存储工作负载映像签名的 Artifact Registry。$PRIMUS_SIGNING_KEY
:审核人员/数据协作者(在本例中为 primus bank)用于为工作负载映像签名的 KMS 密钥。
在 Secundus 项目中配置以下内容:
$SECUNDUS_ARTIFACT_REGISTRY
:将在其中推送工作负载 Docker 映像的 Artifact Registry。$WORKLOAD_IMAGE_NAME
:工作负载 Docker 映像的名称。$WORKLOAD_IMAGE_TAG
:工作负载 Docker 映像的标记。$WORKLOAD_SERVICEACCOUNT
:有权访问运行工作负载的机密虚拟机的服务账号。$SECUNDUS_RESULT_BUCKET
:用于存储工作负载结果的存储分区。
其他资源:
primus_customer_list.csv
包含客户数据。我们会将这些数据上传到$PRIMUS_INPUT_STORAGE_BUCKET
,并创建用于查询这些数据的工作负载。
现有工作流
在机密空间中运行工作负载时,系统会使用配置的资源完成以下过程:
- 工作负载从 WIP 请求
$PRIMUS_SERVICEACCOUNT
的常规 Google 访问令牌。它会提供带有工作负载和环境声明的证明验证程序服务令牌。 - 如果证明验证程序服务令牌中的工作负载衡量声明与 WIP 中的属性条件匹配,则会返回
$PRIMUS_SERVICEACCOUNT.
的访问令牌 - 工作负载使用与
$PRIMUS_SERVICEACCOUNT
关联的服务账号访问令牌来访问$PRIMUS_INPUT_STORAGE_BUCKET
存储分区中的客户数据。 - 工作负载会对这些数据执行操作。
- 工作负载使用
$WORKLOAD_SERVICEACCOUNT
服务账号将该操作的结果写入$SECUNDUS_RESULT_STORAGE_BUCKET
存储分区。
支持签名容器的新工作流
对已签名容器的支持将集成到现有工作流中,如下突出显示。当您通过签名容器映像支持在机密空间中运行工作负载时,系统将使用配置的资源完成以下过程:
- 机密空间会发现与当前运行的工作负载映像相关的任何容器签名,并将这些签名发送到证明验证程序。认证验证程序会验证签名,并在认证声明中包含所有有效签名。
- 工作负载从 WIP 请求
$PRIMUS_SERVICEACCOUNT
的常规 Google 访问令牌。它会提供带有工作负载和环境声明的证明验证程序服务令牌。 - 如果证明验证程序服务令牌中的容器签名声明与 WIP 中的属性条件匹配,则会返回
$PRIMUS_SERVICEACCOUNT
的访问令牌。 - 工作负载使用与
$PRIMUS_SERVICEACCOUNT
关联的服务账号访问令牌来访问$PRIMUS_INPUT_STORAGE_BUCKET
存储分区中的客户数据。 - 工作负载会对这些数据执行操作。
- 工作负载使用
$WORKLOAD_SERVICEACCOUNT
将该操作的结果写入$SECUNDUS_RESULT_STORAGE_BUCKET
存储分区。
2. 设置 Cloud 资源
在设置机密空间时,首先您需要在 Primus 和 Secundus 银行的 GCP 项目下创建所需的云资源。以下是此 Codelab 的新增资源:
在 Primus 项目中:
- KMS 签名密钥,用于在审核代码后为 Secundus 工作负载签名。
- 用于存储 Cosign 签名的 Artifact Registry 仓库。
Secundus 项目中没有新资源。设置完这些资源后,您将为工作负载创建具有所需角色和权限的服务账号。然后,您将创建工作负载映像,由审核工具 Primus 银行对工作负载映像进行签名。然后,工作负载将由数据协作者(此 Codelab 中的 Primus 银行)授权,工作负载运营商(在本例中为 Secundus Bank)将运行工作负载。
在设置机密空间的过程中,您将在 Primus 和 Secundus GCP 项目中创建所需的云资源。
准备工作
- 使用以下命令克隆 此代码库,以获取此 Codelab 中使用的必需脚本。
$ git clone https://github.com/GoogleCloudPlatform/confidential-space
- 确保您已按如下所示设置所需的项目。
$ export PRIMUS_PROJECT_ID=<GCP project id of primus bank>
$ export SECUNDUS_PROJECT_ID=<GCP project id of secundus bank>
- 使用此命令为上述资源名称设置变量。您可以使用这些变量(例如
export PRIMUS_INPUT_STORAGE_BUCKET='my-input-bucket'
)替换资源名称 - 运行以下脚本,根据资源名称的项目 ID,将其余变量名称设置为相应的值。
$ source config_env.sh
- 按照此处的说明安装协同签名。
设置 Primus 银行资源
在此步骤中,您将为 Primus 银行设置所需的云资源。运行以下脚本,为 Primus 银行设置资源。在这些步骤中,系统会创建下述资源:
- Cloud Storage 存储分区 (
$PRIMUS_INPUT_STORAGE_BUCKET
),用于存储 Primus 银行的加密客户数据文件。 - KMS 中的加密密钥 (
$PRIMUS_ENC_KEY
) 和密钥环 ($PRIMUS_ENC_KEYRING
),用于加密 Primus 银行的数据文件。 - 工作负载身份池 (
$PRIMUS_WORKLOAD_IDENTITY_POOL
),用于根据在其提供方下配置的特性条件验证声明。 - 与上述工作负载身份池 (
$PRIMUS_WORKLOAD_IDENTITY_POOL
) 关联的服务账号 ($PRIMUS_SERVICEACCOUNT
),具有以下 IAM 访问权限: roles/cloudkms.cryptoKeyDecrypter
,用于使用 KMS 密钥解密数据。objectViewer
:用于从 Cloud Storage 存储分区读取数据。roles/iam.workloadIdentityUser
(用于将此服务账号连接到工作负载身份池)。
$ ./setup_primus_bank_resources.sh
设置 Secundus 银行资源
在此步骤中,您将为 Secundus 银行设置所需的云资源。运行以下脚本,为 Secundus 银行设置资源。在此步骤中,系统会创建下述资源:
- Cloud Storage 存储分区 (
$SECUNDUS_RESULT_STORAGE_BUCKET
),用于存储 Secundus 银行的工作负载执行结果。
$ ./setup_secundus_bank_resources.sh
3. 创建工作负载并为其签名
创建工作负载服务账号
现在,您将为工作负载创建具有所需角色和权限的服务账号。运行以下脚本,在 Secundus 银行项目中创建工作负载服务账号。此服务账号将供运行工作负载的虚拟机使用。
- 此工作负载服务账号 (
$WORKLOAD_SERVICEACCOUNT
) 将具有以下角色: confidentialcomputing.workloadUser
,用于获取证明令牌logging.logWriter
:用于将日志写入 Cloud Logging。objectViewer
,用于从$PRIMUS_INPUT_STORAGE_BUCKET
Cloud Storage 存储分区中读取数据。objectAdmin
:用于将工作负载结果写入$SECUNDUS_RESULT_STORAGE_BUCKET
Cloud Storage 存储分区。
$ ./create_workload_serviceaccount.sh
创建工作负载
在此步骤中,您将创建一个工作负载 Docker 映像。此 Codelab 中使用的工作负载是基于 CLI 的简单 Go 应用,用于统计参数中提供的地理位置的客户数量(来自 Primus 银行客户数据)。运行以下脚本以创建将执行以下步骤的工作负载:
- 创建 Secundus 银行所有的 Artifact Registry(
$SECUNDUS_ARTIFACT_REGISTRY
)。 - 使用必需的资源名称更新工作负载代码。点击此处,了解此 Codelab 使用的工作负载代码。
- 构建 Go 二进制文件并创建 Dockerfile,用于构建工作负载代码的 Docker 映像。此处是用于此 Codelab 的 Dockerfile。
- 构建 Docker 映像并将其发布到 Secundus 银行所有的 Artifact Registry (
$SECUNDUS_ARTIFACT_REGISTRY
)。 - 向“
$WORKLOAD_SERVICEACCOUNT
”授予“$SECUNDUS_ARTIFACT_REGISTRY
”的读取权限。要让工作负载容器从 Artifact Registry 中拉取工作负载 Docker 映像,必须执行此操作。
$ ./create_workload.sh
为工作负载签名
我们将使用 Cosign 对工作负载映像进行签名。协同签名将默认将签名存储在与其签名的映像相同的代码库中。如需为签名指定其他代码库,您可以设置 COSIGN_REPOSITORY
环境变量。
在这里,我们将以 Artifact Registry 作为示例。您还可以根据您的偏好选择其他基于 OCI 的注册表,例如 Docker Hub、AWS CodeArtifact。
- 创建 Artifact Registry Docker 代码库。
$ gcloud config set project $PRIMUS_PROJECT_ID
$ gcloud artifacts repositories create $PRIMUS_COSIGN_REPOSITORY \
--repository-format=docker --location=us
- 在 KMS 下创建密钥环和密钥,以便为工作负载映像签名。
$ gcloud config set project $PRIMUS_PROJECT_ID
$ gcloud kms keyrings create $PRIMUS_SIGNING_KEYRING \
--location=global
$ gcloud kms keys create $PRIMUS_SIGNING_KEY \
--keyring=$PRIMUS_SIGNING_KEYRING \
--purpose=asymmetric-signing \
--default-algorithm=ec-sign-p256-sha256
--location=us
- 对于 Artifact Registry,需要使用完整的映像名称,例如
$LOCATION/$PROJECT/$REPOSITORY/$IMAGE_NAME
。您可以将任何容器映像上传到存储库以进行签名存储。
$ export COSIGN_REPOSITORY=us-docker.pkg.dev/${PRIMUS_PROJECT_ID}/${PRIMUS_COSIGN_REPOSITORY}/demo
- 将
$PRIMUS_COSIGN_REPOSITORY
代码库的 Viewer 角色授予$WORKLOAD_SERVICEACCOUNT
服务账号。这样一来,机密空间便可发现上传到$PRIMUS_COSIGN_REPOSITORY
的任何容器映像签名。
$ gcloud artifacts repositories add-iam-policy-binding ${PRIMUS_COSIGN_REPOSITORY} \
--project=${PRIMUS_PROJECT_ID} --role='roles/viewer' --location=us \
--member="serviceAccount:${WORKLOAD_SERVICEACCOUNT}@${SECUNDUS_PROJECT_ID}.iam.gserviceaccount.com"
Cosign 是一款强大的工具,具有多种签名功能。对于我们的用例,我们只要求 Cosign 使用密钥对签名。此已签名的容器映像功能不支持协同签名无密钥签名。
使用密钥对签名时,有两个选项:
- 使用 Cosign 生成的本地密钥对签名。
- 使用存储在其他位置(例如 KMS 中)的密钥对签名。
- 如果您没有密钥对,请在 Cosign 中生成一个。如需了解详情,请参阅使用自行管理的密钥签名。
// Set Application Default Credentials.
$ gcloud auth application-default login
// Generate keys using a KMS provider.
$ cosign generate-key-pair --kms <provider>://<key>
// Generate keys using Cosign.
$ cosign generate-key-pair
在上述命令中,替换 <provider>://<key>使用gcpkms://projects/$PRIMUS_PROJECT_ID/locations/global/keyRings/$PRIMUS_SIGNING_KEYRING/cryptoKeys/$PRIMUS_SIGNING_KEY/cryptoKeyVersions/$PRIMUS_SIGNING_KEYVERSION
- <provider>:是指您正在使用的 KMS 解决方案
- <key>: 指 KMS 中的密钥路径
- 检索公钥以进行验证。
// For KMS providers.
$ cosign public-key --key <some provider>://<some key> > pub.pem
// For local key pair signing.
$ cosign public-key --key cosign.key > pub.pem
- 使用 Cosign 为工作负载签名。对公钥执行未填充的 base64 编码
$ PUB=$(cat pub.pem | openssl base64)
// Remove spaces and trailing "=" signs.
$ PUB=$(echo $PUB | tr -d '[:space:]' | sed 's/[=]*$//')
- 使用 Cosign 对工作负载进行签名,并附上导出的公钥和签名算法。
$ IMAGE_REFERENCE=us-docker.pkg.dev/$SECUNDUS_PROJECT_ID/$SECUNDUS_ARTIFACT_REPOSITORY/$WORKLOAD_IMAGE_NAME:$WORKLOAD_IMAGE_TAG
// Sign with KMS support.
$ cosign sign --key <some provider>://<some key> $IMAGE_REFERENCE \
-a dev.cosignproject.cosign/sigalg=ECDSA_P256_SHA256 \
-a dev.cosignproject.cosign/pub=$PUB
// Sign with a local key pair.
$ cosign sign --key cosign.key $IMAGE_REFERENCE \
-a dev.cosignproject.cosign/sigalg=ECDSA_P256_SHA256 \
-a dev.cosignproject.cosign/pub=$PUB
--key
[必需] 指定要使用的签名密钥。在引用由 KMS 提供方管理的密钥时,请遵循 Sigstore KMS 支持中提供的特定 URI 格式。引用 Cosign 生成的密钥时,请改用 cosign.key。$IMAGE_REFERENCE
[必需] 指定要签名的容器映像。IMAGE_REFERENCE
的格式可以通过标记或映像摘要来标识。例如:us-docker.pkg.dev/$SECUNDUS_PROJECT_ID/secundus-workloads/workload-container:latest or us-docker.pkg.dev/$SECUNDUS_PROJECT_ID/secundus-workloads/workload-container
[IMAGE-digest]
- -a [必需] 指定附加到签名负载的注释。对于 Encrypt Space 签名的容器映像,需要将公钥和签名算法附加到签名载荷。
dev.cosignproject.cosign/sigalg
ONLY 接受三个值:- RSASSA_PSS_SHA256:使用 SHA256 摘要进行 PSS 填充的 RSASSA 算法。
- RSASSA_PKCS1V15_SHA256:使用 SHA256 摘要通过 PKCS#1 v1.5 填充的 RSASSA 算法。
- ECDSA_P256_SHA256:具有 SHA256 摘要的 P-256 曲线上的 ECDSA。这也是协同签名生成的密钥对的默认签名算法。
- 将签名上传到 Docker 代码库
协同签名会自动将签名上传到指定的 COSIGN_REPOSITORY.
4. 授权和运行工作负载
为工作负载授权
在此步骤中,我们将在工作负载身份池 ($PRIMUS_WORKLOAD_IDENTITY_POOL
) 下设置工作负载身份提供方。系统为工作负载身份配置了属性条件,如下所示。其中一个条件是根据签名公钥的指纹验证工作负载映像签名的指纹。使用该属性条件时,当 Secundus Bank 发布新的工作负载映像时,Primus Bank 审核工作负载代码并为新的工作负载映像签名,而无需使用映像摘要更新 WIP 政策。
$ gcloud config set project $PRIMUS_PROJECT_ID
$ PUBLIC_KEY_FINGERPRINT=$(openssl pkey -pubin -in pub.pem -outform DER | openssl sha256 | cut -d' ' -f2)
$ gcloud iam workload-identity-pools providers create-oidc ${PRIMUS_WIP_PROVIDER} \
--location="global" \
--workload-identity-pool="${PRIMUS_WORKLOAD_IDENTITY_POOL}" \
--issuer-uri="https://confidentialcomputing.googleapis.com/" \
--allowed-audiences="https://sts.googleapis.com" \
--attribute-mapping="google.subject='assertion.sub'" \
--attribute-condition="assertion.swname == 'CONFIDENTIAL_SPACE' &&
'STABLE' in assertion.submods.confidential_space.support_attributes
&& '${WORKLOAD_SERVICEACCOUNT}@${SECUNDUS_PROJECT_ID}.iam.gserviceaccount.com' in
assertion.google_service_accounts
&& ['ECDSA_P256_SHA256:${PUBLIC_KEY_FINGERPRINT}']
.exists(fingerprint, fingerprint in assertion.submods.container.image_signatures.map(sig,sig.signature_algorithm+':'+sig.key_id))"
运行工作负载
在此步骤中,我们将在机密虚拟机上运行工作负载。系统使用元数据标记传递必需的 TEE 参数。工作负载容器的参数使用“tee-cmd
”传递部分。对工作负载进行编码,以将其结果发布到 $SECUNDUS_RESULT_STORAGE_BUCKET
。
$ gcloud config set project $SECUNDUS_PROJECT_ID
$ gcloud compute instances create signed-container-vm \
--confidential-compute-type=SEV \
--shielded-secure-boot \
--maintenance-policy=TERMINATE \
--scopes=cloud-platform --zone=us-west1-b \
--image-project=confidential-space-images \
--image-family=confidential-space \ --service-account=${WORKLOAD_SERVICEACCOUNT}@${SECUNDUS_PROJECT_ID}.iam.gserviceaccount.com \
--metadata "^~^tee-image-reference=us-docker.pkg.dev/${SECUNDUS_PROJECT_ID}/${SECUNDUS_ARTIFACT_REPOSITORY}/${WORKLOAD_IMAGE_NAME}:${WORKLOAD_IMAGE_TAG}~tee-restart-policy=Never~tee-cmd="[\"count-location\",\"Seattle\",\"gs://${SECUNDUS_RESULT_STORAGE_BUCKET}/seattle-result\"]"~tee-signed-image-repos=us-docker.pkg.dev/${PRIMUS_PROJECT_ID}/${PRIMUS_COSIGN_REPOSITORY}/demo"
查看结果
在 Secundus 项目中,查看工作负载的结果。
$ gcloud config set project $SECUNDUS_PROJECT_ID
$ gsutil cat gs://$SECUNDUS_RESULT_STORAGE_BUCKET/seattle-result
结果应该为 3
,因为 primus_customer_list.csv
文件中列出了来自西雅图的人数!
5. 清理
此处是一个脚本,可用于清理我们在此 Codelab 中创建的资源。在此次清理过程中,系统将删除以下资源:
- Primus 银行的输入存储分区 (
$PRIMUS_INPUT_STORAGE_BUCKET
)。 - Primus 银行服务账号 (
$PRIMUS_SERVICEACCOUNT
)。 - 包含映像签名的 Primus Bank 工件注册表 (
$PRIMUS_COSIGN_REPOSITORY
)。 - Primus Bank 工作负载身份池 (
$PRIMUS_WORKLOAD_IDENTITY_POOL
)。 - Secundus Bank 的工作负载服务账号 (
$WORKLOAD_SERVICEACCOUNT
)。 - 工作负载计算实例。
- Secundus Bank 的结果存储分区 (
$SECUNDUS_RESULT_STORAGE_BUCKET
)。 - Secundus Bank (
$SECUNDUS_ARTIFACT_REGISTRY
) 的 Artifact Registry。
// run the clean up script to delete the resources created as part of this codelab.
$ ./cleanup.sh
如果已完成探索,请考虑删除您的项目。
- 转到 Cloud Platform Console
- 选择要关停的项目,然后点击“删除”顶部:这会安排删除项目
恭喜
恭喜,您已成功完成此 Codelab!
您学习了如何利用已签名的容器映像功能来提高机密空间的易用性。
后续操作
查看一些类似的 Codelab...