이 가이드에서 알렉스와 볼라는 서로 숫자를 공개하지 않으면서 최고 급여자를 찾아내려고 합니다. 이들은 자신의 데이터를 기밀로 유지하기 위해 Confidential Space를 사용하기로 결정하고 다음 역할을 각자 수행하기로 협의합니다.
알렉스: 데이터 공동작업자, 워크로드 작성자
볼라: 데이터 공동작업자, 워크로드 운영자
이 구성은 이 가이드에서 최대한 간단하게 설명하기 위해 설계되었습니다. 하지만 워크로드 작성자와 운영자가 데이터 공동작업자와 완전히 독립적일 수 있고, 원하는 만큼 공동작업자를 지정할 수 있습니다.
시작하기 전에
이 가이드에서는 전체 프로세스를 경험할 수 있도록 여러 프로젝트에 액세스할 수 있는 단일 조직의 단일 계정을 사용하는 Confidential Space 시나리오를 보여줍니다. 프로덕션 배포에서 공동작업자, 워크로드 작성자, 워크로드 운영자는 각자 고유한 계정을 가지며, 각자 자신의 프로젝트가 개별 조직에 포함되어 있으므로, 서로 액세스할 수 없고, 각자의 기밀 데이터가 별도로 유지됩니다.
Confidential Space는 많은 Google Cloud서비스와 상호작용을 통해 결과를 생성하지만 다음을 포함하되 이에 국한되지 않습니다.
이 가이드에서는 이 모든 기능을 기본적으로 이해하고 있다고 가정합니다.
필요한 역할
이 가이드를 완료하는 데 필요한 권한을 얻으려면 관리자에게 프로젝트에 대한 다음 IAM 역할을 부여해 달라고 요청하세요.
-
데이터 공동작업자(알렉스 및 볼라)에 대한 Cloud KMS 관리자(
roles/cloudkms.admin) -
데이터 공동작업자(알렉스 및 볼라)에 대한 IAM 워크로드 아이덴티티 풀 관리자(
roles/iam.workloadIdentityPoolAdmin) -
데이터 공동작업자(알렉스 및 볼라)에 대한 서비스 사용량 관리자(
roles/serviceusage.serviceUsageAdmin) -
데이터 공동작업자(알렉스 및 볼라) 및 워크로드 운영자(볼라)에 대한 스토리지 관리자(
roles/storage.admin) -
워크로드 운영자 (볼라)에 대한 서비스 계정 관리자 (
roles/iam.serviceAccountAdmin) -
워크로드 운영자(볼라)에 대한 컴퓨팅 관리자(
roles/compute.admin) -
워크로드 운영자(볼라)에 대한 보안 관리자(
roles/securityAdmin) -
워크로드 작성자(알렉스)에 대한 Artifact Registry 관리자(
roles/artifactregistry.admin)
역할 부여에 대한 자세한 내용은 프로젝트, 폴더, 조직에 대한 액세스 관리를 참조하세요.
커스텀 역할이나 다른 사전 정의된 역할을 통해 필요한 권한을 얻을 수도 있습니다.
데이터 공동작업자 리소스 설정
알렉스와 볼라 모두 다음 리소스가 포함된 독립 실행형 프로젝트가 필요합니다.
기밀 데이터 자체
데이터를 암호화하고 기밀로 유지하기 위한 암호화 키
암호화된 데이터를 저장할 Cloud Storage 버킷
워크로드 아이덴티티 풀입니다. 기밀 데이터를 처리하는 워크로드는 풀을 사용하여 비공개 데이터에 액세스하고 이를 복호화합니다.
시작하려면 Google Cloud 콘솔로 이동합니다.
알렉스의 리소스 설정
알렉스를 위한 리소스를 설정하려면 다음 안내를 완료하세요.
- Cloud Shell 활성화를 클릭합니다.
-
Cloud Shell에서 다음 명령어를 입력하여 알렉스를 위한 프로젝트를 만듭니다. ALEX_PROJECT_ID는 선택한 이름으로 바꿉니다.
gcloud projects create ALEX_PROJECT_ID -
새로 만든 프로젝트로 전환합니다.
gcloud config set project ALEX_PROJECT_ID -
데이터 공동작업자 및 워크로드 작성자로서 알렉스에게 필요한 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 -
새로 만든 암호화 키를 사용하여 데이터를 암호화할 수 있도록 알렉스에게
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 workload-identity-pools create ALEX_POOL_NAME \ --location=global -
입력 데이터에 대해 Cloud Storage 버킷을 하나 만들고 결과를 저장할 버킷을 하나 더 만듭니다.
gcloud storage buckets create gs://ALEX_INPUT_BUCKET_NAME \ gs://ALEX_OUTPUT_BUCKET_NAME -
알렉스 급여만 숫자로 포함하는 파일을 만듭니다.
echo 123456 > ALEX_SALARY.txt -
파일을 암호화하고 이를 알렉스의 버킷으로 업로드합니다.
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
볼라의 리소스 설정
볼라를 위한 리소스를 설정하려면 다음 안내를 완료하세요.
-
Cloud Shell에서 다음 명령어를 입력하여 볼라를 위한 프로젝트를 만듭니다. BOLA_PROJECT_ID는 선택한 이름으로 바꿉니다.
gcloud projects create BOLA_PROJECT_ID -
새로 만든 프로젝트로 전환합니다.
gcloud config set project BOLA_PROJECT_ID -
데이터 공동작업자 및 워크로드 운영자로서 볼라에게 필요한 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 -
새로 생성된 암호화 키를 사용하여 데이터를 암호화할 수 있도록 볼라에게
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 workload-identity-pools create BOLA_POOL_NAME \ --location=global -
입력 데이터에 대해 Cloud Storage 버킷을 하나 만들고 결과를 저장할 버킷을 하나 더 만듭니다.
gcloud storage buckets create gs://BOLA_INPUT_BUCKET_NAME \ gs://BOLA_OUTPUT_BUCKET_NAME -
볼라 급여만 숫자로 포함하는 파일을 만듭니다.
echo 111111 > BOLA_SALARY.txt -
파일을 암호화하고 이를 볼라의 버킷으로 업로드합니다.
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
워크로드에 대한 서비스 계정 만들기
이 가이드에서는 볼라가 워크로드를 운영하고 실행하지만 제3자를 포함하여 누구나 이 역할을 가질 수 있습니다. 작업 부하를 실행하기 위해 볼라가 만드는 VM 인스턴스에는 증명 토큰을 생성하고, 로그를 쓰고, 알렉스와 볼라의 암호화된 데이터를 읽고, 특정 Cloud Storage 버킷에 결과를 쓸 수 있는 권한이 있는 서비스 계정이 연결되어 있습니다.
서비스 계정을 설정하기 위해 볼라의 프로젝트에서 다음 단계를 수행합니다.
워크로드 실행을 위해 서비스 계정을 만듭니다.
gcloud iam service-accounts create WORKLOAD_SERVICE_ACCOUNT_NAMEBola에게
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워크로드 진행 상황을 확인할 수 있도록 Cloud Logging에 로그를 기록하기 위해 서비스 계정에
logging.logWriter역할을 부여합니다.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암호화된 데이터가 포함된 알렉스 및 볼라의 버킷에 모두 서비스 계정 읽기 액세스 권한을 부여하고 각 결과 버킷에 쓰기 액세스 권한을 부여합니다.
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) 역할이 있다고 가정합니다.
워크로드 만들기
이 가이드에서 알렉스가 워크로드에 대한 코드를 제공하고 이를 포함하도록 Docker 이미지를 빌드하지만, 제3자를 포함하여 누구나 이 역할을 가질 수 있습니다.
알렉스가 워크로드에 대해 다음 리소스를 만들어야 합니다.
워크로드를 수행하는 코드
워크로드를 실행하는 서비스 계정이 액세스할 수 있는 Artifact Registry의 Docker 저장소
워크로드 코드를 포함하고 실행하는 Docker 이미지
리소스를 만들고 설정하려면 알렉스의 프로젝트에서 다음 단계를 수행합니다.
알렉스의 프로젝트로 전환합니다.
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 코드를 컴파일한 다음 컴파일된 코드 버전을 최종 워크로드 컨테이너에 복사합니다. 또한 해당 워크로드 컨테이너에서 특정 환경 변수를 사용할 수 있습니다. 이러한 환경 변수의 값은 나중에 워크로드가 작동하는 데 필요한 특정 리소스에 매핑됩니다.터미널 열기를 클릭하여 Cloud Shell로 다시 전환하거나 보기 메뉴에서 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.readerus-docker.pkg.dev도메인 이름을 포함하도록 Docker 사용자 인증 정보를 업로드합니다.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" .Artifact Registry에 Docker 이미지를 푸시합니다.
docker push \ us-docker.pkg.dev/ALEX_PROJECT_ID/REPOSITORY_NAME/WORKLOAD_CONTAINER_NAMEDocker 푸시 응답에는 이미지의 SHA256 다이제스트가 나열되어 있으며, 이는 나중에 워크로드를 승인하는 데 필요합니다. 다이제스트는 다음 예시와 비슷합니다.
sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855이미지 다이제스트 (
sha256:접두사 포함)를 참조할 수 있는 위치에 복사합니다. 다음 코드 샘플에 다이제스트를 입력하여 값이 필요한 이 가이드의 나머지 코드 샘플을 미리 채울 수도 있습니다.WORKLOAD_CONTAINER_IMAGE_DIGEST사용을 승인하기 전에 모든 당사자가 Docker 이미지를 감사하고 신뢰성을 확인하도록 해야 합니다.
워크로드 승인
양 당사자가 워크로드를 승인함에 따라 알렉스와 볼라는 Google Cloud 증명을 제공자로서 워크로드 아이덴티티 풀에 추가해야 합니다. 공급자는 사용할 증명 서비스와 워크로드가 알렉스 또는 볼라의 데이터에 대해 작동하도록 허용되기 위해 일치해야 하는 속성을 지정합니다. 악의적인 행위자가 Docker 이미지를 변경하거나 다른 측정된 속성을 변경하면 워크로드의 액세스가 거부됩니다.
이 가이드에서는 속성 매핑을 사용하여 이미지 다이제스트를 기반으로 워크로드에 직접 리소스 액세스 권한을 제공합니다. 하지만 다른 상황에서는 서비스 계정 가장을 사용하여 리소스에 액세스하는 것이 더 나을 수 있습니다. 자세한 내용은 외부 워크로드 액세스를 참고하세요.
필요한 조건을 사용해서 알렉스와 볼라에 대해 공급자를 설정하려면 다음 단계를 완료합니다.
다음 명령어를 입력하여 알렉스에 대한 공급자를 만듭니다.
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)"알렉스 공급업체에서 정의한 제휴 ID에
cloudkms.cryptoKeyDecrypter역할을 부여하고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볼라의 프로젝트로 전환합니다.
gcloud config set project BOLA_PROJECT_ID다음 명령어를 입력하여 볼라에 대해 공급자를 만듭니다.
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'"다음 명령어를 위해 볼라의 프로젝트 번호를 가져옵니다.
gcloud projects describe BOLA_PROJECT_ID --format="value(projectNumber)"볼라의 공급업체에서 정의한 제휴 ID에
cloudkms.cryptoKeyDecrypter역할을 부여하여 지정된 다이제스트가 있는 워크로드 컨테이너만 KMS 키를 복호화할 수 있도록image_digest속성을 지정합니다.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
워크로드 테스트
알렉스와 볼라의 워크로드 아이덴티티 풀에 공급자가 추가되었고 필요한 리소스가 배치되었으면 워크로드 운영자가 워크로드를 테스트할 시간입니다.
워크로드를 테스트하기 위해 볼라의 프로젝트에서 다음 속성이 포함된 새 컨피덴셜 VM 인스턴스를 만듭니다.
AMD SEV, Intel TDX 또는 NVIDIA 컨피덴셜 컴퓨팅 (미리보기)이 적용된 Intel TDX를 사용하는 지원되는 구성
사용 설정된 보안 부팅
문제 해결을 더 쉽게 할 수 있도록 Confidential Space 디버그 이미지를 기반으로 하는 OS
알렉스가 이전에 만든 Docker 이미지에 대한 참조 이는 Confidential Space 이미지 위에 로드됩니다.
STDOUT및STDERR이 Cloud Logging과 직렬 콘솔로 리디렉션됩니다.워크로드가 환경 변수로 설정해야 하는 리소스입니다.
볼라의 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"
진행률 보기
로그 탐색기로 이동하여 볼라의 프로젝트에서 워크로드 진행 상황을 확인할 수 있습니다.
Confidential Space 로그 항목만 표시하려면 사용 가능한 경우 다음 로그 필드를 기준으로 필터링합니다.
리소스 유형: VM 인스턴스
인스턴스 ID: VM의 인스턴스 ID
로그 이름: confidential-space-launcher
로그를 새로고침하려면 현재 시점으로 이동을 클릭합니다. 이전 결과로 스크롤한 다음 로그 끝으로 다시 스크롤하여 최신 항목을 로드할 수도 있습니다.
결과 보기
워크로드 작업이 종료되고 0가 반환되면 오류가 발생하지 않았으며 Alex와 Bola의 출력 버킷에서 출력을 확인할 시간입니다.
알렉스의 프로젝트로 전환합니다.
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파일이 없으면 워크로드를 디버그해야 합니다.
볼라의 프로젝트로 전환합니다.
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
이 파일을 읽으면 알렉스와 볼라 모두 자신의 급여를 상대방에게 공개하지 않으면서도 급여가 더 높은 사람이 누구인지 확인할 수 있습니다.
워크로드 디버그 및 다시 시작
컨피덴셜 스페이스 환경에는 여러 부분이 있으며 워크로드가 실패하도록 잘못 구성된 부분이 있을 수 있습니다.
프로덕션 Confidential Space 이미지와 달리 디버그 이미지는 워크로드가 완료된 후에도 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 이미지가 워크로드에 사용되는지 확인하기 위해 제공업체에 support_attributes 어설션을 추가해야 합니다.
알렉스의 프로젝트로 전환합니다.
gcloud config set project ALEX_PROJECT_ID다음 명령어를 입력하여 알렉스의 공급자를 업데이트합니다.
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"볼라의 프로젝트로 전환합니다.
gcloud config set project BOLA_PROJECT_ID다음 명령어를 입력하여 볼라의 공급자를 업데이트합니다.
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에 표시됩니다.
볼라의 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
삭제
이 가이드에서 만든 리소스를 삭제하려면 다음 안내를 따르세요.
알렉스의 리소스 삭제
알렉스의 프로젝트로 전환합니다.
gcloud config set project ALEX_PROJECT_ID알렉스의 워크로드 아이덴티티 풀을 삭제합니다.
gcloud iam workload-identity-pools delete ALEX_POOL_NAME \ --location=global알렉스의 Cloud Storage 버킷을 삭제합니다.
gcloud storage rm gs://ALEX_INPUT_BUCKET_NAME \ gs://ALEX_OUTPUT_BUCKET_NAME --recursive알렉스의 급여 파일, Go 코드,
Dockerfile를 삭제합니다.rm ALEX_SALARY.txt \ ALEX_ENCRYPTED_SALARY_FILE \ salary.go \ Dockerfile선택사항: 알렉스의 프로젝트를 종료합니다.
볼라의 리소스 삭제
볼라의 프로젝트로 전환합니다.
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볼라의 워크로드 아이덴티티 풀을 삭제합니다.
gcloud iam workload-identity-pools delete BOLA_POOL_NAME \ --location=global볼라의 Cloud Storage 버킷을 삭제합니다.
gcloud storage rm gs://BOLA_INPUT_BUCKET_NAME \ gs://BOLA_OUTPUT_BUCKET_NAME --recursive볼라의 급여 파일을 삭제합니다.
rm BOLA_SALARY.txt \ BOLA_ENCRYPTED_SALARY_FILE선택사항: 볼라의 프로젝트를 종료합니다.