1. 概览
本 Codelab 基于 Confidential Space Codelab 构建而成。签名容器映像支持提供了一种使用经过证明的公钥对容器进行身份验证的选项,而无需在工作负载身份池 (WIP) 政策中指定映像摘要。
Confidential Space 中对已签名容器映像的支持有何变化:
使用体验更出色:随着已签名容器映像功能的推出,我们现在可以为授权映像的协作者/审核员从工作负载映像摘要方法改用容器签名方法。
- 直接使用映像摘要时,资源所有者每次授权新映像时都必须使用映像摘要更新其政策。通过使用映像签名,该政策包含一个公钥指纹,其对应的私钥归协作者/审核员所有,用于对受审核的映像签名。
- 对于某些安全模型,引用可信的图片签名密钥比更新新图片摘要值列表更为方便。
不会降低安全性:与之前的映像摘要方法相比,这种容器签名方法不会降低安全性,因为信任边界保持不变。在容器签名方法中,资源所有者通过在 WIP 政策中指定可信公钥指纹来授权验证密钥,授权检查由 Attestation Verifier Service 和 WIP 执行;Attestation Verifier Service 会验证签名是否与正在运行的工作负载相关联,而 WIP 政策会检查服务断言的公钥是否已获得政策授权。
强大的安全性:使用容器映像签名可让用户将一定程度的信任委托给映像签名者。通过在认证政策中指定可信签名者的公钥指纹,资源所有者可以授权该签名者对哪些容器映像符合政策做出认可。证明验证器服务会验证签名是否与正在运行的工作负载相关联,而政策会检查创建签名的公钥是否已获得政策授权。通过这种方式,映像签名提供的额外间接层可确保 Confidential Space 的强大安全保障。
这两种方法之间的唯一区别在于,后一种方法会使用额外的间接层,在该层中,工作负载映像使用签名密钥进行授权。这不会引入任何新的安全漏洞,因为信任边界保持不变。
学习内容
在此 Codelab 中,您将学习如何使用容器映像签名授权对受保护资源的访问权限:
- 如何使用
cosign
对经过审核的容器映像进行签名 - 如何将容器映像签名上传到 OCI 注册表以进行签名发现和存储
- 如何配置运行 Confidential Space 所需的云资源
- 如何在支持签名容器映像的 Confidential Space 中运行工作负载
此 Codelab 将向您展示如何使用机密空间远程证明由 Google Compute Engine 上运行的受信任密钥签名的容器映像。
所需条件
- 完成 Confidential Space Codelab
- 一个 Google Cloud Platform 项目
- 一个浏览器,例如 Chrome 或 Firefox
- 熟悉标准的 Linux 文本编辑器,例如 Vim、Emacs 或 Nano
- Sigstore 联合签名基础知识
- 具备 Google Compute Engine(代码库)、机密虚拟机、容器和远程代码库的基础知识
- Cloud KMS 基本知识(Codelab)
- 服务账号、工作负载身份联合和属性条件的基础知识。
- Artifact Registry 基础知识
- 数字签名基础知识
使用已签名容器映像的 Confidential Space 中涉及的角色
在此 Codelab 中,Primus Bank 将充当审核员和资源所有者,负责以下事项:
- 使用示例数据设置所需资源。
- 审核工作负载代码。
- 使用
cosign
对工作负载映像进行签名。 - 将签名上传到代码库。
- 配置 WIP 政策以保护客户数据。
Secundus Bank 将成为工作负载作者和运维者,并负责:
- 设置存储结果所需的资源。
- 编写工作负载代码。
- 发布工作负载映像。
- 在 Confidential Space 中运行工作负载,并支持签名容器映像。
Secundus 银行将开发并发布一个工作负载,用于查询存储在 Primus 银行拥有的云端存储分区中的客户数据。Primus Bank 将审核工作负载、对容器映像进行签名,并配置 WIP 政策,以允许已获批准的工作负载访问其数据。此工作负载执行结果将存储在 Secundus 银行拥有的 Cloud Storage 存储分区中。
Confidential Space 设置涉及的资源
本 Codelab 引用了多个变量,您应将这些变量设置为适合您的 GCP 项目的值。本 Codelab 中的命令假定这些变量已设置。(例如,export PRIMUS_INPUT_STORAGE_BUCKET='my-input-bucket'
可用于设置 Primus 银行的输入存储分区的名称。)如果尚未设置 resource-names 的变量,系统会根据 GCP 项目 ID 生成该变量。
在 Primus 项目中配置以下内容:
$PRIMUS_INPUT_STORAGE_BUCKET
:存储客户数据文件的存储分区。$PRIMUS_WORKLOAD_IDENTITY_POOL
:用于验证声明的工作负载身份池 (WIP)。$PRIMUS_WIP_PROVIDER
:Workload Identity 池提供程序,其中包含要用于由认证验证服务签名的令牌的授权条件。$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
,并创建一个用于查询这些数据的工作负载。
现有工作流
当您在 Confidential Space 中运行工作负载时,系统会使用配置的资源执行以下流程:
- 工作负载从 WIP 请求
$PRIMUS_SERVICEACCOUNT
的通用 Google 访问令牌。它提供包含工作负载和环境声明的 Attestation Verifier 服务令牌。 - 如果证明验证器服务令牌中的工作负载衡量声明与 WIP 中的属性条件匹配,则会返回
$PRIMUS_SERVICEACCOUNT.
的访问令牌 - 该工作负载使用与
$PRIMUS_SERVICEACCOUNT
关联的服务账号访问令牌来访问$PRIMUS_INPUT_STORAGE_BUCKET
存储分区中的客户数据。 - 工作负载会对这些数据执行操作。
- 工作负载使用
$WORKLOAD_SERVICEACCOUNT
服务账号将该操作的结果写入$SECUNDUS_RESULT_STORAGE_BUCKET
存储分区。
支持签名容器的新工作流
已签名容器支持将集成到现有工作流中,如下所示。当您在支持签名容器映像的 Confidential Space 中运行工作负载时,系统会使用配置的资源执行以下流程:
- Confidential Space 会发现与当前正在运行的工作负载映像相关的所有容器签名,并将这些签名发送给证明验证程序。认证验证程序会验证签名,并在认证声明中包含所有有效的签名。
- 工作负载从 WIP 请求
$PRIMUS_SERVICEACCOUNT
的通用 Google 访问令牌。它提供包含工作负载和环境声明的 Attestation Verifier 服务令牌。 - 如果 Attestation Verifier 服务令牌中的容器签名声明与 WIP 中的属性条件匹配,则会返回
$PRIMUS_SERVICEACCOUNT
的访问令牌。 - 该工作负载使用与
$PRIMUS_SERVICEACCOUNT
关联的服务账号访问令牌来访问$PRIMUS_INPUT_STORAGE_BUCKET
存储分区中的客户数据。 - 工作负载会对这些数据执行操作。
- 工作负载使用
$WORKLOAD_SERVICEACCOUNT
将该操作的结果写入$SECUNDUS_RESULT_STORAGE_BUCKET
存储分区。
2. 设置 Cloud 资源
在 Confidential Space 设置过程中,您需要先在 Primus 银行和 Secundus 银行的 GCP 项目下创建所需的云资源。以下是本 Codelab 中新增的资源:
在 Primus 项目中:
- 用于在审核代码后为 Secundus 工作负载签名的 KMS 签名密钥。
- 用于存储 Cosign 签名的 Artifact Registry 仓库。
Secundus 项目中没有任何新资源。设置完这些资源后,您需要为工作负载创建一个具有所需角色和权限的服务账号。然后,您将创建工作负载映像,审核员 Primus 银行将对工作负载映像进行签名。然后,工作负载将由数据协作者(在此 Codelab 中为 Primus 银行)授权,工作负载操作者(在本例中为 Secundus 银行)将运行工作负载。
在 Confidential Space 设置过程中,您需要在 Primus 和 Secundus GCP 项目中创建所需的云资源。
准备工作
- 使用以下命令克隆 此代码库,以获取此 Codelab 中所需的脚本。
git clone https://github.com/GoogleCloudPlatform/confidential-space
- 更改此 Codelab 的目录。
cd confidential-space/codelabs/signed_container_codelab/scripts
- 确保您已按如下所示设置所需的项目。
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
- 按照此处的说明安装 cosign。
设置 Primus 银行资源
在此步骤中,您将为 Primus 银行设置所需的云资源。运行以下脚本,为 Primus 银行设置资源。在执行这些步骤时,系统会创建以下资源:
- 用于存储 Primus 银行加密客户数据文件的 Cloud Storage 存储分区 (
$PRIMUS_INPUT_STORAGE_BUCKET
)。 - 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 银行拥有的工件注册表(
$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=${PRIMUS_PROJECT_REPOSITORY_REGION}
- 在 KMS 下创建密钥环和密钥,以便为工作负载映像签名。
gcloud config set project $PRIMUS_PROJECT_ID
gcloud kms keyrings create $PRIMUS_SIGNING_KEYRING \
--location=${PRIMUS_PROJECT_LOCATION}
gcloud kms keys create $PRIMUS_SIGNING_KEY \
--keyring=$PRIMUS_SIGNING_KEYRING \
--purpose=asymmetric-signing \
--default-algorithm=ec-sign-p256-sha256 \
--location=${PRIMUS_PROJECT_LOCATION}
- 对于 Artifact Registry,应使用完整的映像名称,例如
$LOCATION/$PROJECT/$REPOSITORY/$IMAGE_NAME
。您可以将任何容器映像上传到代码库以存储签名。
export COSIGN_REPOSITORY=us-docker.pkg.dev/${PRIMUS_PROJECT_ID}/${PRIMUS_COSIGN_REPOSITORY}/demo
- 向
$WORKLOAD_SERVICEACCOUNT
服务账号授予$PRIMUS_COSIGN_REPOSITORY
代码库的“查看者”角色。这样,Confidential Space 就可以发现上传到$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 生成的本地密钥对进行签名。
- 使用存储在其他位置(例如 KMS 中)的密钥对进行签名。
- 如果您还没有密钥对,请在 Cosign 中生成一个。如需了解详情,请参阅使用自行管理的密钥进行签名。在这里,我们指定了生成密钥对和对工作负载进行签名的两种方法(本地和使用 KMS 提供方),请按照其中一种方法对工作负载容器进行签名。
// 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 [必需] 用于指定附加到签名载荷的注解。对于 Confidential Space 签名的容器映像,必须将公钥和签名算法附加到签名载荷。
dev.cosignproject.cosign/sigalg
仅接受以下三个值:- RSASSA_PSS_SHA256:使用 PSS 填充的 RSASSA 算法,带有 SHA256 摘要。
- RSASSA_PKCS1V15_SHA256:使用 PKCS#1 v1.5 填充和 SHA256 摘要的 RSASSA 算法。
- ECDSA_P256_SHA256:具有 SHA256 摘要的 P-256 曲线上的 ECDSA。这也是 Cosign 生成的密钥对的默认签名算法。
- 将签名上传到 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 compute instances create ${WORKLOAD_VM} \
--confidential-compute-type=SEV \
--shielded-secure-boot \
--maintenance-policy=MIGRATE \
--scopes=cloud-platform \
--zone=${SECUNDUS_PROJECT_ZONE} \
--project=${SECUNDUS_PROJECT_ID} \
--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
文件中列出了来自西雅图的 3
位用户!
5. 清理
此处提供了一个脚本,可用于清理我们在此 Codelab 中创建的资源。在此清理过程中,系统将删除以下资源:
- Primus 银行的输入存储分区 (
$PRIMUS_INPUT_STORAGE_BUCKET
)。 - Primus 银行服务账号 (
$PRIMUS_SERVICEACCOUNT
)。 - 存储映像签名 (
$PRIMUS_COSIGN_REPOSITORY
) 的 Primus Bank 工件注册库。 - Primus Bank 工作负载身份池 (
$PRIMUS_WORKLOAD_IDENTITY_POOL
)。 - Secundus Bank 的工作负载服务账号 (
$WORKLOAD_SERVICEACCOUNT
)。 - 工作负载计算实例。
- Secundus Bank 的结果存储分区 (
$SECUNDUS_RESULT_STORAGE_BUCKET
)。 - Secundus Bank 的 Artifact Registry (
$SECUNDUS_ARTIFACT_REGISTRY
)。 - Secundus Bank 的工作负载虚拟机 (
$WORKLOAD_VM
)。
./cleanup.sh
如果您已完成探索,请考虑删除您的项目。
- 前往 Cloud Platform 控制台
- 选择要关停的项目,然后点击顶部的“删除”:这会安排删除该项目
恭喜
恭喜,您已成功完成此 Codelab!
您了解了如何利用签名容器映像功能来提高 Confidential Space 的可用性。
后续操作
查看下列类似 Codelab…