1. 概览
本 Codelab 基于 Confidential Space Codelab 构建而成。支持已签名的容器映像,并提供以下选项:使用经过证明的公钥对容器进行身份验证,而不是在工作负载身份池 (WIP) 政策中指定映像摘要。
Confidential Space 中对已签名容器映像的支持有何变化:
改进了易用性:随着已签名容器映像功能的推出,我们现在可以从工作负载映像摘要方法转为容器签名方法,以便协作者/审核员授权映像。
- 如果直接使用映像摘要,资源所有者每次授权新映像时都必须使用映像摘要更新其政策。通过使用映像签名,政策包含一个公钥指纹,其对应的私钥归协作者/审核员所有,用于对审核的映像进行签名。
- 对于某些安全模型,引用可信的映像签名密钥比更新新映像摘要值列表更方便。
无安全性回归:与之前的映像摘要方法相比,此容器签名方法不会带来任何安全性回归,因为信任边界保持不变。在容器签名方法中,资源所有者通过在 WIP 政策中指定可信的公钥指纹来授权验证密钥,并且授权检查由证明验证器服务和 WIP 执行;证明验证器服务验证签名是否与正在运行的工作负载相关联,并且 WIP 政策检查服务断言的公钥是否已获得政策授权。
安全性高:使用容器映像签名可将一定程度的信任委托给映像签名者。通过在证明政策中指定可信签名者的公钥指纹,资源所有者可授权该签名者对符合政策的容器映像进行认可。证明验证器服务会验证签名是否与正在运行的工作负载相关联,并且政策会检查创建签名的公钥是否已获得政策授权。通过这种方式,映像签名提供的间接层可保持 Confidential Space 的强大安全保证。
这两种方法的唯一区别在于,后一种方法使用额外的间接层,通过签名密钥授权工作负载映像。这不会引入任何新的安全漏洞,因为信任边界保持不变。
学习内容
在此 Codelab 中,您将学习如何利用容器映像签名来授权对受保护资源的访问权限:
- 如何使用
cosign对经过审核的容器映像进行签名 - 如何将容器映像签名上传到 OCI 注册表以进行签名发现和存储
- 如何配置运行 Confidential Space 所需的云资源
- 如何在支持已签名容器映像的 Confidential Space 中运行工作负载
此 Codelab 演示了如何使用 Confidential Space 对在 Google Compute Engine 上运行的由可信密钥签名的容器映像进行远程证明。
所需条件
- 完成 Confidential Space Codelab
- 一个 Google Cloud Platform 项目
- 一个浏览器,例如 Chrome 或 Firefox
- 熟悉标准的 Linux 文本编辑器,例如 Vim、Emacs 或 Nano
- 具备 Sigstore cosign 方面的基础知识
- 具备 Google Compute Engine ( Codelab)、机密虚拟机、容器和远程代码库的基础知识
- Cloud KMS 的基本知识(Codelab)
- 具备服务账号、工作负载身份联合和属性条件方面的基本知识。
- Artifact Registry 基础知识
- 数字签名的基础知识
涉及签名容器映像的 Confidential Space 中的角色
在此 Codelab 中,Primus Bank 将作为审核员和资源所有者,负责以下事项:
- 使用示例数据设置所需资源。
- 审核工作负载代码。
- 使用
cosign对工作负载映像进行签名。 - 将签名上传到代码库。
- 配置 WIP 政策以保护客户数据。
Secundus Bank 将是工作负载的创建者和运维人员,负责:
- 设置存储结果所需的资源。
- 编写工作负载代码。
- 发布工作负载映像。
- 在 Confidential Space 中运行工作负载,并支持已签名的容器映像。
Secundus Bank 将开发并发布一个工作负载,用于查询存储在 Cloud Storage 存储分区中且归 Primus Bank 所有的客户数据。Primus Bank 将审核工作负载、签署容器映像,并配置 WIP 政策以允许经过批准的工作负载访问其数据。相应工作负载的执行结果将存储在 Secundus 银行拥有的 Cloud Storage 存储分区中。
Confidential Space 设置中涉及的资源
此 Codelab 引用了许多变量,您应为这些变量设置适合您 GCP 项目的值。此 Codelab 中的命令假定这些变量已设置。(例如,export PRIMUS_INPUT_STORAGE_BUCKET='my-input-bucket' 可用于设置 Primus 银行的输入存储分区的名称。)如果资源名称的变量尚未设置,则会根据 GCP 项目 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 映像的制品注册表。$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 访问令牌。它提供包含工作负载和环境声明的证明验证器服务令牌。 - 如果证明验证器服务令牌中的工作负载衡量声明与 WIP 中的属性条件相匹配,则返回
$PRIMUS_SERVICEACCOUNT.的访问令牌 - 工作负载使用与
$PRIMUS_SERVICEACCOUNT关联的服务账号访问令牌来访问$PRIMUS_INPUT_STORAGE_BUCKET存储分区中的客户数据。 - 工作负载对该数据执行操作。
- 工作负载使用
$WORKLOAD_SERVICEACCOUNT服务账号将相应操作的结果写入$SECUNDUS_RESULT_STORAGE_BUCKET存储分区。
支持已签名容器的新工作流
签名容器支持将集成到现有工作流程中,如下所示。在 Confidential Space 中运行支持已签名容器映像的工作负载时,系统会使用配置的资源执行以下流程:
- Confidential Space 会发现与当前正在运行的工作负载映像相关的任何容器签名,并将这些签名发送给证明验证器。证明验证器会验证签名,并将证明声明中的所有有效签名都纳入其中。
- 工作负载从 WIP 请求
$PRIMUS_SERVICEACCOUNT的一般 Google 访问令牌。它提供包含工作负载和环境声明的证明验证器服务令牌。 - 如果证明验证器服务令牌中的容器签名声明与 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 签名的制品注册库。
Secundus 项目中没有新资源。设置好这些资源后,您将为工作负载创建一个具有所需角色和权限的服务账号。然后,您将创建一个工作负载映像,而审计方 Primus 银行将对该工作负载映像进行签名。然后,数据协作者(在此 Codelab 中为 Primus 银行)将授权工作负载,工作负载运营商(在此 Codelab 中为 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 银行设置资源。在此步骤中,您将创建以下资源:
- 用于存储 Secundus 银行执行工作负载的结果的 Cloud Storage 存储分区 (
$SECUNDUS_RESULT_STORAGE_BUCKET)。
./setup_secundus_bank_resources.sh
3. 创建并签署工作负载
创建工作负载服务账号
现在,您将为工作负载创建一个具有所需角色和权限的服务账号。运行以下脚本,在 Secundus 银行项目中创建工作负载服务账号。此服务账号将由运行工作负载的虚拟机使用。
- 此工作负载服务账号 (
$WORKLOAD_SERVICEACCOUNT) 将具有以下角色: confidentialcomputing.workloadUser以获取证明令牌logging.logWriter以将日志写入 Cloud Logging。objectViewer以从$PRIMUS_INPUT_STORAGE_BUCKETCloud Storage 存储分区读取数据。objectAdmin将工作负载结果写入$SECUNDUS_RESULT_STORAGE_BUCKETCloud 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)。 - 为
$SECUNDUS_ARTIFACT_REGISTRY授予$WORKLOAD_SERVICEACCOUNT读取权限。这是工作负载容器从 Artifact Registry 中拉取工作负载 Docker 映像所必需的。
./create_workload.sh
对工作负载进行签名
我们将使用 Cosign 对工作负载映像进行签名。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 使用密钥对进行签名。此已签名容器映像功能不支持 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 签名会自动将签名上传到指定的 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 Bank 的输入存储分区 (
$PRIMUS_INPUT_STORAGE_BUCKET)。 - Primus bank service-account (
$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)。 - Secundus Bank 的工作负载虚拟机 (
$WORKLOAD_VM)。
./cleanup.sh
如果您已完成探索,请考虑删除项目。
- 前往 Cloud Platform 控制台
- 选择要关停的项目,然后点击顶部的“删除”:系统会安排删除该项目
恭喜
恭喜,您已成功完成此 Codelab!
您已了解如何利用签名容器映像功能来提高 Confidential Space 的易用性。
后续操作
不妨查看以下类似的 Codelab…