1. 概览
Confidential Space 可为多方协作提供安全的环境。此 Codelab 演示了如何使用 Confidential Space 来保护敏感的知识产权,例如机器学习模型。
在此 Codelab 中,您将使用 Confidential Space 使一家公司能够安全地与另一家想要使用其专有机器学习模型的公司共享该模型。具体来说,Primus 公司有一个机器学习模型,该模型仅会发布到在 Confidential Space 中运行的工作负载,从而使 Primus 能够完全控制其知识产权。Secundus 公司将成为工作负载操作员,并在 Confidential Space 中运行机器学习工作负载。Secundus 将加载此模型,并使用 Secundus 拥有的样本数据运行推理。
在此示例中,Primus 是工作负载作者,负责编写工作负载代码,也是一位希望保护其知识产权免受不受信任的工作负载操作员 Secundus 侵害的协作者。Secundus 是机器学习工作负载的工作负载运算符。

学习内容
- 如何配置一个环境,使一方可以与另一方分享其专有的机器学习模型,而不会失去对其知识产权的控制权。
所需条件
- 一个 Google Cloud Platform 项目
- 具备 Google Compute Engine ( Codelab)、机密虚拟机、容器和远程代码库的基础知识
- 具备服务账号、工作负载身份联合和属性条件方面的基本知识。
参与 Confidential Space 设置的角色
在此 Codelab 中,Primus 公司将成为资源所有者和工作负载作者,负责以下事项:
- 使用机器学习模型设置所需的云资源
- 编写工作负载代码
- 发布工作负载映像
- 配置工作负载身份池政策以防范不受信任的运维人员窃取机器学习模型
Secundus Company 将成为运营商,并负责:
- 设置所需的云资源,以存储工作负载使用的示例图片和结果
- 在 Confidential Space 中使用 Primus 提供的模型运行机器学习工作负载
Confidential Space 的运作方式
在 Confidential Space 中运行工作负载时,系统会使用配置的资源执行以下流程:
- 工作负载从工作负载身份池请求
$PRIMUS_SERVICEACCOUNT的一般 Google 访问令牌。它提供包含工作负载和环境声明的证明验证器服务令牌。 - 如果 Attestation Verifier 服务令牌中的工作负载衡量声明与 WIP 中的属性条件相匹配,则返回
$PRIMUS_SERVICEACCOUNT.的访问令牌 - 工作负载使用与
$PRIMUS_SERVICEACCOUNT关联的服务账号访问令牌来访问存储在$PRIMUS_INPUT_STORAGE_BUCKET存储分区中的机器学习模型。 - 工作负载对 Secundus 拥有的数据执行操作,并且该工作负载由 Secundus 在其项目中操作和运行。
- 工作负载使用
$WORKLOAD_SERVICEACCOUNT服务账号将相应操作的结果写入$SECUNDUS_RESULT_STORAGE_BUCKET存储分区。
2. 设置 Cloud 资源
准备工作
- 使用以下命令克隆 此代码库,以获取此 Codelab 中使用的必需脚本。
git clone https://github.com/GoogleCloudPlatform/confidential-space.git
- 更改此 Codelab 的目录。
cd confidential-space/codelabs/ml_model_protection/scripts
- 确保您已设置必需的项目环境变量,如下所示。如需详细了解如何设置 GCP 项目,请参阅 此 Codelab。您可以参阅这篇文章,详细了解如何检索项目 ID,以及项目 ID 与项目名称和项目编号有何不同。
export PRIMUS_PROJECT_ID=<GCP project id of Primus>
export SECUNDUS_PROJECT_ID=<GCP project id of Secundus>
- 为您的项目启用结算功能。
- 为这两个项目启用 Confidential Computing API 和以下 API。
gcloud services enable \
cloudapis.googleapis.com \
cloudresourcemanager.googleapis.com \
cloudshell.googleapis.com \
container.googleapis.com \
containerregistry.googleapis.com \
iam.googleapis.com \
confidentialcomputing.googleapis.com
- 使用以下命令为上述资源名称的变量分配值。借助这些变量,您可以根据需要自定义资源名称,还可以使用已创建的现有资源。(例如
export PRIMUS_INPUT_STORAGE_BUCKET='my-input-bucket')
- 您可以使用 Primus 项目中的现有云资源名称设置以下变量。如果设置了该变量,则会使用 Primus 项目中相应的现有云资源。如果未设置该变量,系统会根据项目名称生成云资源名称,并使用该名称创建新的云资源。以下是资源名称支持的变量:
| 用于存储 Primus 的机器学习模型的存储分区。 |
| 用于验证声明的 Primus 工作负载身份池 (WIP)。 |
| Primus 的工作负载身份池提供方,其中包含用于由证明验证器服务签名的令牌的授权条件。 |
| Primus 服务账号, |
| 工作负载 Docker 映像将推送到的制品库。 |
- 您可以使用 Secundus 项目中的现有云资源名称设置以下变量。如果设置了该变量,则会使用 Secundus 项目中相应的现有云资源。如果未设置该变量,系统会根据项目名称生成云资源名称,并使用该名称创建新的云资源。以下是资源名称支持的变量:
| 存储 Secundus 希望使用 Primus 提供的模型进行分类的示例图片的存储分区。 |
| 用于存储工作负载结果的存储分区。 |
| 工作负载容器映像的名称。 |
| 工作负载容器映像的标记。 |
| 有权访问运行工作负载的保密虚拟机的服务账号。 |
- 您需要为这两个项目获取特定权限,并且可以参阅本指南,了解如何使用 GCP 控制台授予 IAM 角色:
- 对于
$PRIMUS_PROJECT_ID,您需要 Storage Admin、Artifact Registry Administrator、Service Account Admin、IAM Workload Identity Pool Admin。 - 对于
$SECUNDUS_PROJECT_ID,您需要 Compute Admin、Storage Admin、Service Account Admin、IAM Workload Identity Pool Admin、Security Admin(可选)。 - 运行以下脚本,根据您的项目 ID 为资源名称设置剩余的变量名称值。
source config_env.sh
设置 Primus Company 资源
在此步骤中,您将为 Primus 设置所需的云资源。运行以下 脚本以设置 Primus 的资源。在执行脚本时,系统会创建以下资源:
- 用于存储 Primus 机器学习模型的 Cloud Storage 存储分区 (
$PRIMUS_INPUT_STORAGE_BUCKET)。 - 工作负载身份池 (
$PRIMUS_WORKLOAD_IDENTITY_POOL),用于根据其提供方下配置的属性条件验证声明。 - 附加到上述工作负载身份池 (
$PRIMUS_WORKLOAD_IDENTITY_POOL) 的服务账号 ($PRIMUS_SERVICEACCOUNT),具有从 Cloud Storage 存储分区读取数据(使用objectViewer角色)以及将此服务账号连接到工作负载身份池(使用roles/iam.workloadIdentityUser角色)的 IAM 访问权限。
在此云资源设置过程中,我们将使用 TensorFlow 模型。我们可以将包含模型架构、权重和训练配置的整个模型保存到 ZIP 归档文件中。在此 Codelab 中,我们将使用在 ImageNet 数据集上训练的 MobileNet V1 模型,该模型可在此处找到。
./setup_primus_company_resources.sh
上述脚本将设置云资源,我们现在将下载模型并将其发布到脚本创建的 Cloud Storage 存储分区。
- 点击此处下载预训练模型。
- 下载完成后,将下载的 tar 文件重命名为 model.tar.gz。
- 使用以下命令,从包含 model.tar.gz 文件的目录中将 model.tar.gz 文件发布到 Cloud Storage 存储分区。
gsutil cp model.tar.gz gs://${PRIMUS_INPUT_STORAGE_BUCKET}/
设置 Secundus Company 资源
在此步骤中,您将为 Secundus 设置所需的云资源。运行以下脚本,为 Secundus 设置资源。在这些步骤中,您将创建以下资源:
- 用于存储样本图片的 Cloud Storage 存储分区 (
$SECUNDUS_INPUT_STORAGE_BUCKET),以便 Secundus 运行推理。 - 用于存储 Secundus 执行机器学习工作负载的结果的 Cloud Storage 存储分区 (
$SECUNDUS_RESULT_STORAGE_BUCKET)。
此 Codelab 提供了一些示例图片,您可点击此处查看。
./setup_secundus_company_resources.sh
3. 创建工作负载
创建工作负载服务账号
现在,您将为工作负载创建一个具有所需角色和权限的服务账号。运行以下脚本,在 Secundus 项目中创建工作负载服务账号。此服务账号将由运行 ML 工作负载的虚拟机使用。
此工作负载服务账号 ($WORKLOAD_SERVICEACCOUNT) 将具有以下角色:
confidentialcomputing.workloadUser以获取证明令牌logging.logWriter以将日志写入 Cloud Logging。objectViewer以从$SECUNDUS_INPUT_STORAGE_BUCKETCloud Storage 存储分区读取数据。objectUser将工作负载结果写入$SECUNDUS_RESULT_STORAGE_BUCKETCloud Storage 存储分区。
./create_workload_service_account.sh
创建工作负载
在此步骤中,您将创建工作负载 Docker 映像。工作负载将由 Primus 编写。此 Codelab 中使用的工作负载是机器学习 Python 代码,该代码可访问存储在 Primus 存储分区中的机器学习模型,并使用存储在存储分区中的示例图片运行推理。
存储在 Primus 存储分区中的机器学习模型只能由满足所需属性条件的工作负载访问。下一部分将更详细地介绍这些属性条件,其中涉及授权工作负载。
以下是将在本 Codelab 中创建和使用的工作负载的 run_inference() 方法。您可以点击此处查看整个工作负载代码。
def run_inference(image_path, model):
try:
# Read and preprocess the image
image = tf.image.decode_image(tf.io.read_file(image_path), channels=3)
image = tf.image.resize(image, (128, 128))
image = tf.image.convert_image_dtype(image, tf.float32)
image = tf.expand_dims(image, axis=0)
# Get predictions from the model
predictions = model(image)
predicted_class = np.argmax(predictions)
top_k = 5
top_indices = np.argsort(predictions[0])[-top_k:][::-1]
# Convert top_indices to a TensorFlow tensor
top_indices_tensor = tf.convert_to_tensor(top_indices, dtype=tf.int32)
# Use TensorFlow tensor for indexing
top_scores = tf.gather(predictions[0], top_indices_tensor)
return {
"predicted_class": int(predicted_class),
"top_k_predictions": [
{"class_index": int(idx), "score": float(score)}
for idx, score in zip(top_indices, top_scores)
],
}
except Exception as e:
return {"error": str(e)}
运行以下脚本以创建工作负载,其中执行以下步骤:
- 创建由 Primus 拥有的 Artifact Registry(
$PRIMUS_ARTIFACT_REGISTRY)。 - 使用所需的资源名称更新工作负载代码。
- 构建 ML 工作负载,并创建 Dockerfile 以构建工作负载代码的 Docker 映像。此处是此 Codelab 使用的 Dockerfile。
- 构建 Docker 映像并将其发布到 Primus 拥有的 Artifact Registry (
$PRIMUS_ARTIFACT_REGISTRY)。 - 为
$PRIMUS_ARTIFACT_REGISTRY授予$WORKLOAD_SERVICEACCOUNT读取权限。这是工作负载容器从 Artifact Registry 中拉取工作负载 Docker 映像所必需的。
./create_workload.sh
此外,工作负载可以进行编码,以确保在加载机器学习模型之前检查其哈希或签名,从而确保加载的是预期版本的模型。此类额外检查的优势在于,它可以确保机器学习模型的完整性。这样一来,当工作负载需要使用不同版本的机器学习模型时,工作负载操作员还需要更新工作负载映像或其参数。
4. 授权并运行工作负载
授权工作负载
Primus 希望授权工作负载基于以下资源的属性访问其机器学习模型:
- 内容:已通过验证的代码
- 地点:安全的环境
- Who:受信任的运营商
Primus 使用工作负载身份联合来根据这些要求强制执行访问政策。借助工作负载身份联合,您可以指定特性条件。这些条件用于限制哪些身份可以使用工作负载身份池 (WIP) 进行身份验证。您可以将证明验证器服务作为工作负载身份池提供方添加到 WIP,以提供测量结果并强制执行政策。
工作负载身份池已在之前作为云资源设置步骤的一部分创建。现在,Primus 将创建一个新的 OIDC 工作负载身份池提供方。指定的 --attribute-condition 授权对工作负载容器的访问权限。它需要:
- 内容:最新
$WORKLOAD_IMAGE_NAME已上传到$PRIMUS_ARTIFACT_REPOSITORY代码库。 - 其中:Confidential Space 可信执行环境在完全受支持的 Confidential Space 虚拟机映像上运行。
- 对象:Secundus
$WORKLOAD_SERVICE_ACCOUNT服务账号。
export WORKLOAD_IMAGE_DIGEST=$(gcloud artifacts docker images describe ${PRIMUS_PROJECT_REPOSITORY_REGION}-docker.pkg.dev/$PRIMUS_PROJECT_ID/$PRIMUS_ARTIFACT_REPOSITORY/$WORKLOAD_IMAGE_NAME:$WORKLOAD_IMAGE_TAG --format="value(image_summary.digest)" --project ${PRIMUS_PROJECT_ID})
gcloud config set project $PRIMUS_PROJECT_ID
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 &&
assertion.submods.container.image_digest == '${WORKLOAD_IMAGE_DIGEST}' &&
assertion.submods.container.image_reference == '${PRIMUS_PROJECT_REPOSITORY_REGION}-docker.pkg.dev/$PRIMUS_PROJECT_ID/$PRIMUS_ARTIFACT_REPOSITORY/$WORKLOAD_IMAGE_NAME:$WORKLOAD_IMAGE_TAG' &&
'$WORKLOAD_SERVICEACCOUNT@$SECUNDUS_PROJECT_ID.iam.gserviceaccount.com' in assertion.google_service_accounts"
运行工作负载
在此步骤中,我们将在 Confidential Space 虚拟机中运行工作负载。必需的 TEE 实参通过 元数据标志传递。工作负载容器的实参通过标志的“tee-cmd”部分传递。工作负载执行结果将发布到 $SECUNDUS_RESULT_STORAGE_BUCKET。
gcloud compute instances create ${WORKLOAD_VM} \
--confidential-compute-type=SEV \
--shielded-secure-boot \
--project=${SECUNDUS_PROJECT_ID} \
--maintenance-policy=MIGRATE \
--scopes=cloud-platform --zone=${SECUNDUS_PROJECT_ZONE} \
--image-project=confidential-space-images \
--image-family=confidential-space \
--service-account=${WORKLOAD_SERVICEACCOUNT}@${SECUNDUS_PROJECT_ID}.iam.gserviceaccount.com \
--metadata ^~^tee-image-reference=${PRIMUS_PROJECT_REPOSITORY_REGION}-docker.pkg.dev/${PRIMUS_PROJECT_ID}/${PRIMUS_ARTIFACT_REPOSITORY}/${WORKLOAD_IMAGE_NAME}:${WORKLOAD_IMAGE_TAG}
查看结果
工作负载成功完成后,机器学习工作负载的结果将发布到 $SECUNDUS_RESULT_STORAGE_BUCKET。
gsutil cat gs://$SECUNDUS_RESULT_STORAGE_BUCKET/result
以下是一些示例,展示了对样本图片进行推理后可能得到的结果:
Image: sample_image_1.jpeg, Response: {'predicted_class': 531, 'top_k_predictions': [{'class_index': 531, 'score': 12.08437442779541}, {'class_index': 812, 'score': 10.269512176513672}, {'class_index': 557, 'score': 9.202644348144531}, {'class_index': 782, 'score': 9.08737564086914}, {'class_index': 828, 'score': 8.912498474121094}]}
Image: sample_image_2.jpeg, Response: {'predicted_class': 905, 'top_k_predictions': [{'class_index': 905, 'score': 9.53619384765625}, {'class_index': 557, 'score': 7.928380966186523}, {'class_index': 783, 'score': 7.70129919052124}, {'class_index': 531, 'score': 7.611623287200928}, {'class_index': 906, 'score': 7.021416187286377}]}
Image: sample_image_3.jpeg, Response: {'predicted_class': 905, 'top_k_predictions': [{'class_index': 905, 'score': 6.09878396987915}, {'class_index': 447, 'score': 5.992854118347168}, {'class_index': 444, 'score': 5.9582319259643555}, {'class_index': 816, 'score': 5.502010345458984}, {'class_index': 796, 'score': 5.450454235076904}]}
对于 Secundus 存储分区中的每个样本图片,您都会在结果中看到一个条目。此条目将包含两项关键信息:
- predicted_class 的指数:这是一个数值指数,表示模型预测的图片所属的类别。
- Top_k_predictions::此属性提供最多 k 个图像预测结果,按可能性从高到低排序。在此 Codelab 中,k 的值设置为 5,但您可以在工作负载代码中调整该值,以获得更多或更少的预测结果。
如需将类索引转换为直观易懂的类名称,请参阅此处提供的标签列表。例如,如果您看到类别指数为 2,则表示它对应于标签列表中的类别标签“tench”。
在此 Codelab 中,我们演示了由 Primus 拥有的模型仅发布到在 TEE 中运行的工作负载。Secundus 在 TEE 中运行机器学习工作负载,并且该工作负载能够使用 Primus 拥有的模型,而 Primus 仍对该模型拥有完全控制权。
运行未经授权的工作负载
Secundus 通过从其自己的未经 Primus 授权的制品仓库中拉取不同的工作负载映像来更改工作负载映像。Primus 的工作负载身份池仅授权了 ${PRIMUS_PROJECT_REPOSITORY_REGION}-docker.pkg.dev/$PRIMUS_PROJECT_ID/$PRIMUS_ARTIFACT_REPOSITORY/$WORKLOAD_IMAGE_NAME:$WORKLOAD_IMAGE_TAG 工作负载映像。
重新运行工作负载
当 Secundus 尝试使用此新工作负载映像运行原始工作负载时,将会失败。如需查看错误,请删除原始结果文件和虚拟机实例,然后尝试再次运行工作负载。
请确保在 Secundus 的制品注册表 (us-docker.pkg.dev/${SECUNDUS_PROJECT_ID}/custom-image/${WORKLOAD_IMAGE_NAME}:${WORKLOAD_IMAGE_TAG}) 下发布了新的 Docker 映像,并且工作负载服务账号 ($WORKLOAD_SERVICEACCOUNT) 已获得制品注册表读取者权限,可以读取此新的工作负载映像。这是为了确保工作负载不会在 Primus 的 WIP 政策拒绝工作负载提供的令牌之前退出。
删除现有结果文件和虚拟机实例
- 将项目设置为
$SECUNDUS_PROJECT_ID项目。
gcloud config set project $SECUNDUS_PROJECT_ID
- 删除结果文件。
gsutil rm gs://$SECUNDUS_RESULT_STORAGE_BUCKET/result
- 删除机密虚拟机实例。
gcloud compute instances delete ${WORKLOAD_VM} --zone=${SECUNDUS_PROJECT_ZONE}
运行未经授权的工作负载:
gcloud compute instances create ${WORKLOAD_VM} \
--confidential-compute-type=SEV \
--shielded-secure-boot \
--maintenance-policy=MIGRATE \
--scopes=cloud-platform --zone=${SECUNDUS_PROJECT_ZONE} \
--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}/custom-image/${WORKLOAD_IMAGE_NAME}:${WORKLOAD_IMAGE_TAG}
查看错误
您会看到错误 (The given credential is rejected by the attribute condition),而不是工作负载的结果。
gsutil cat gs://$SECUNDUS_RESULT_STORAGE_BUCKET/result
5. 清理
点击此处可查看用于清理我们在本 Codelab 中创建的资源的脚本。在此清理过程中,系统将删除以下资源:
- Primus 的输入存储分区 (
$PRIMUS_INPUT_STORAGE_BUCKET). - Primus 服务账号 (
$PRIMUS_SERVICEACCOUNT)。 - Primus 的制品库 (
$PRIMUS_ARTIFACT_REPOSITORY)。 - Primus 工作负载身份池 (
$PRIMUS_WORKLOAD_IDENTITY_POOL)。 - Secundus 的工作负载服务账号 (
$WORKLOAD_SERVICEACCOUNT)。 - Secundus 的输入存储分区 (
$SECUNDUS_INPUT_STORAGE_BUCKET). - 工作负载计算实例。
- Secundus 的结果存储分区 (
$SECUNDUS_RESULT_STORAGE_BUCKET)。
$ ./cleanup.sh
如果您已完成探索,请考虑删除项目。
- 前往 Cloud Platform 控制台
- 选择要关停的项目,然后点击顶部的“删除”:系统会安排删除该项目
后续操作
不妨查看以下类似 Codelab…