Confidential Space を使用して ML モデルと知的財産を保護する

1. 概要

Confidential Space は、複数の関係者間のコラボレーションのための安全な環境を提供します。この Codelab では、Confidential Space を使用して、ML モデルなどの機密性の高い知的財産を保護する方法について説明します。

この Codelab では、Confidential Space を使用して、ある企業が独自の機械学習モデルを、そのモデルを使用したい別の企業と安全に共有できるようにします。具体的には、Company Primus には Confidential Space で実行されるワークロードにのみリリースされる機械学習モデルがあり、Primus は知的財産を完全に制御できます。Company Secundus はワークロード オペレーターとなり、Confidential Space で ML ワークロードを実行します。Secundus はこのモデルを読み込み、Secundus が所有するサンプルデータを使用して推論を実行します。

ここで、Primus はワークロード コードを作成するワークロード作成者であり、信頼できないワークロード オペレーター Secundus から知的財産を保護したい共同編集者です。Secundus は、ML ワークロードのワークロード オペレータです。

5a86c47d935da998.jpeg

学習内容

  • 一方の当事者が知的財産権の制御を失うことなく、独自の ML モデルを別の当事者と共有できる環境を構成する方法。

必要なもの

Confidential Space の設定に関与するロール

この Codelab では、Company Primus がリソース オーナーとワークロード作成者になり、次のことを行います。

  1. ML モデルを使用して必要なクラウド リソースを設定する
  2. ワークロード コードの作成
  3. ワークロード イメージを公開する
  4. 信頼できないオペレーターから ML モデルを保護するように Workload Identity プールポリシーを構成する

Secundus Company は、以下の責任を負う事業者となります。

  1. ワークロードで使用されるサンプル画像と結果を保存するために必要なクラウドリソースを設定する
  2. Primus が提供するモデルを使用して Confidential Space で ML ワークロードを実行する

Confidential Space の仕組み

Confidential Space でワークロードを実行すると、構成されたリソースを使用して次のプロセスが実行されます。

  1. ワークロードは、Workload Identity プールから $PRIMUS_SERVICEACCOUNT の汎用 Google アクセス トークンをリクエストします。ワークロードと環境のクレームを含む証明書検証サービス トークンを提供します。
  2. 構成証明検証サービストークンのワークロード測定値のクレームが WIP の属性条件と一致する場合、$PRIMUS_SERVICEACCOUNT. のアクセス トークンを返します。
  3. ワークロードは、$PRIMUS_SERVICEACCOUNT に関連付けられたサービス アカウントのアクセス トークンを使用して、$PRIMUS_INPUT_STORAGE_BUCKET バケットに保存されている ML モデルにアクセスします。
  4. ワークロードは Secundus が所有するデータに対してオペレーションを実行し、そのワークロードは Secundus のプロジェクトで運用されます。
  5. ワークロードは $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 の取得方法と、プロジェクト名やプロジェクト番号との違いについては、こちらをご覧ください。
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'
  1. Primus プロジェクトの既存のクラウド リソース名を使用して、次の変数を設定できます。変数が設定されている場合、Primus プロジェクトの対応する既存のクラウド リソースが使用されます。変数が設定されていない場合、クラウド リソース名はプロジェクト名から生成され、その名前で新しいクラウド リソースが作成されます。リソース名でサポートされている変数は次のとおりです。

$PRIMUS_INPUT_STORAGE_BUCKET

Primus の ML モデルを保存するバケット。

$PRIMUS_WORKLOAD_IDENTITY_POOL

クレームを検証する Primus の Workload Identity プール(WIP)。

$PRIMUS_WIP_PROVIDER

Attestation Verifier サービスによって署名されたトークンに使用する認可条件を含む、Primus の Workload Identity プール プロバイダ。

$PRIMUS_SERVICE_ACCOUNT

$PRIMUS_WORKLOAD_IDENTITY_POOL が保護されたリソース(この Codelab の ML モデル)にアクセスするために使用する Primus サービス アカウント。このステップでは、$PRIMUS_INPUT_STORAGE_BUCKET バケットに保存されている ML モデルを読み取る権限があります。

$PRIMUS_ARTIFACT_REPOSITORY

ワークロード Docker イメージが push されるアーティファクト リポジトリ。

  1. Secundus プロジェクトの既存のクラウド リソース名を使用して、次の変数を設定できます。変数が設定されている場合、Secundus プロジェクトの対応する既存のクラウド リソースが使用されます。変数が設定されていない場合、クラウド リソース名はプロジェクト名から生成され、その名前で新しいクラウド リソースが作成されます。リソース名でサポートされている変数は次のとおりです。

$SECUNDUS_INPUT_STORAGE_BUCKET

Primus が提供するモデルを使用して Secundus が分類するサンプル画像を保存するバケット。

$SECUNDUS_RESULT_STORAGE_BUCKET

ワークロードの結果を格納するバケット。

$WORKLOAD_IMAGE_NAME

ワークロード コンテナ イメージの名前。

$WORKLOAD_IMAGE_TAG

ワークロード コンテナ イメージのタグ。

$WORKLOAD_SERVICE_ACCOUNT

ワークロードを実行する Confidential VM にアクセスする権限を持つサービス アカウント。

  • これらの 2 つのプロジェクトには特定の権限が必要です。GCP コンソールを使用して IAM ロールを付与する方法については、こちらのガイドをご覧ください。
  • $PRIMUS_PROJECT_ID の場合、Storage 管理者、Artifact Registry 管理者、サービス アカウント管理者、IAM Workload Identity プール管理者が必要です。
  • $SECUNDUS_PROJECT_ID の場合、Compute 管理者、Storage 管理者、サービス アカウント管理者、IAM Workload Identity プール管理者、セキュリティ管理者(省略可)が必要です。
  • 次のスクリプトを実行して、残りの変数名をリソース名のプロジェクト ID に基づく値に設定します。
source config_env.sh

Primus Company のリソースを設定する

このステップでは、Primus に必要なクラウド リソースを設定します。次のスクリプトを実行して、Primus のリソースを設定します。スクリプトの実行の一環として、次のリソースが作成されます。

  • Primus の ML モデルを保存する Cloud Storage バケット($PRIMUS_INPUT_STORAGE_BUCKET)。
  • プロバイダで構成された属性条件に基づいてクレームを検証する Workload Identity プール($PRIMUS_WORKLOAD_IDENTITY_POOL)。
  • 上記の Workload Identity プール($PRIMUS_WORKLOAD_IDENTITY_POOL)にアタッチされたサービス アカウント($PRIMUS_SERVICEACCOUNT)。クラウド ストレージ バケットからデータを読み取る(objectViewer ロールを使用)IAM アクセス権と、このサービス アカウントを Workload Identity プールに接続する(roles/iam.workloadIdentityUser ロールを使用)IAM アクセス権があります。

このクラウド リソースの設定の一環として、TensorFlow モデルを使用します。モデルのアーキテクチャ、重み、トレーニング構成を含むモデル全体を ZIP アーカイブに保存できます。この Codelab では、こちらにある ImageNet データセットでトレーニングされた MobileNet V1 モデルを使用します。

./setup_primus_company_resources.sh

上記のスクリプトはクラウド リソースを設定します。次に、モデルをダウンロードして、スクリプトで作成された Cloud Storage バケットに公開します。

  1. 事前トレーニング済みモデルをこちらからダウンロードします。
  2. ダウンロードが完了したら、ダウンロードした tar ファイルの名前を model.tar.gz に変更します。
  3. model.tar.gz ファイルを含むディレクトリから次のコマンドを使用して、model.tar.gz ファイルを Cloud Storage バケットに公開します。
gsutil cp model.tar.gz gs://${PRIMUS_INPUT_STORAGE_BUCKET}/

Secundus Company のリソースを設定する

このステップでは、Secundus に必要なクラウド リソースを設定します。次のスクリプトを実行して、Secundus のリソースを設定します。これらの手順の一環として、次のリソースが作成されます。

  • Secundus による推論の実行に使用するサンプル画像を保存する Cloud Storage バケット($SECUNDUS_INPUT_STORAGE_BUCKET)。
  • Secundus による ML ワークロード実行の結果を保存する Cloud Storage バケット($SECUNDUS_RESULT_STORAGE_BUCKET)。

この Codelab では、こちらでサンプル画像を利用できます。

./setup_secundus_company_resources.sh

3. ワークロードを作成する

ワークロード サービス アカウントを作成する

次に、必要なロールと権限を持つワークロードのサービス アカウントを作成します。次のスクリプトを実行して、Secundus プロジェクトにワークロード サービス アカウントを作成します。このサービス アカウントは、ML ワークロードを実行する VM で使用されます。

このワークロード サービス アカウント($WORKLOAD_SERVICEACCOUNT)には次のロールが付与されます。

  • confidentialcomputing.workloadUser で証明書トークンを取得する
  • Cloud Logging にログを書き込むための logging.logWriter
  • $SECUNDUS_INPUT_STORAGE_BUCKET Cloud Storage バケットからデータを読み取るための objectViewer
  • objectUser: ワークロードの結果を $SECUNDUS_RESULT_STORAGE_BUCKET Cloud Storage バケットに書き込みます。
./create_workload_service_account.sh

ワークロードを作成する

このステップでは、ワークロード Docker イメージを作成します。ワークロードは Primus によって作成されます。この Codelab で使用するワークロードは、Primus のストレージ バケットに保存されている ML モデルにアクセスし、ストレージ バケットに保存されているサンプル画像を使用して推論を実行する機械学習 Python コードです。

Primus のストレージ バケットに保存されている ML モデルには、必要な属性条件を満たすワークロードのみがアクセスできます。これらの属性条件については、ワークロードの認可に関する次のセクションで詳しく説明します。

この 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 ワークロードをビルドし、ワークロード コードの Docker イメージをビルドするための Dockerfile を作成します。この Codelab で使用する Dockerfile はこちらです。
  • Docker イメージをビルドして、Primus が所有する Artifact Registry($PRIMUS_ARTIFACT_REGISTRY)に公開します。
  • $PRIMUS_ARTIFACT_REGISTRY に対する $WORKLOAD_SERVICEACCOUNT の読み取り権限を付与します。これは、ワークロード コンテナが Artifact Registry からワークロード Docker イメージを pull するために必要です。
./create_workload.sh

また、ワークロードは、使用前にモデルのハッシュまたは署名を確認することで、想定されるバージョンの ML モデルを読み込んでいることを確認するようにコーディングできます。このような追加のチェックを行うことで、ML モデルの完全性を確保できます。これにより、ワークロードで異なるバージョンの ML モデルを使用することが想定される場合、ワークロード オペレーターはワークロード イメージまたはそのパラメータも更新する必要があります。

4. ワークロードを承認して実行する

ワークロードを承認する

Primus は、次のリソースの属性に基づいて、ワークロードが ML モデルにアクセスすることを承認したいと考えています。

  • 内容: 確認済みのコード
  • 場所: 安全な環境
  • Who: 信頼されているオペレーター

Primus は、これらの要件に基づいてアクセス ポリシーを適用するために Workload Identity 連携を使用します。Workload Identity 連携では、属性条件を指定できます。これらの条件により、Workload Identity プール(WIP)で認証できる ID が制限されます。証明書検証サービスを Workload Identity プール プロバイダとして WIP に追加して、測定値を提示し、ポリシーを適用できます。

Workload Identity プールは、クラウド リソースの設定手順の一部としてすでに作成されています。これで、Primus は新しい OIDC Workload Identity プール プロバイダを作成します。指定された --attribute-condition は、ワークロード コンテナへのアクセスを承認します。この構成では、次のものが必要になります。

  • 内容: 最新の $WORKLOAD_IMAGE_NAME$PRIMUS_ARTIFACT_REPOSITORY リポジトリにアップロードされました。
  • 条件: Confidential Space の信頼できる実行環境が、完全にサポートされている Confidential Space VM イメージで実行されている。
  • 対象: 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 VM でワークロードを実行します。必要な 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}

結果を表示

ワークロードが正常に完了すると、ML ワークロードの結果が $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 ストレージ バケット内のサンプル画像ごとに、結果にエントリが表示されます。このエントリには、次の 2 つの重要な情報が含まれます。

  • predicted_class のインデックス: モデルが画像を分類したクラスを表す数値インデックスです。
  • Top_k_predictions: 画像の予測を最大 k 個提供します。可能性の高い順にランク付けされます。この Codelab では k の値は 5 に設定されていますが、ワークロード コードで調整して、予測の数を増減させることができます。

クラス インデックスを人間が読める形式のクラス名に変換するには、こちらで入手可能なラベルのリストをご覧ください。たとえば、クラス インデックスが 2 の場合、ラベルリストのクラスラベル「tench」に対応します。

この Codelab では、TEE で実行されているワークロードにのみリリースされる Primus 所有のモデルを実証しました。Secundus は TEE で ML ワークロードを実行します。このワークロードは Primus が所有するモデルを使用できますが、Primus はモデルを完全に制御できます。

不正なワークロードを実行する

Secundus は、Primus によって承認されていない独自のアーティファクト リポジトリから別のワークロード イメージを pull して、ワークロード イメージを変更します。Primus の Workload Identity プールは、${PRIMUS_PROJECT_REPOSITORY_REGION}-docker.pkg.dev/$PRIMUS_PROJECT_ID/$PRIMUS_ARTIFACT_REPOSITORY/$WORKLOAD_IMAGE_NAME:$WORKLOAD_IMAGE_TAG ワークロード イメージのみを承認しています。

ワークロードを再実行する

Secundus がこの新しいワークロード イメージで元のワークロードを実行しようとすると、失敗します。エラーを表示するには、元の結果ファイルと VM インスタンスを削除してから、ワークロードの実行を再度試してください。

Secundus の Artifact Registry(us-docker.pkg.dev/${SECUNDUS_PROJECT_ID}/custom-image/${WORKLOAD_IMAGE_NAME}:${WORKLOAD_IMAGE_TAG})に新しい Docker イメージが公開され、ワークロード サービス アカウント($WORKLOAD_SERVICEACCOUNT)にこの新しいワークロード イメージを読み取る Artifact Registry 読み取り権限が付与されていることを確認してください。これは、Primus の WIP ポリシーがワークロードによって提示されたトークンを拒否する前に、ワークロードが終了しないようにするためです。

既存の結果ファイルと VM インスタンスを削除する

  1. プロジェクトを $SECUNDUS_PROJECT_ID プロジェクトに設定します。
gcloud config set project $SECUNDUS_PROJECT_ID
  1. 結果ファイルを削除します。
gsutil rm gs://$SECUNDUS_RESULT_STORAGE_BUCKET/result
  1. Confidential VM インスタンスを削除します。
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 ワークロード ID プール($PRIMUS_WORKLOAD_IDENTITY_POOL)。
  • Secundus のワークロード サービス アカウント($WORKLOAD_SERVICEACCOUNT)。
  • Secundus の入力ストレージ バケット($SECUNDUS_INPUT_STORAGE_BUCKET))。
  • ワークロード コンピューティング インスタンス。
  • Secundus の結果ストレージ バケット($SECUNDUS_RESULT_STORAGE_BUCKET)。
$ ./cleanup.sh

確認が終わったら、プロジェクトの削除をご検討ください。

  • Cloud Platform Console に移動します。
  • シャットダウンするプロジェクトを選択し、上部の [削除] をクリックします。これにより、プロジェクトの削除がスケジュールされます。

次のステップ

以下の類似の Codelab をご覧ください。