在本指南中,Alex 和 Bola 想找出誰的薪水最高,但不想讓對方知道確切金額。他們決定使用 Confidential Space 保護資料機密性,並同意擔任下列角色:
Alex:資料協作者、工作負載作者
Bola:資料協作者、工作負載運算子
這項安排是為了盡可能簡化本指南的內容。不過,工作負載的作者和運算子可以完全獨立於資料協作者,而且您可以視需要新增任意數量的協作者。
事前準備
本指南會使用單一機構中的單一帳戶,示範機密空間情境,並提供多個專案的存取權,讓您體驗整個流程。在正式部署中,協作者、工作負載作者和工作負載運算子會使用不同的帳戶,並在各自的機構中擁有專案,彼此無法存取,機密資料也會分開存放。
機密空間可與許多 Google Cloud服務互動,以產生結果,包括但不限於:
本指南會使用這些功能,並假設您已具備基本瞭解。
必要的角色
如要取得完成本指南所需的權限,請要求管理員在專案中授予您下列 IAM 角色:
-
資料協作者 (Alex 和 Bola) 的 Cloud KMS 管理員 (
roles/cloudkms.admin)。 -
資料協作者 (Alex 和 Bola) 的身分與存取權管理 Workload Identity 集區管理員 (
roles/iam.workloadIdentityPoolAdmin)。 -
資料協作者 (Alex 和 Bola) 的服務使用情形管理員 (
roles/serviceusage.serviceUsageAdmin)。 -
資料協作者 (Alex 和 Bola) 和工作負載運算子 (Bola) 的 Storage 管理員 (
roles/storage.admin)。 -
工作負載運算子 (Bola) 的服務帳戶管理員 (
roles/iam.serviceAccountAdmin)。 -
工作負載運算子 (Bola) 的 Compute 管理員 (
roles/compute.admin)。 -
工作負載運算子 (Bola) 的安全管理員 (
roles/securityAdmin)。 -
工作負載作者 (Alex) 的 Artifact Registry 管理員 (
roles/artifactregistry.admin)。
如要進一步瞭解如何授予角色,請參閱「管理專案、資料夾和組織的存取權」。
設定資料協作者資源
Alex 和 Bola 都需要獨立專案,其中包含下列資源:
機密資料本身。
加密金鑰,用於加密資料並確保機密性。
用於儲存加密資料的 Cloud Storage bucket。
工作負載身分集區。處理機密資料的工作負載會使用集區存取私有資料並解密。
如要開始使用,請前往 Google Cloud 控制台:
設定 Alex 的資源
如要為 Alex 設定資源,請按照下列操作說明進行。
- 按一下「啟用 Cloud Shell」。
-
在 Cloud Shell 中輸入下列指令,為 Alex 建立專案,並將 ALEX_PROJECT_ID 替換為您選擇的名稱:
gcloud projects create ALEX_PROJECT_ID -
切換至新建立的專案:
gcloud config set project ALEX_PROJECT_ID -
以資料協作者和工作負載作者的身分,啟用 Alex 需要的 API:
gcloud services enable \ artifactregistry.googleapis.com \ cloudkms.googleapis.com \ iamcredentials.googleapis.com -
使用 Cloud Key Management Service 建立金鑰環和加密金鑰:
gcloud kms keyrings create ALEX_KEYRING_NAME \ --location=global gcloud kms keys create ALEX_KEY_NAME \ --location=global \ --keyring=ALEX_KEYRING_NAME \ --purpose=encryption -
授予 Alex
cloudkms.cryptoKeyEncrypter角色,讓他們可以使用新建立的加密金鑰加密資料:gcloud kms keys add-iam-policy-binding \ "projects/ALEX_PROJECT_ID/locations/global/\ keyRings/ALEX_KEYRING_NAME/\ cryptoKeys/ALEX_KEY_NAME" \ --member=user:$(gcloud config get-value account) \ --role=roles/cloudkms.cryptoKeyEncrypter -
建立 Alex 的 workload identity pool:
gcloud iam workload-identity-pools create ALEX_POOL_NAME \ --location=global -
建立 Cloud Storage bucket 來存放輸入資料,並建立另一個 bucket 來存放結果:
gcloud storage buckets create gs://ALEX_INPUT_BUCKET_NAME \ gs://ALEX_OUTPUT_BUCKET_NAME -
建立檔案,只包含 Alex 的薪資 (以數字表示):
echo 123456 > ALEX_SALARY.txt -
加密檔案,然後上傳至 Alex 的 bucket:
gcloud kms encrypt \ --ciphertext-file="ALEX_ENCRYPTED_SALARY_FILE" \ --plaintext-file="ALEX_SALARY.txt" \ --key="projects/ALEX_PROJECT_ID/locations/global/\ keyRings/ALEX_KEYRING_NAME/\ cryptoKeys/ALEX_KEY_NAME"gcloud storage cp ALEX_ENCRYPTED_SALARY_FILE gs://ALEX_INPUT_BUCKET_NAME
設定 Bola 的資源
如要為 Bola 設定資源,請按照下列操作說明進行。
-
在 Cloud Shell 中輸入下列指令,為 Bola 建立專案,並將 BOLA_PROJECT_ID 替換成您選擇的名稱:
gcloud projects create BOLA_PROJECT_ID -
切換至新建立的專案:
gcloud config set project BOLA_PROJECT_ID -
以資料協作者和工作負載運算子身分,啟用 Bola 要求的 API:
gcloud services enable \ cloudkms.googleapis.com \ compute.googleapis.com \ confidentialcomputing.googleapis.com \ iamcredentials.googleapis.com -
使用 Cloud Key Management Service 建立金鑰環和加密金鑰:
gcloud kms keyrings create BOLA_KEYRING_NAME \ --location=global gcloud kms keys create BOLA_KEY_NAME \ --location=global \ --keyring=BOLA_KEYRING_NAME \ --purpose=encryption -
授予 Bola
cloudkms.cryptoKeyEncrypter角色,讓他們可以使用新建立的加密金鑰加密資料:gcloud kms keys add-iam-policy-binding \ "projects/BOLA_PROJECT_ID/locations/global/\ keyRings/BOLA_KEYRING_NAME/\ cryptoKeys/BOLA_KEY_NAME" \ --member=user:$(gcloud config get-value account) \ --role=roles/cloudkms.cryptoKeyEncrypter -
建立 Bola 的 workload identity pool:
gcloud iam workload-identity-pools create BOLA_POOL_NAME \ --location=global -
建立 Cloud Storage bucket 來存放輸入資料,並建立另一個 bucket 來存放結果:
gcloud storage buckets create gs://BOLA_INPUT_BUCKET_NAME \ gs://BOLA_OUTPUT_BUCKET_NAME -
建立檔案,只包含 Bola 的薪資 (以數字表示):
echo 111111 > BOLA_SALARY.txt -
加密檔案,然後上傳至 Bola 的 bucket:
gcloud kms encrypt \ --ciphertext-file="BOLA_ENCRYPTED_SALARY_FILE" \ --plaintext-file="BOLA_SALARY.txt" \ --key="projects/BOLA_PROJECT_ID/locations/global/\ keyRings/BOLA_KEYRING_NAME/\ cryptoKeys/BOLA_KEY_NAME"gcloud storage cp BOLA_ENCRYPTED_SALARY_FILE gs://BOLA_INPUT_BUCKET_NAME
為工作負載建立服務帳戶
在本指南中,Bola 負責運作及執行工作負載,但任何人 (包括第三方) 都能擔任這些角色。Bola 建立的 VM 執行個體會附加服務帳戶,該帳戶有權產生認證權杖、寫入記錄、讀取 Alex 和 Bola 的加密資料,以及將結果寫入特定 Cloud Storage 值區。
在 Bola 的專案中完成下列步驟,設定服務帳戶:
建立服務帳戶來執行工作負載:
gcloud iam service-accounts create WORKLOAD_SERVICE_ACCOUNT_NAME將
iam.serviceAccountUser角色授予 Bola,這樣他稍後就能將服務帳戶附加至工作負載 VM:gcloud iam service-accounts add-iam-policy-binding \ WORKLOAD_SERVICE_ACCOUNT_NAME@BOLA_PROJECT_ID.iam.gserviceaccount.com \ --member=user:$(gcloud config get-value account) \ --role=roles/iam.serviceAccountUser將
confidentialcomputing.workloadUser角色授予服務帳戶,以便產生認證權杖:gcloud projects add-iam-policy-binding BOLA_PROJECT_ID \ --member=serviceAccount:WORKLOAD_SERVICE_ACCOUNT_NAME@BOLA_PROJECT_ID.iam.gserviceaccount.com \ --role=roles/confidentialcomputing.workloadUser將
logging.logWriter角色授予服務帳戶,以便將記錄寫入 Cloud Logging,方便您查看工作負載的進度:gcloud projects add-iam-policy-binding BOLA_PROJECT_ID \ --member=serviceAccount:WORKLOAD_SERVICE_ACCOUNT_NAME@BOLA_PROJECT_ID.iam.gserviceaccount.com \ --role=roles/logging.logWriter授予服務帳戶讀取 Alex 和 Bola 值區的權限,這些值區包含加密資料,並授予服務帳戶寫入每個結果值區的權限:
gcloud storage buckets add-iam-policy-binding gs://ALEX_INPUT_BUCKET_NAME \ --member=serviceAccount:WORKLOAD_SERVICE_ACCOUNT_NAME@BOLA_PROJECT_ID.iam.gserviceaccount.com \ --role=roles/storage.objectViewer gcloud storage buckets add-iam-policy-binding gs://ALEX_OUTPUT_BUCKET_NAME \ --member=serviceAccount:WORKLOAD_SERVICE_ACCOUNT_NAME@BOLA_PROJECT_ID.iam.gserviceaccount.com \ --role=roles/storage.objectAdmin gcloud storage buckets add-iam-policy-binding gs://BOLA_INPUT_BUCKET_NAME \ --member=serviceAccount:WORKLOAD_SERVICE_ACCOUNT_NAME@BOLA_PROJECT_ID.iam.gserviceaccount.com \ --role=roles/storage.objectViewer gcloud storage buckets add-iam-policy-binding gs://BOLA_OUTPUT_BUCKET_NAME \ --member=serviceAccount:WORKLOAD_SERVICE_ACCOUNT_NAME@BOLA_PROJECT_ID.iam.gserviceaccount.com \ --role=roles/storage.objectAdmin前提是授予存取權的使用者,在包含要操作的 Cloud Storage 值區的專案中,具有「儲存空間管理員」
roles/storage.admin角色。
建立工作負載
在本指南中,Alex 會提供工作負載的程式碼,並建構 Docker 映像檔來存放程式碼,但任何人 (包括第三方) 都可以擔任這些角色。
Alex 需要為工作負載建立下列資源:
執行工作負載的程式碼。
Artifact Registry 中的 Docker 存放區,執行工作負載的服務帳戶必須具備存取權。
包含並執行工作負載程式碼的 Docker 映像檔。
如要建立及設定資源,請在 Alex 的專案中完成下列步驟:
切換至 Alex 的專案:
gcloud config set project ALEX_PROJECT_ID按一下「開啟編輯器」開啟 Cloud Shell 編輯器,然後建立名為
salary.go的新檔案。將下列程式碼複製到檔案中,然後儲存:package main import ( "context" "fmt" "io" "os" "strconv" "strings" "time" kms "cloud.google.com/go/kms/apiv1" kmspb "cloud.google.com/go/kms/apiv1/kmspb" "cloud.google.com/go/storage" "google.golang.org/api/option" ) type collaborator struct { name string wipName string keyName string inputBucket string inputFile string outputBucket string } // The following values are pulled from environment variables // Alex's values var collaborator1Name string = os.Getenv("COLLAB_1_NAME") // Alex's name var collaborator1EncryptedSalaryFileName string = os.Getenv("COLLAB_1_ENCRYPTED_SALARY") // The name of Alex's encrypted salary file. var collaborator1BucketInputName string = os.Getenv("COLLAB_1_INPUT_BUCKET") // The name of the storage bucket that contains Alex's encrypted salary file. var collaborator1BucketOutputName string = os.Getenv("COLLAB_1_OUTPUT_BUCKET") // The name of the storage bucket to store Alex's results in. var collaborator1KMSKeyringName string = os.Getenv("COLLAB_1_KEYRING_NAME") // Alex's Key Management Service key ring. var collaborator1KMSKeyName string = os.Getenv("COLLAB_1_KEY_NAME") // Alex's Key Management Service key. var collaborator1ProjectName string = os.Getenv("COLLAB_1_PROJECT_ID") // Alex's project ID. var collaborator1ProjectNumber string = os.Getenv("COLLAB_1_PROJECT_NUMBER") // Alex's project number. var collaborator1PoolName string = os.Getenv("COLLAB_1_POOL_NAME") // Alex's workload identity pool name. // Bola's values var collaborator2Name string = os.Getenv("COLLAB_2_NAME") // Bola's name var collaborator2EncryptedSalaryFileName string = os.Getenv("COLLAB_2_ENCRYPTED_SALARY") // The name of Bola's encrypted salary file. var collaborator2BucketInputName string = os.Getenv("COLLAB_2_INPUT_BUCKET") // The name of the storage bucket that contains Bola's encrypted salary file. var collaborator2BucketOutputName string = os.Getenv("COLLAB_2_OUTPUT_BUCKET") // The name of the storage bucket to store Bola's results in. var collaborator2KMSKeyringName string = os.Getenv("COLLAB_2_KEYRING_NAME") // Bola's Key Management Service key ring. var collaborator2KMSKeyName string = os.Getenv("COLLAB_2_KEY_NAME") // Bola's Key Management Service key. var collaborator2ProjectName string = os.Getenv("COLLAB_2_PROJECT_ID") // Bola's project ID. var collaborator2ProjectNumber string = os.Getenv("COLLAB_2_PROJECT_NUMBER") // Bola's project number. var collaborator2PoolName string = os.Getenv("COLLAB_2_POOL_NAME") // Bola's workload identity pool name. var collaborators = [2]collaborator{ { collaborator1Name, "projects/" + collaborator1ProjectNumber + "/locations/global/workloadIdentityPools/" + collaborator1PoolName + "/providers/attestation-verifier", "projects/" + collaborator1ProjectName + "/locations/global/keyRings/" + collaborator1KMSKeyringName + "/cryptoKeys/" + collaborator1KMSKeyName, collaborator1BucketInputName, collaborator1EncryptedSalaryFileName, collaborator1BucketOutputName, }, { collaborator2Name, "projects/" + collaborator2ProjectNumber + "/locations/global/workloadIdentityPools/" + collaborator2PoolName + "/providers/attestation-verifier", "projects/" + collaborator2ProjectName + "/locations/global/keyRings/" + collaborator2KMSKeyringName + "/cryptoKeys/" + collaborator2KMSKeyName, collaborator2BucketInputName, collaborator2EncryptedSalaryFileName, collaborator2BucketOutputName, }, } const credentialConfig = `{ "type": "external_account", "audience": "//iam.googleapis.com/%s", "subject_token_type": "urn:ietf:params:oauth:token-type:jwt", "token_url": "https://sts.googleapis.com/v1/token", "credential_source": { "file": "/run/container_launcher/attestation_verifier_claims_token" } }` func main() { fmt.Println("workload started") ctx := context.Background() storageClient, err := storage.NewClient(ctx) // Using the default credential on the Compute Engine VM if err != nil { panic(err) } // Get and decrypt s0, err := getSalary(ctx, storageClient, collaborators[0]) if err != nil { panic(err) } s1, err := getSalary(ctx, storageClient, collaborators[1]) if err != nil { panic(err) } res := "" if s0 > s1 { res = fmt.Sprintf("%s earns more!\n", collaborators[0].name) } else if s1 > s0 { res = fmt.Sprintf("%s earns more!\n", collaborators[1].name) } else { res = "You earn the same!\n" } now := time.Now() for _, cw := range collaborators { outputWriter := storageClient.Bucket(cw.outputBucket).Object(fmt.Sprintf("comparison-result-%d", now.Unix())).NewWriter(ctx) _, err = outputWriter.Write([]byte(res)) if err != nil { fmt.Printf("Could not write: %v", err) panic(err) } if err = outputWriter.Close(); err != nil { fmt.Printf("Could not close: %v", err) panic(err) } } } func getSalary(ctx context.Context, storageClient *storage.Client, cw collaborator) (float64, error) { encryptedBytes, err := getFile(ctx, storageClient, cw.inputBucket, cw.inputFile) if err != nil { return 0.0, err } decryptedByte, err := decryptByte(ctx, cw.keyName, cw.wipName, encryptedBytes) if err != nil { return 0.0, err } decryptedNumber := strings.TrimSpace(string(decryptedByte)) num, err := strconv.ParseFloat(decryptedNumber, 64) if err != nil { return 0.0, err } return num, nil } func decryptByte(ctx context.Context, keyName, wippro string, encryptedData []byte) ([]byte, error) { cc := fmt.Sprintf(credentialConfig, wippro) kmsClient, err := kms.NewKeyManagementClient(ctx, option.WithCredentialsJSON([]byte(cc))) if err != nil { return nil, fmt.Errorf("creating a new KMS client with federated credentials: %w", err) } decryptRequest := &kmspb.DecryptRequest{ Name: keyName, Ciphertext: encryptedData, } decryptResponse, err := kmsClient.Decrypt(ctx, decryptRequest) if err != nil { return nil, fmt.Errorf("could not decrypt ciphertext: %w", err) } return decryptResponse.Plaintext, nil } func getFile(ctx context.Context, c *storage.Client, bucketName string, objPath string) ([]byte, error) { bucketHandle := c.Bucket(bucketName) objectHandle := bucketHandle.Object(objPath) objectReader, err := objectHandle.NewReader(ctx) if err != nil { return nil, err } defer objectReader.Close() s, err := io.ReadAll(objectReader) if err != nil { return nil, err } return s, nil }請確保所有當事人都能閱讀及稽核原始碼。
在 Cloud Shell 編輯器中建立名為
Dockerfile的檔案,並在當中加入下列內容:# Compile the provided Go code to a statically linked binary FROM golang:latest AS build WORKDIR /build COPY salary.go . RUN go mod init salary RUN go get cloud.google.com/go/kms/apiv1 cloud.google.com/go/storage google.golang.org/api/option google.golang.org/genproto/googleapis/cloud/kms/v1 RUN CGO_ENABLED=0 go build -trimpath # Build the workload container image FROM alpine:latest AS run WORKDIR /test COPY --from=build /build/salary /test/salary ENTRYPOINT ["/test/salary"] CMD [] # Allow the workload to access the following environment variables LABEL "tee.launch_policy.allow_env_override"="\ COLLAB_1_NAME,\ COLLAB_2_NAME,\ COLLAB_1_ENCRYPTED_SALARY,\ COLLAB_2_ENCRYPTED_SALARY,\ COLLAB_1_INPUT_BUCKET,\ COLLAB_2_INPUT_BUCKET,\ COLLAB_1_OUTPUT_BUCKET,\ COLLAB_2_OUTPUT_BUCKET,\ COLLAB_1_KEYRING_NAME,\ COLLAB_2_KEYRING_NAME,\ COLLAB_1_KEY_NAME,\ COLLAB_2_KEY_NAME,\ COLLAB_1_PROJECT_ID,\ COLLAB_2_PROJECT_ID,\ COLLAB_1_PROJECT_NUMBER,\ COLLAB_2_PROJECT_NUMBER,\ COLLAB_1_POOL_NAME,\ COLLAB_2_POOL_NAME"這個
Dockerfile會使用多階段建構程序,先編譯 Go 程式碼,然後將編譯後的程式碼複製到最終的工作負載容器。此外,您也可以在該工作負載容器中使用特定環境變數。這些環境變數的值稍後會對應至工作負載運作所需的特定資源。按一下「Open Terminal」(開啟終端機) 切換回 Cloud Shell,或從「View」(檢視) 選單叫用 Cloud Shell 編輯器內建的終端機。
在 Artifact Registry 中建立 Docker 存放區:
gcloud artifacts repositories create REPOSITORY_NAME \ --repository-format=docker \ --location=us將 Artifact Registry 讀取者 (
roles/artifactregistry.reader) 角色授予要執行工作負載的服務帳戶,以便從存放區讀取資料:gcloud artifacts repositories add-iam-policy-binding REPOSITORY_NAME \ --location=us \ --member=serviceAccount:WORKLOAD_SERVICE_ACCOUNT_NAME@BOLA_PROJECT_ID.iam.gserviceaccount.com \ --role=roles/artifactregistry.reader更新 Docker 憑證,加入
us-docker.pkg.dev網域名稱:gcloud auth configure-docker us-docker.pkg.dev在終端機中輸入下列指令,從
Dockerfile建立 Docker 映像檔:docker build -t \ "us-docker.pkg.dev/ALEX_PROJECT_ID/\ REPOSITORY_NAME/WORKLOAD_CONTAINER_NAME:latest" .將 Docker 映像檔推送至 Artifact Registry:
docker push \ us-docker.pkg.dev/ALEX_PROJECT_ID/REPOSITORY_NAME/WORKLOAD_CONTAINER_NAMEDocker 推送回應會列出映像檔的 SHA256 摘要,稍後授權工作負載時需要用到這個摘要。摘要內容類似下列範例:
sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855將映像檔摘要 (包括
sha256:前置字串) 複製到可供參考的位置。您也可以在下列程式碼範例中輸入摘要,預先填寫本指南中需要該值的其餘程式碼範例:WORKLOAD_CONTAINER_IMAGE_DIGEST授權使用 Docker 映像檔前,請確保所有當事人稽核該映像檔,並確認映像檔值得信賴。
授權工作負載
雙方核准工作負載後,Alex 和 Bola 必須將 Google Cloud 認證新增為工作負載身分集區的提供者。供應商會指定要使用的認證服務,以及工作負載必須符合的屬性,才能在 Alex 或 Bola 的資料上運作。如果惡意行為人變更 Docker 映像檔,或修改其他測量的屬性,工作負載就會遭到拒絕存取。
本指南使用屬性對應,根據映像檔摘要,直接授予工作負載資源存取權。不過,在其他情況下,您可能偏好使用服務帳戶模擬功能來存取資源。詳情請參閱「外部工作負載存取權」。
如要為 Alex 和 Bola 設定供應商,並符合必要條件,請完成下列步驟:
輸入下列指令,為 Alex 建立供應商:
gcloud iam workload-identity-pools providers create-oidc attestation-verifier \ --location=global \ --workload-identity-pool=ALEX_POOL_NAME \ --issuer-uri="https://confidentialcomputing.googleapis.com/" \ --allowed-audiences="https://sts.googleapis.com" \ --attribute-mapping="google.subject=\"gcpcs\ ::\"+assertion.submods.container.image_digest+\"\ ::\"+assertion.submods.gce.project_number+\"\ ::\"+assertion.submods.gce.instance_id,\ attribute.image_digest=assertion.submods.container.image_digest" \ --attribute-condition="assertion.swname == 'CONFIDENTIAL_SPACE'"取得 Alex 的專案編號,以用於下一個指令:
gcloud projects describe ALEX_PROJECT_ID --format="value(projectNumber)"將
cloudkms.cryptoKeyDecrypter角色授予 Alex 的供應商所定義的聯合身分,並指定image_digest屬性,這樣只有具有指定摘要的容器才能解密 KMS 金鑰:gcloud kms keys add-iam-policy-binding \ "projects/ALEX_PROJECT_ID/locations/global/\ keyRings/ALEX_KEYRING_NAME/\ cryptoKeys/ALEX_KEY_NAME" \ --member="principalSet://iam.googleapis.com/\ projects/ALEX_PROJECT_NUMBER/locations/global/\ workloadIdentityPools/ALEX_POOL_NAME/\ attribute.image_digest/WORKLOAD_CONTAINER_IMAGE_DIGEST" \ --role=roles/cloudkms.cryptoKeyDecrypter切換至 Bola 的專案:
gcloud config set project BOLA_PROJECT_ID輸入下列指令,為 Bola 建立供應商:
gcloud iam workload-identity-pools providers create-oidc attestation-verifier \ --location=global \ --workload-identity-pool=BOLA_POOL_NAME \ --issuer-uri="https://confidentialcomputing.googleapis.com/" \ --allowed-audiences="https://sts.googleapis.com" \ --attribute-mapping="google.subject=\"gcpcs\ ::\"+assertion.submods.container.image_digest+\"\ ::\"+assertion.submods.gce.project_number+\"\ ::\"+assertion.submods.gce.instance_id,\ attribute.image_digest=assertion.submods.container.image_digest" \ --attribute-condition="assertion.swname == 'CONFIDENTIAL_SPACE'"取得 Bola 的專案編號,以用於下一個指令:
gcloud projects describe BOLA_PROJECT_ID --format="value(projectNumber)"將
cloudkms.cryptoKeyDecrypter角色授予 Bola 提供者定義的聯合身分,並指定image_digest屬性,這樣只有具有指定摘要的工作負載容器才能解密其 KMS 金鑰:gcloud kms keys add-iam-policy-binding \ "projects/BOLA_PROJECT_ID/locations/global/\ keyRings/BOLA_KEYRING_NAME/\ cryptoKeys/BOLA_KEY_NAME" \ --member="principalSet://iam.googleapis.com/\ projects/BOLA_PROJECT_NUMBER/locations/global/\ workloadIdentityPools/BOLA_POOL_NAME/\ attribute.image_digest/WORKLOAD_CONTAINER_IMAGE_DIGEST" \ --role=roles/cloudkms.cryptoKeyDecrypter
測試工作負載
在 Alex 和 Bola 的 workload identity pool 中新增提供者,並備妥必要資源後,工作負載運算子就可以測試工作負載。
如要測試工作負載,請在 Bola 的專案中建立新的機密 VM 執行個體,並具備下列屬性:
支援的設定,搭配 AMD SEV、Intel TDX 或 Intel TDX 與 NVIDIA Confidential Computing (搶先版)。
已啟用安全啟動功能。
以機密空間偵錯映像檔為基礎的 OS,方便進行疑難排解。
參照Alex 先前建立的 Docker 映像檔。這會載入 Confidential Space 映像檔。
附加的服務帳戶 (Bola 先前建立),用於執行工作負載。
STDOUT和STDERR重新導向至 Cloud Logging 和序列埠控制台。工作負載需要的資源詳細資料會設為環境變數。
在 Bola 的 Cloud Shell 中輸入下列指令,測試工作負載:
gcloud compute instances create WORKLOAD_VM_2_NAME \
--confidential-compute-type=SEV \
--shielded-secure-boot \
--scopes=cloud-platform \
--zone=us-west1-b \
--maintenance-policy=MIGRATE \
--min-cpu-platform="AMD Milan" \
--image-project=confidential-space-images \
--image-family=confidential-space-debug \
--service-account=WORKLOAD_SERVICE_ACCOUNT_NAME@BOLA_PROJECT_ID.iam.gserviceaccount.com \
--metadata="^~^tee-image-reference=us-docker.pkg.dev/\
ALEX_PROJECT_ID/REPOSITORY_NAME/WORKLOAD_CONTAINER_NAME:latest\
~tee-container-log-redirect=true\
~tee-env-COLLAB_1_NAME=Alex\
~tee-env-COLLAB_2_NAME=Bola\
~tee-env-COLLAB_1_ENCRYPTED_SALARY=ALEX_ENCRYPTED_SALARY_FILE\
~tee-env-COLLAB_2_ENCRYPTED_SALARY=BOLA_ENCRYPTED_SALARY_FILE\
~tee-env-COLLAB_1_INPUT_BUCKET=ALEX_INPUT_BUCKET_NAME\
~tee-env-COLLAB_2_INPUT_BUCKET=BOLA_INPUT_BUCKET_NAME\
~tee-env-COLLAB_1_OUTPUT_BUCKET=ALEX_OUTPUT_BUCKET_NAME\
~tee-env-COLLAB_2_OUTPUT_BUCKET=BOLA_OUTPUT_BUCKET_NAME\
~tee-env-COLLAB_1_KEYRING_NAME=ALEX_KEYRING_NAME\
~tee-env-COLLAB_2_KEYRING_NAME=BOLA_KEYRING_NAME\
~tee-env-COLLAB_1_KEY_NAME=ALEX_KEY_NAME\
~tee-env-COLLAB_2_KEY_NAME=BOLA_KEY_NAME\
~tee-env-COLLAB_1_PROJECT_ID=ALEX_PROJECT_ID\
~tee-env-COLLAB_2_PROJECT_ID=BOLA_PROJECT_ID\
~tee-env-COLLAB_1_PROJECT_NUMBER=ALEX_PROJECT_NUMBER\
~tee-env-COLLAB_2_PROJECT_NUMBER=BOLA_PROJECT_NUMBER\
~tee-env-COLLAB_1_POOL_NAME=ALEX_POOL_NAME\
~tee-env-COLLAB_2_POOL_NAME=BOLA_POOL_NAME"
查看進度
如要查看 Bola 專案中工作負載的進度,請前往 Logs Explorer。
如要只顯示私密空間記錄項目,請依下列記錄欄位 (如有) 篩選:
資源類型:VM 執行個體
執行個體 ID:VM 的執行個體 ID
記錄名稱:confidential-space-launcher
如要重新整理記錄,請按一下「跳到現在時間」 。你也可以捲動至較早的結果,然後再次捲動至記錄結尾,載入最新項目。
查看結果
如果工作負載工作結束並傳回 0,表示沒有發生任何錯誤,現在可以檢查 Alex 和 Bola 輸出值 bucket 中的輸出內容:
切換至 Alex 的專案:
gcloud config set project ALEX_PROJECT_ID列出結果值區中的所有檔案:
gcloud storage ls gs://ALEX_OUTPUT_BUCKET_NAME讀取列出的最新檔案,並將
ALEX_OUTPUT_CLOUD_STORAGE_PATH替換為檔案路徑,包括gs://:gcloud storage cat ALEX_OUTPUT_CLOUD_STORAGE_PATH如果沒有檔案,請偵錯工作負載。
切換至 Bola 的專案:
gcloud config set project BOLA_PROJECT_ID列出結果值區中的所有檔案:
gcloud storage ls gs://BOLA_OUTPUT_BUCKET_NAME讀取列出的最新檔案,並將
BOLA_RESULTS_CLOUD_STORAGE_PATH替換為檔案路徑,包括gs://:gcloud storage cat BOLA_RESULTS_CLOUD_STORAGE_PATH如果沒有檔案,請偵錯工作負載。
成功讀取結果後,請停止 VM 執行個體:
gcloud compute instances stop WORKLOAD_VM_2_NAME --zone=us-west1-b
Alex 和 Bola 讀取檔案後,就能知道誰的薪水較高,但彼此不會看到對方的薪資。
偵錯並重新啟動工作負載
機密空間環境包含許多部分,可能因設定錯誤而導致工作負載失敗。
與正式版機密空間映像檔不同,偵錯映像檔會在工作負載完成後,讓 VM 執行個體繼續運作。也就是說,如果記錄檔提供的資訊不足以解決問題,下一步就是透過 SSH 連線至 VM 執行個體,然後繼續偵錯。
偵錯完成後,請停止 VM 執行個體:
gcloud compute instances stop WORKLOAD_VM_2_NAME --zone=us-west1-b
如要在偵錯環境中執行工作負載,請再次啟動 VM:
gcloud compute instances start WORKLOAD_VM_2_NAME --zone=us-west1-b
強化正式版環境
成功測試工作負載後,即可強化 Confidential Space 環境,以利部署至正式環境。Alex 和 Bola 需要在供應商中加入support_attributes斷言,確認工作負載使用的是正式版 Confidential Space 映像檔:
切換至 Alex 的專案:
gcloud config set project ALEX_PROJECT_ID輸入下列指令,更新 Alex 的供應商:
gcloud iam workload-identity-pools providers update-oidc attestation-verifier \ --location=global \ --workload-identity-pool=ALEX_POOL_NAME \ --issuer-uri="https://confidentialcomputing.googleapis.com/" \ --allowed-audiences="https://sts.googleapis.com" \ --attribute-mapping="google.subject=\"gcpcs\ ::\"+assertion.submods.container.image_digest+\"\ ::\"+assertion.submods.gce.project_number+\"\ ::\"+assertion.submods.gce.instance_id,\ attribute.image_digest=assertion.submods.container.image_digest" \ --attribute-condition="assertion.swname == 'CONFIDENTIAL_SPACE' \ && 'STABLE' in assertion.submods.confidential_space.support_attributes"切換至 Bola 的專案:
gcloud config set project BOLA_PROJECT_ID輸入下列指令,更新 Bola 的供應商:
gcloud iam workload-identity-pools providers update-oidc attestation-verifier \ --location=global \ --workload-identity-pool=BOLA_POOL_NAME \ --issuer-uri="https://confidentialcomputing.googleapis.com/" \ --allowed-audiences="https://sts.googleapis.com" \ --attribute-mapping="google.subject=\"gcpcs\ ::\"+assertion.submods.container.image_digest+\"\ ::\"+assertion.submods.gce.project_number+\"\ ::\"+assertion.submods.gce.instance_id,\ attribute.image_digest=assertion.submods.container.image_digest" \ --attribute-condition="assertion.swname == 'CONFIDENTIAL_SPACE' \ && 'STABLE' in assertion.submods.confidential_space.support_attributes"
部署正式環境工作負載
Bola 需要建立另一個 VM 執行個體來執行實際工作環境工作負載。與測試工作負載相比,以下項目有所不同:
這個 OS 是以正式版 Confidential Space 映像檔為基礎,這項設定會停用 SSH,且 VM 執行個體會在工作負載完成後停止。
記錄重新導向已移除。Cloud Logging 只會顯示不含機密資訊的基本記錄。
在 Bola 的 Cloud Shell 中輸入下列指令,部署正式版工作負載:
gcloud compute instances create WORKLOAD_VM_NAME \
--confidential-compute-type=SEV \
--shielded-secure-boot \
--scopes=cloud-platform \
--zone=us-west1-b \
--maintenance-policy=MIGRATE \
--image-project=confidential-space-images \
--image-family=confidential-space \
--service-account=WORKLOAD_SERVICE_ACCOUNT_NAME@BOLA_PROJECT_ID.iam.gserviceaccount.com \
--metadata="^~^tee-image-reference=us-docker.pkg.dev/\
ALEX_PROJECT_ID/REPOSITORY_NAME/WORKLOAD_CONTAINER_NAME:latest\
~tee-env-COLLAB_1_NAME=Alex\
~tee-env-COLLAB_2_NAME=Bola\
~tee-env-COLLAB_1_ENCRYPTED_SALARY=ALEX_ENCRYPTED_SALARY_FILE\
~tee-env-COLLAB_2_ENCRYPTED_SALARY=BOLA_ENCRYPTED_SALARY_FILE\
~tee-env-COLLAB_1_INPUT_BUCKET=ALEX_INPUT_BUCKET_NAME\
~tee-env-COLLAB_2_INPUT_BUCKET=BOLA_INPUT_BUCKET_NAME\
~tee-env-COLLAB_1_OUTPUT_BUCKET=ALEX_OUTPUT_BUCKET_NAME\
~tee-env-COLLAB_2_OUTPUT_BUCKET=BOLA_OUTPUT_BUCKET_NAME\
~tee-env-COLLAB_1_KEYRING_NAME=ALEX_KEYRING_NAME\
~tee-env-COLLAB_2_KEYRING_NAME=BOLA_KEYRING_NAME\
~tee-env-COLLAB_1_KEY_NAME=ALEX_KEY_NAME\
~tee-env-COLLAB_2_KEY_NAME=BOLA_KEY_NAME\
~tee-env-COLLAB_1_PROJECT_ID=ALEX_PROJECT_ID\
~tee-env-COLLAB_2_PROJECT_ID=BOLA_PROJECT_ID\
~tee-env-COLLAB_1_PROJECT_NUMBER=ALEX_PROJECT_NUMBER\
~tee-env-COLLAB_2_PROJECT_NUMBER=BOLA_PROJECT_NUMBER\
~tee-env-COLLAB_1_POOL_NAME=ALEX_POOL_NAME\
~tee-env-COLLAB_2_POOL_NAME=BOLA_POOL_NAME"
生產工作負載完成後,VM 執行個體就會停止。如要查看不同結果,您可以變更薪資、重新加密、重新上傳至對應的 Cloud Storage 值區,然後重新啟動 VM 執行個體,再次執行工作負載:
gcloud compute instances start WORKLOAD_VM_NAME --zone=us-west1-b
清除所用資源
如要移除本指南中建立的資源,請完成下列操作說明。
清除 Alex 的資源
切換至 Alex 的專案:
gcloud config set project ALEX_PROJECT_ID刪除 Alex 的工作負載身分集區:
gcloud iam workload-identity-pools delete ALEX_POOL_NAME \ --location=global刪除 Alex 的 Cloud Storage bucket:
gcloud storage rm gs://ALEX_INPUT_BUCKET_NAME \ gs://ALEX_OUTPUT_BUCKET_NAME --recursive刪除 Alex 的薪資檔案、Go 程式碼和
Dockerfile:rm ALEX_SALARY.txt \ ALEX_ENCRYPTED_SALARY_FILE \ salary.go \ Dockerfile選用: 關閉 Alex 的專案。
清除 Bola 的資源
切換至 Bola 的專案:
gcloud config set project BOLA_PROJECT_ID刪除執行測試工作流程的 VM:
gcloud compute instances delete WORKLOAD_VM_2_NAME --zone=us-west1-b刪除執行正式版工作流程的 VM:
gcloud compute instances delete WORKLOAD_VM_NAME --zone=us-west1-b刪除執行工作負載的服務帳戶:
gcloud iam service-accounts delete \ WORKLOAD_SERVICE_ACCOUNT_NAME@BOLA_PROJECT_ID.iam.gserviceaccount.com刪除 Bola 的工作負載身分集區:
gcloud iam workload-identity-pools delete BOLA_POOL_NAME \ --location=global刪除 Bola 的 Cloud Storage bucket:
gcloud storage rm gs://BOLA_INPUT_BUCKET_NAME \ gs://BOLA_OUTPUT_BUCKET_NAME --recursive刪除 Bola 的薪資檔案:
rm BOLA_SALARY.txt \ BOLA_ENCRYPTED_SALARY_FILE選用: 關閉 Bola 的專案。