在本指南中,Alex 和 Bola 想找出誰的薪水最高,但不想讓對方知道確切金額。他們決定使用 Confidential Space 保護資料機密性,並同意擔任下列角色:
Alex:資料協作者、工作負載作者
Bola:資料協作者、工作負載運算子
這種安排方式是為了盡可能簡化本指南的內容。不過,工作負載作者和運算子可以完全獨立於資料協作者,而且您可以視需要新增任意數量的協作者。
事前準備
本指南會使用單一機構中的單一帳戶,示範機密空間情境,並提供多個專案的存取權,讓您體驗整個流程。在正式部署中,協作者、工作負載作者和工作負載運算子會使用不同的帳戶,並擁有各自的專案 (這些專案位於不同的機構中,彼此無法存取),確保機密資料不會混淆。
機密空間可與許多 Google Cloud服務互動,以產生結果,包括但不限於:
本指南會使用所有這些功能,並假設您已具備基本瞭解。
必要 API
您必須在指定專案中啟用下列 API,才能完成本指南。
API 名稱 | API 名稱 | 在這些專案中啟用 |
---|---|---|
cloudkms.googleapis.com |
Cloud KMS | 資料協作者 (Alex 和 Bola 的專案) |
iamcredentials.googleapis.com |
IAM Service Account Credentials API |
資料協作者 (Alex 和 Bola 的專案) |
artifactregistry.googleapis.com |
Artifact Registry | 工作負載作者 (Alex 的專案) |
compute.googleapis.com |
Compute Engine | 工作負載運算子 (Bola 的專案) |
confidentialcomputing.googleapis.com |
機密運算 | 工作負載運算子 (Bola 的專案) |
必要的角色
如要取得完成本指南所需的權限,請要求管理員授予您專案的下列 IAM 角色:
-
資料協作者 (Alex 和 Bola) 的 Cloud KMS 管理員 (
roles/cloudkms.admin
)。 -
資料協作者 (Alex 和 Bola) 的身分與存取權管理 Workload Identity 集區管理員 (
roles/iam.workloadIdentityPoolAdmin
)。 -
資料協作者 (Alex 和 Bola) 的服務使用情形管理員 (
roles/serviceusage.serviceUsageAdmin
)。 -
資料協作者 (Alex 和 Bola) 的服務帳戶管理員 (
roles/iam.serviceAccountAdmin
)。 -
資料協作者 (Alex 和 Bola) 和工作負載運算子 (Bola) 的 Storage 管理員 (
roles/storage.admin
)。 -
工作負載運算子 (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 cloudkms.googleapis.com artifactregistry.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
-
建立服務帳戶,供工作負載稍後用來解密資料:
gcloud iam service-accounts create ALEX_SERVICE_ACCOUNT_NAME
-
將
cloudkms.cryptoKeyDecrypter
角色授予服務帳戶,讓服務帳戶可以使用您剛建立的加密金鑰解密資料:gcloud kms keys add-iam-policy-binding \ projects/ALEX_PROJECT_ID/locations/global/keyRings/ALEX_KEYRING_NAME/cryptoKeys/ALEX_KEY_NAME \ --member=serviceAccount:ALEX_SERVICE_ACCOUNT_NAME@ALEX_PROJECT_ID.iam.gserviceaccount.com \ --role=roles/cloudkms.cryptoKeyDecrypter
-
建立 workload identity pool,然後使用
iam.workloadIdentityUser
角色將服務帳戶連結至該集區:gcloud iam workload-identity-pools create ALEX_POOL_NAME \ --location=global
gcloud iam service-accounts add-iam-policy-binding \ ALEX_SERVICE_ACCOUNT_NAME@ALEX_PROJECT_ID.iam.gserviceaccount.com \ --member="principalSet://iam.googleapis.com/projects/"$(gcloud projects describe ALEX_PROJECT_ID \ --format="value(projectNumber)")"/locations/global/workloadIdentityPools/ALEX_POOL_NAME/*" \ --role=roles/iam.workloadIdentityUser
-
建立 Cloud Storage bucket 來儲存輸入資料,並建立另一個 bucket 來儲存結果:
gcloud storage buckets create gs://ALEX_INPUT_BUCKET_NAME \ gs://ALEX_RESULTS_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 iamcredentials.googleapis.com compute.googleapis.com confidentialcomputing.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
-
建立服務帳戶,供工作負載稍後用來解密資料:
gcloud iam service-accounts create BOLA_SERVICE_ACCOUNT_NAME
-
將
cloudkms.cryptoKeyDecrypter
角色授予服務帳戶,讓服務帳戶可以使用您剛建立的加密金鑰解密資料:gcloud kms keys add-iam-policy-binding \ projects/BOLA_PROJECT_ID/locations/global/keyRings/BOLA_KEYRING_NAME/cryptoKeys/BOLA_KEY_NAME \ --member=serviceAccount:BOLA_SERVICE_ACCOUNT_NAME@BOLA_PROJECT_ID.iam.gserviceaccount.com \ --role=roles/cloudkms.cryptoKeyDecrypter
-
建立 workload identity pool,然後使用
iam.workloadIdentityUser
角色將服務帳戶連結至該集區:gcloud iam workload-identity-pools create BOLA_POOL_NAME \ --location=global
gcloud iam service-accounts add-iam-policy-binding \ BOLA_SERVICE_ACCOUNT_NAME@BOLA_PROJECT_ID.iam.gserviceaccount.com \ --member="principalSet://iam.googleapis.com/projects/"$(gcloud projects describe BOLA_PROJECT_ID \ --format="value(projectNumber)")"/locations/global/workloadIdentityPools/BOLA_POOL_NAME/*" \ --role=roles/iam.workloadIdentityUser
-
建立 Cloud Storage bucket 來儲存輸入資料,並建立另一個 bucket 來儲存結果:
gcloud storage buckets create gs://BOLA_INPUT_BUCKET_NAME \ gs://BOLA_RESULTS_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
為工作負載建立服務帳戶
除了 Alex 和 Bola 設定的服務帳戶 (用於解密資料) 之外,還需要另一個服務帳戶來執行工作負載。由於服務帳戶可用於解密及處理機密資料,因此只有擁有者才能查看資料。
在本指南中,Bola 負責操作及執行工作負載,但任何人 (包括第三方) 都能擔任這些角色。
在 Bola 的專案中完成下列步驟,設定服務帳戶:
建立服務帳戶來執行工作負載:
gcloud iam service-accounts create WORKLOAD_SERVICE_ACCOUNT_NAME
授予 Bola
iam.serviceAccountUser
角色,模擬服務帳戶,這是必要步驟,因為他們稍後需要建立工作負載 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://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://ALEX_RESULTS_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_RESULTS_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
在 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
按一下「開啟編輯器」開啟 Cloud Shell 編輯器,然後建立名為
salary.go
的新檔案。將下列程式碼複製到檔案中,然後儲存檔案:// READ ME FIRST: Before compiling, customize the details in the USER VARIABLES // SECTION starting at line 30. package main import ( kms "cloud.google.com/go/kms/apiv1" "cloud.google.com/go/storage" "context" "fmt" "google.golang.org/api/option" kmspb "google.golang.org/genproto/googleapis/cloud/kms/v1" "io/ioutil" "strconv" "strings" "time" ) type collaborator struct { name string wipName string sa string keyName string inputBucket string inputFile string outputBucket string outputFile string } // ============================ // START USER VARIABLES SECTION // You need to customize this section, replacing each const's value with your // own. // To get a project number, use the following command, and substitute // <PROJECT_ID> for the data collaborator's project ID. // gcloud projects describe <PROJECT_ID> --format="value(projectNumber)" // Alex's values const collaborator1Name string = "Alex" // Alex's name const collaborator1EncryptedSalaryFileName string = "" // The name of Alex's encrypted salary file const collaborator1BucketInputName string = "" // The name of the storage bucket that contains Alex's encrypted salary file const collaborator1BucketOutputName string = "" // The name of the storage bucket to store Alex's results in const collaborator1BucketOutputFileName string = "" // The name of Alex's output file that contains the results const collaborator1KMSKeyringName string = "" // Alex's Key Management Service key ring const collaborator1KMSKeyName string = "" // Alex's Key Management Service key const collaborator1ProjectName string = "" // Alex's project ID const collaborator1ProjectNumber string = "" // Alex's project number const collaborator1PoolName string = "" // Alex's workload identity pool name const collaborator1ServiceAccountName string = "" // The name of Alex's service account that can decrypt their salary // Bola's values const collaborator2Name string = "Bola" // Bola's name const collaborator2EncryptedSalaryFileName string = "" // The name of Bola's encrypted salary file const collaborator2BucketInputName string = "" // The name of the storage bucket that contains Bola's encrypted salary file const collaborator2BucketOutputName string = "" // The name of the storage bucket to store Bola's results in const collaborator2BucketOutputFileName string = "" // The name of Bola's output file that contains the results const collaborator2KMSKeyringName string = "" // Bola's Key Management Service key ring const collaborator2KMSKeyName string = "" // Bola's Key Management Service key const collaborator2ProjectName string = "" // Bola's project ID const collaborator2ProjectNumber string = "" // Bola's project number const collaborator2PoolName string = "" // Bola's workload identity pool name const collaborator2ServiceAccountName string = "" // The name of Bola's service account that can decrypt their salary // END USER VARIABLES SECTION // ========================== var collaborators = [2]collaborator{ { collaborator1Name, "projects/" + collaborator1ProjectNumber + "/locations/global/workloadIdentityPools/" + collaborator1PoolName + "/providers/attestation-verifier", collaborator1ServiceAccountName + "@" + collaborator1ProjectName + ".iam.gserviceaccount.com", "projects/" + collaborator1ProjectName + "/locations/global/keyRings/" + collaborator1KMSKeyringName + "/cryptoKeys/" + collaborator1KMSKeyName, collaborator1BucketInputName, collaborator1EncryptedSalaryFileName, collaborator1BucketOutputName, collaborator1BucketOutputFileName, }, { collaborator2Name, "projects/" + collaborator2ProjectNumber + "/locations/global/workloadIdentityPools/" + collaborator2PoolName + "/providers/attestation-verifier", collaborator2ServiceAccountName + "@" + collaborator2ProjectName + ".iam.gserviceaccount.com", "projects/" + collaborator2ProjectName + "/locations/global/keyRings/" + collaborator2KMSKeyringName + "/cryptoKeys/" + collaborator2KMSKeyName, collaborator2BucketInputName, collaborator2EncryptedSalaryFileName, collaborator2BucketOutputName, collaborator2BucketOutputFileName, }, } 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" }, "service_account_impersonation_url": "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/%s:generateAccessToken" }` 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 = "earns same\n" } now := time.Now() for _, cw := range collaborators { outputWriter := storageClient.Bucket(cw.outputBucket).Object(fmt.Sprintf("%s-%d", cw.outputFile, 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.sa, 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, trustedServiceAccountEmail, wippro string, encryptedData []byte) ([]byte, error) { cc := fmt.Sprintf(credentialConfig, wippro, trustedServiceAccountEmail) 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 := ioutil.ReadAll(objectReader) if err != nil { return nil, err } return s, nil }
修改原始碼中的
USER VARIABLES SECTION
,並根據程式碼註解中的說明,將空白的const
值替換為相關資源名稱。如果您已按照本指南編輯預留位置變數 (如ALEX_PROJECT_ID
),下列程式碼範例會包含這些值,您可以複製並貼上,覆寫現有程式碼:// Alex's values const collaborator1Name string = "Alex" // Alex's name const collaborator1EncryptedSalaryFileName string = "ALEX_ENCRYPTED_SALARY_FILE" // The name of Alex's encrypted salary file const collaborator1BucketInputName string = "ALEX_INPUT_BUCKET_NAME" // The name of the storage bucket that contains Alex's encrypted salary file const collaborator1BucketOutputName string = "ALEX_RESULTS_BUCKET_NAME" // The name of the storage bucket to store Alex's results in const collaborator1BucketOutputFileName string = "ALEX_RESULTS_FILE_NAME" // The name of Alex's output file that contains the results const collaborator1KMSKeyringName string = "ALEX_KEYRING_NAME" // Alex's Key Management Service key ring const collaborator1KMSKeyName string = "ALEX_KEY_NAME" // Alex's Key Management Service key const collaborator1ProjectName string = "ALEX_PROJECT_ID" // Alex's project ID const collaborator1ProjectNumber string = "ALEX_PROJECT_NUMBER" // Alex's project number const collaborator1PoolName string = "ALEX_POOL_NAME" // Alex's workload identity pool name const collaborator1ServiceAccountName string = "ALEX_SERVICE_ACCOUNT_NAME" // The name of Alex's service account that can decrypt their salary // Bola's values const collaborator2Name string = "Bola" // Bola's name const collaborator2EncryptedSalaryFileName string = "BOLA_ENCRYPTED_SALARY_FILE" // The name of Bola's encrypted salary file const collaborator2BucketInputName string = "BOLA_INPUT_BUCKET_NAME" // The name of the storage bucket that contains Bola's encrypted salary file const collaborator2BucketOutputName string = "BOLA_RESULTS_BUCKET_NAME" // The name of the storage bucket to store Bola's results in const collaborator2BucketOutputFileName string = "BOLA_RESULTS_FILE_NAME" // The name of Bola's output file that contains the results const collaborator2KMSKeyringName string = "BOLA_KEYRING_NAME" // Bola's Key Management Service key ring const collaborator2KMSKeyName string = "BOLA_KEY_NAME" // Bola's Key Management Service key const collaborator2ProjectName string = "BOLA_PROJECT_ID" // Bola's project ID const collaborator2ProjectNumber string = "BOLA_PROJECT_NUMBER" // Bola's project number const collaborator2PoolName string = "BOLA_POOL_NAME" // Bola's workload identity pool name const collaborator2ServiceAccountName string = "BOLA_SERVICE_ACCOUNT_NAME" // The name of Bola's service account that can decrypt their salary
請務必一併更新 Alex 和 Bola 的專案編號。您可以使用下列指令擷取這些值:
gcloud projects describe PROJECT_ID --format="value(projectNumber)"
請確保所有當事人已閱讀並稽核原始碼。
依序點選「Terminal」(終端機) >「New Terminal」(新增終端機),在 Cloud Shell 編輯器中開啟終端機。
在終端機中輸入下列指令,設定 Go 環境:
go mod init salary 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
輸入下列指令,將原始碼編譯為靜態連結的二進位檔:
CGO_ENABLED=0 go build -trimpath
在 Cloud Shell 編輯器中建立名為
Dockerfile
的檔案,並在當中加入下列內容:FROM alpine:latest WORKDIR /test COPY salary /test ENTRYPOINT ["/test/salary"] CMD []
更新 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_NAME
從 Docker 推送回應中,將 Docker 映像檔的摘要 (包括
sha256:
前置字元) 複製到安全的地方,以供日後使用。請確保所有當事人稽核 Docker 映像檔,並驗證其是否值得信任,再授權使用。
授權工作負載
雙方核准工作負載後,Alex 和 Bola 必須將 Google Cloud 驗證新增為工作負載身分集區的提供者。這樣一來,附加至工作負載的服務帳戶就能模擬連結至集區的服務帳戶,並存取這些帳戶的資料,前提是符合特定屬性條件。也就是說,屬性條件會做為認證政策。
本指南使用的屬性條件如下:
正在執行的 Docker 映像檔摘要
執行工作負載的服務帳戶電子郵件地址
如果惡意行為人變更 Docker 映像檔,或將其他服務帳戶附加至工作負載,工作負載就無法存取 Alex 或 Bola 的資料。
如要查看可用的屬性條件,請參閱「認證聲明」。
如要為 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=assertion.sub" \ --attribute-condition="assertion.submods.container.image_digest == 'DOCKER_IMAGE_DIGEST' \ && 'WORKLOAD_SERVICE_ACCOUNT_NAME@BOLA_PROJECT_ID.iam.gserviceaccount.com' in assertion.google_service_accounts \ && 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 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=assertion.sub" \ --attribute-condition="assertion.submods.container.image_digest == 'DOCKER_IMAGE_DIGEST' \ && 'WORKLOAD_SERVICE_ACCOUNT_NAME@BOLA_PROJECT_ID.iam.gserviceaccount.com' in assertion.google_service_accounts \ && assertion.swname == 'CONFIDENTIAL_SPACE' \ && 'STABLE' in assertion.submods.confidential_space.support_attributes"
部署工作負載
在 Alex 和 Bola 的 workload identity pool 中新增提供者,並備妥必要資源後,工作負載運算子即可執行工作負載。
如要部署工作負載,請在 Bola 的專案中建立新的機密 VM 執行個體,並具備下列屬性:
AMD SEV 或 Intel TDX 機密 VM 執行個體的支援設定。
以 Confidential Space 映像檔為基礎的 OS。
已啟用安全啟動功能。
Alex 先前建立的 Docker 映像檔。
執行工作負載的服務帳戶。
在 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"
前往記錄檔瀏覽器,即可查看 Bola 專案的工作負載進度。
如要尋找機密空間記錄,請依下列記錄欄位 (如有) 篩選:
資源類型:VM 執行個體
執行個體 ID:VM 的執行個體 ID
記錄檔名稱:confidential-space-launcher
如要重新整理記錄,請按一下「跳到現在時間」
。工作負載完成後,VM 執行個體就會停止運作。如要變更加密薪資檔案並再次部署工作負載,只需啟動現有 VM 即可:
gcloud compute instances start WORKLOAD_VM_NAME --zone=us-west1-b
查看結果
工作負載順利完成後,Alex 和 Bola 都能在各自的結果 bucket 中查看結果:
切換至 Alex 的專案:
gcloud config set project ALEX_PROJECT_ID
列出結果 bucket 中的所有檔案:
gcloud storage ls gs://ALEX_RESULTS_BUCKET_NAME
然後讀取最新檔案:
gcloud storage cat gs://ALEX_RESULTS_BUCKET_NAME/ALEX_RESULTS_FILE_NAME
切換至 Bola 的專案:
gcloud config set project BOLA_PROJECT_ID
針對 Bola,列出結果 bucket 中的所有檔案:
gcloud storage ls gs://BOLA_RESULTS_BUCKET_NAME
然後讀取最新檔案:
gcloud storage cat gs://BOLA_RESULTS_BUCKET_NAME/BOLA_RESULTS_FILE_NAME
Alex 和 Bola 讀取檔案後,就能知道誰的薪水較高,但彼此不會看到對方的薪資。
偵錯工作負載
您可以使用「記錄探索器」排解問題,例如資源未正確設定,或供應商中的屬性條件與 Confidential Space 工作負載提出的聲明不符。
如要這麼做,請進行下列變更:
更新 Alex 和 Bola 的工作負載身分集區提供者,移除
support_attributes
聲明。您需要使用 Confidential Space 偵錯映像檔執行更深入的疑難排解,但該映像檔沒有可供驗證的支援屬性。使用 Confidential Space 偵錯映像檔建立工作負載 VM,並將 VM 中繼資料設為將
STDOUT
和STDERR
重新導向至 Cloud Logging,以擷取工作負載的所有輸出內容。
如要進行變更,請完成下列步驟:
切換至 Alex 的專案:
gcloud config set project ALEX_PROJECT_ID
更新 Alex 的提供者,移除
support_attributes
判斷: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=assertion.sub" \ --attribute-condition="assertion.submods.container.image_digest == 'DOCKER_IMAGE_DIGEST' \ && 'WORKLOAD_SERVICE_ACCOUNT_NAME@BOLA_PROJECT_ID.iam.gserviceaccount.com' in assertion.google_service_accounts \ && assertion.swname == 'CONFIDENTIAL_SPACE'"
切換至 Bola 的專案:
gcloud config set project BOLA_PROJECT_ID
更新 Bola 的供應商,移除
support_attributes
聲明: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=assertion.sub" \ --attribute-condition="assertion.submods.container.image_digest == 'DOCKER_IMAGE_DIGEST' \ && 'WORKLOAD_SERVICE_ACCOUNT_NAME@BOLA_PROJECT_ID.iam.gserviceaccount.com' in assertion.google_service_accounts \ && assertion.swname == 'CONFIDENTIAL_SPACE'"
使用 Confidential Space 偵錯映像檔建立新的 VM,並在其中繼資料中
tee-container-log-redirect
設為true
。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"
與正式版映像檔不同,偵錯映像檔會在工作負載完成後,讓 VM 繼續運作。也就是說,您可以使用 SSH 連線至 VM,繼續進行偵錯。
清除所用資源
如要移除本指南中建立的資源,請完成下列操作說明。
清除 Alex 的資源
切換至 Alex 的專案:
gcloud config set project ALEX_PROJECT_ID
刪除用於解密 Alex 資料的服務帳戶:
gcloud iam service-accounts delete \ ALEX_SERVICE_ACCOUNT_NAME@ALEX_PROJECT_ID.iam.gserviceaccount.com
刪除 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_RESULTS_BUCKET_NAME --recursive
刪除 Alex 的薪資檔案和 Go 程式碼:
rm ALEX_SALARY.txt \ ALEX_ENCRYPTED_SALARY_FILE \ salary.go salary \ go.mod go.sum
選用: 關閉 Alex 的專案。
清除 Bola 的資源
切換至 Bola 的專案:
gcloud config set project BOLA_PROJECT_ID
刪除工作負載 VM:
gcloud compute instances delete WORKLOAD_VM_NAME --zone=us-west1-b
刪除解密 Bola 資料的服務帳戶,以及執行工作負載的服務帳戶:
gcloud iam service-accounts delete \ BOLA_SERVICE_ACCOUNT_NAME@BOLA_PROJECT_ID.iam.gserviceaccount.com
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_RESULTS_BUCKET_NAME --recursive
刪除 Bola 的薪資檔案:
rm BOLA_SALARY.txt \ BOLA_ENCRYPTED_SALARY_FILE
選用: 關閉 Bola 的專案。