Neste guia, Alex e Bola querem descobrir quem tem o salário mais alto sem revelar números um ao outro. Eles decidem usar o Confidential Space para manter os dados confidenciais e concordam em assumir os seguintes papéis:
Alex: colaborador de dados, autor de carga de trabalho
Bola: colaborador de dados, operador de carga de trabalho
Essa organização foi projetada para manter as coisas o mais simples possível para este guia. No entanto, é possível que o autor e o operador da carga de trabalho sejam totalmente independentes dos colaboradores de dados, e você pode ter quantos colaboradores quiser.
Antes de começar
Neste guia, você vai conferir um cenário do Confidential Space usando uma única conta em uma organização com acesso a vários projetos. Assim, é possível conferir todo o processo. Em uma implantação de produção, os colaboradores, autores de carga de trabalho e operadores de carga de trabalho têm contas separadas e os próprios projetos contidos em organizações discretas, inacessíveis uns aos outros e mantendo os dados confidenciais separados.
O Confidential Space pode interagir com muitos serviços do Google Cloudpara produzir resultados, incluindo, entre outros:
Neste guia, usamos e pressupomos uma compreensão básica de todos esses recursos.
APIs necessárias
Você precisa ativar as seguintes APIs nos projetos especificados para concluir este guia.
Nome da API | Título da API | Ativar nestes projetos |
---|---|---|
cloudkms.googleapis.com |
Cloud KMS | Colaboradores de dados (projetos de Alex e Bola) |
iamcredentials.googleapis.com |
API IAM Service Account Credentials |
Colaboradores de dados (projeto do Alex) Neste guia, apenas o Alex precisa dessa API ativada. No entanto, se mais de duas partes estiverem envolvidas, a API Service Account Credentials do IAM precisa ser ativada no projeto de cada colaborador de dados que não hospeda a conta de serviço da carga de trabalho. |
artifactregistry.googleapis.com |
Artifact Registry | Autor de carga de trabalho (projeto do Alex) |
compute.googleapis.com |
Compute Engine | Operador de carga de trabalho (projeto do Bola) |
confidentialcomputing.googleapis.com |
Computação confidencial | Operador de carga de trabalho (projeto do Bola) |
Funções exigidas
Para ter as permissões necessárias para concluir este guia, peça ao administrador para conceder a você os papéis do IAM a seguir no projeto:
-
Administrador do Cloud KMS (
roles/cloudkms.admin
) para os colaboradores de dados (Alex e Bola). -
Administrador de pool de Identidade da carga de trabalho do IAM (
roles/iam.workloadIdentityPoolAdmin
) para os colaboradores de dados (Alex e Bola). -
Administrador do Service Usage (
roles/serviceusage.serviceUsageAdmin
) para os colaboradores de dados (Alex e Bola). -
Administrador da conta de serviço (
roles/iam.serviceAccountAdmin
) para os colaboradores de dados (Alex e Bola). -
Administrador de armazenamento (
roles/storage.admin
) para os colaboradores de dados (Alex e Bola) e para o operador de carga de trabalho (Bola). -
Administrador do Compute (
roles/compute.admin
) para o operador de carga de trabalho (Bola). -
Administrador de segurança (
roles/securityAdmin
) para o operador de carga de trabalho (Bola). -
Administrador do Artifact Registry (
roles/artifactregistry.admin
) para o autor da carga de trabalho (Alex).
Para mais informações sobre a concessão de papéis, consulte Gerenciar o acesso a projetos, pastas e organizações.
Também é possível conseguir as permissões necessárias por meio de papéis personalizados ou de outros papéis predefinidos.
Configurar recursos de colaboradores de dados
Alex e Bola precisam de projetos independentes que contenham os seguintes recursos:
Os dados confidenciais em si.
Uma chave para criptografar esses dados e mantê-los confidenciais.
Um bucket do Cloud Storage para armazenar os dados criptografados.
Uma conta de serviço que tenha acesso à chave de criptografia para descriptografar os dados confidenciais.
Um pool de identidades de carga de trabalho com essa conta de serviço conectada a ele. A carga de trabalho que processa os dados confidenciais usa o pool para representar a conta de serviço e recuperar os dados não criptografados.
Para começar, acesse o console do Google Cloud:
Acessar o Console do Google Cloud
Configurar os recursos do Alex
Para configurar os recursos do Alex, siga as instruções a seguir.
- Clique em Ativar o Cloud Shell.
-
No Cloud Shell, insira o comando a seguir para criar um projeto para o Alex, substituindo ALEX_PROJECT_ID por um nome de sua escolha:
gcloud projects create ALEX_PROJECT_ID
-
Alterne para o projeto recém-criado:
gcloud config set project ALEX_PROJECT_ID
-
Ative as APIs exigidas pelo Alex como colaborador de dados e autor da carga de trabalho, caso ainda não tenha feito isso:
gcloud services enable cloudkms.googleapis.com artifactregistry.googleapis.com iamcredentials.googleapis.com
-
Crie um keyring e uma chave de criptografia com o 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
-
Conceda ao Alex o papel
cloudkms.cryptoKeyEncrypter
para que ele possa usar a chave recém-criada para criptografar dados: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
-
Crie uma conta de serviço que será usada mais tarde pela carga de trabalho para descriptografar os dados:
gcloud iam service-accounts create ALEX_SERVICE_ACCOUNT_NAME
-
Conceda à conta de serviço o papel
cloudkms.cryptoKeyDecrypter
para que ela possa usar a chave de criptografia que você acabou de criar para descriptografar dados: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
-
Crie um pool de identidades da carga de trabalho e conecte a conta de serviço a ele com um papel de
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
-
Crie um bucket do Cloud Storage para os dados de entrada e outro para armazenar os resultados:
gcloud storage buckets create gs://ALEX_INPUT_BUCKET_NAME \ gs://ALEX_RESULTS_BUCKET_NAME
-
Crie um arquivo que contenha apenas o salário do Alex como número:
echo 123456 > ALEX_SALARY.txt
-
Criptografe o arquivo e faça upload dele para o bucket do Alex:
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
Configurar os recursos do Bola
Para configurar os recursos do Bola, siga as instruções a seguir.
-
No Cloud Shell, digite o seguinte comando para criar um projeto para o Bola, substituindo BOLA_PROJECT_ID por um nome de sua escolha:
gcloud projects create BOLA_PROJECT_ID
-
Alterne para o projeto recém-criado:
gcloud config set project BOLA_PROJECT_ID
-
Ative as APIs que o Bola exige como colaborador de dados e operador de carga de trabalho, caso ainda não tenha feito isso:
gcloud services enable cloudkms.googleapis.com compute.googleapis.com confidentialcomputing.googleapis.com
-
Crie um keyring e uma chave de criptografia com o 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
-
Conceda ao Bola o papel
cloudkms.cryptoKeyEncrypter
para que ele possa usar a chave recém-criada para criptografar dados: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
-
Crie uma conta de serviço que será usada mais tarde pela carga de trabalho para descriptografar os dados:
gcloud iam service-accounts create BOLA_SERVICE_ACCOUNT_NAME
-
Conceda à conta de serviço o papel
cloudkms.cryptoKeyDecrypter
para que ela possa usar a chave de criptografia que você acabou de criar para descriptografar dados: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
-
Crie um pool de identidades da carga de trabalho e conecte a conta de serviço a ele com um papel de
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
-
Crie um bucket do Cloud Storage para os dados de entrada e outro para armazenar os resultados:
gcloud storage buckets create gs://BOLA_INPUT_BUCKET_NAME \ gs://BOLA_RESULTS_BUCKET_NAME
-
Crie um arquivo que contenha apenas o salário de Bola como número:
echo 111111 > BOLA_SALARY.txt
-
Criptografe o arquivo e faça upload dele para o bucket do Bola:
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
Crie uma conta de serviço para a carga de trabalho
Além das contas de serviço que Alex e Bola configuraram para descriptografar os dados, outra conta de serviço é necessária para executar a carga de trabalho. Como as contas de serviço são usadas para descriptografar os dados confidenciais e processá-los, a visibilidade de dados é restrita aos proprietários deles.
Neste guia, Bola opera e executa a carga de trabalho, mas qualquer pessoa pode assumir esses papéis, incluindo terceiros.
Conclua as etapas a seguir no projeto do Bola para configurar a conta de serviço:
Crie uma conta de serviço para executar a carga de trabalho:
gcloud iam service-accounts create WORKLOAD_SERVICE_ACCOUNT_NAME
Conceda ao Bola o papel
iam.serviceAccountUser
para representar a conta de serviço, o que é necessário para que ele possa criar uma VM de carga de trabalho mais tarde: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
Conceda à conta de serviço o papel
confidentialcomputing.workloadUser
para que ela possa gerar um token de atestado: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
Conceda à conta de serviço o papel
logging.logWriter
para gravar registros no Cloud Logging para que seja possível verificar o progresso da carga de trabalho: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
Conceda à conta de serviço acesso de leitura aos buckets do Alex e do Bola que contêm os dados criptografados e acesso de gravação para cada um dos buckets de resultados:
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
Isso pressupõe que o usuário que concede o acesso tenha o papel de Administrador do Storage (
roles/storage.admin
) no projeto que contém o bucket do Cloud Storage que está sendo operado.
Criar a carga de trabalho
Neste guia, Alex fornece o código para a carga de trabalho e cria uma imagem do Docker para contê-la, mas qualquer pessoa pode assumir esses papéis, incluindo um terceiro.
Alex precisa criar os seguintes recursos para a carga de trabalho:
O código que executa a carga de trabalho.
Um repositório do Docker no Artifact Registry que pode ser acessado pela conta de serviço que executa a carga de trabalho.
Uma imagem do Docker que contém e executa o código da carga de trabalho.
Para criar e configurar os recursos, conclua as seguintes etapas no projeto do Alex:
Alterne para o projeto do Alex:
gcloud config set project ALEX_PROJECT_ID
Criar um repositório do Docker no Artifact Registry:
gcloud artifacts repositories create REPOSITORY_NAME \ --repository-format=docker \ --location=us
Conceda à conta de serviço que executará a carga de trabalho o papel de Leitor do Artifact Registry (
roles/artifactregistry.reader
) para que ela possa ler do repositório: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
Clique em Abrir editor para abrir o editor do Cloud Shell e crie um novo arquivo com o nome
salary.go
. Copie o código a seguir no arquivo e salve-o:// 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 }
Modifique o
USER VARIABLES SECTION
no código-fonte, substituindo os valoresconst
vazios pelos nomes de recursos relevantes, conforme descrito nos comentários do código. Se você tiver editado as variáveis de marcador comoALEX_PROJECT_ID
mostradas neste guia, os valores serão incluídos no exemplo de código a seguir, que pode ser copiado e colado no código existente:// 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
Atualize também os números dos dois projetos. É possível recuperá-las com o seguinte comando:
gcloud projects describe PROJECT_ID --format="value(projectNumber)"
Verifique se todas as partes leem e auditam o código-fonte.
Clique em Terminal > Novo terminal para abrir um terminal no Cloud Shell Editor.
Digite os seguintes comandos no terminal para configurar o ambiente do 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
Digite o comando a seguir para compilar o código-fonte em um binário vinculado estaticamente:
CGO_ENABLED=0 go build -trimpath
Crie um arquivo chamado
Dockerfile
no Editor do Cloud Shell com o seguinte conteúdo:FROM alpine:latest WORKDIR /test COPY salary /test ENTRYPOINT ["/test/salary"] CMD []
Atualize suas credenciais do Docker para incluir o nome de domínio
us-docker.pkg.dev
:gcloud auth configure-docker us-docker.pkg.dev
Crie uma imagem do Docker a partir de
Dockerfile
. Para isso, digite o seguinte comando no terminal:docker build -t \ us-docker.pkg.dev/ALEX_PROJECT_ID/REPOSITORY_NAME/WORKLOAD_CONTAINER_NAME:latest .
Envie a imagem do Docker para o Artifact Registry:
docker push \ us-docker.pkg.dev/ALEX_PROJECT_ID/REPOSITORY_NAME/WORKLOAD_CONTAINER_NAME
Na resposta push do Docker, copie o resumo da imagem do Docker (incluindo o prefixo
sha256:
) em um local seguro para uso posterior.Certifique-se de que todas as partes auditem a imagem do Docker e verifique se ela é confiável antes de autorizar o uso.
Autorizar a carga de trabalho
Com a carga de trabalho aprovada por ambas as partes, Alex e Bola precisam adicionar o serviço do verificador de atestado do Confidential Space como um provedor aos pools de identidade da carga de trabalho. Isso permite que a conta de serviço anexada à carga de trabalho represente as contas de serviço conectadas aos pools e acesse os dados deles, desde que determinadas condições de atributo sejam atendidas. Isso significa que as condições do atributo atuam como políticas de atestado.
As condições dos atributos usadas neste guia são as seguintes:
O resumo da imagem do Docker em execução
O endereço de e-mail da conta de serviço que executa a carga de trabalho
Se uma pessoa mal-intencionada mudar a imagem do Docker ou uma conta de serviço diferente estiver anexada à carga de trabalho, ela não terá acesso aos dados do Alex ou do Bola.
Para conferir as condições de atributo disponíveis, consulte Afirmações de atestado.
Para configurar os provedores do Alex e do Bola com as condições necessárias, siga estas etapas:
Digite o seguinte comando para criar o provedor do 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"
Mude para o projeto do Bola:
gcloud config set project BOLA_PROJECT_ID
Digite o seguinte comando para criar o provedor do 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"
Implantar a carga de trabalho
Com os provedores adicionados aos pools de identidade da carga de trabalho do Alex e do Bola e os recursos necessários ativos, é hora do operador da carga de trabalho executar a carga de trabalho.
Para implantar a carga de trabalho, crie uma nova instância de VM confidencial no projeto do Bola que tenha as seguintes propriedades:
Uma configuração com suporte para uma instância de VM confidencial do AMD SEV ou do Intel TDX.
Um SO baseado na imagem do Confidential Space.
Inicialização segura ativada.
A imagem do Docker anexada que Alex criou anteriormente.
A conta de serviço anexada que executa a carga de trabalho.
Para implantar a carga de trabalho, digite o seguinte comando no Cloud Shell do Bola:
gcloud compute instances create WORKLOAD_VM_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 \
--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"
É possível ver o progresso da carga de trabalho no projeto do Bola acessando o Explorador de registros.
Acessar o "Explorador de registros"
Para encontrar os registros do Confidential Space, filtre pelos seguintes campos de registro, se estiverem disponíveis:
Tipo de recurso: instância de VM
ID da instância: o ID da instância da VM
Nome do registro: confidencial-space-launcherer
Para atualizar o registro, clique em Ir para momento atual.
Quando a carga de trabalho é concluída, a instância de VM é interrompida. Se você quiser mudar os arquivos salariais criptografados e implantar a carga de trabalho novamente, basta iniciar a VM atual:
gcloud compute instances start WORKLOAD_VM_NAME --zone=us-west1-b
Depurar a carga de trabalho
Também é possível usar o explorador de registros para resolver problemas, como recursos que não estão sendo configurados corretamente ou atribuir condições em provedores que não correspondam às declarações feitas pela carga de trabalho do Confidential Space.
Para isso, faça as seguintes alterações:
Atualize os provedores de pools de identidade da carga de trabalho de Alex e Bola para remover a declaração
support_attributes
. Você precisa usar a imagem de depuração do Confidential Space para realizar uma solução de problemas mais detalhada, e essa imagem não tem atributos de suporte para verificar.Crie a VM da carga de trabalho usando a imagem de depuração do Confidential Space e defina os metadados da VM para redirecionar
STDOUT
eSTDERR
ao Cloud Logging e capturar toda a saída da carga de trabalho.
Para fazer as alterações, siga estas etapas:
Alterne para o projeto do Alex:
gcloud config set project ALEX_PROJECT_ID
Atualize o provedor do Alex para remover a declaração
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'"
Mude para o projeto do Bola:
gcloud config set project BOLA_PROJECT_ID
Atualize o provedor de Bola para remover a declaração
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'"
Crie uma nova VM com a imagem de depuração do espaço confidencial e a
tee-container-log-redirect
definida comotrue
nos metadados.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"
Ao contrário da imagem de produção, a imagem de depuração mantém a VM em execução após a conclusão da carga de trabalho. Isso significa que é possível usar o SSH para se conectar à VM e continuar a depuração.
Ver os resultados
Depois que a carga de trabalho for concluída, Alex e Bola poderão ver os resultados nos respectivos buckets:
Alterne para o projeto do Alex:
gcloud config set project ALEX_PROJECT_ID
Liste todos os arquivos no bucket de resultados:
gcloud storage ls gs://ALEX_RESULTS_BUCKET_NAME
Em seguida, leia o arquivo mais recente:
gcloud storage cat gs://ALEX_RESULTS_BUCKET_NAME/ALEX_RESULTS_FILE_NAME
Mude para o projeto do Bola:
gcloud config set project BOLA_PROJECT_ID
Para o Bola, liste todos os arquivos no bucket de resultados:
gcloud storage ls gs://BOLA_RESULTS_BUCKET_NAME
Em seguida, leia o arquivo mais recente:
gcloud storage cat gs://BOLA_RESULTS_BUCKET_NAME/BOLA_RESULTS_FILE_NAME
Ao ler os arquivos, Alex e Bola descobrem quem ganha mais sem nunca revelar o salário um para o outro.
Limpeza
Para remover os recursos criados neste guia, siga as instruções a seguir.
Limpar os recursos do Alex
Alterne para o projeto do Alex:
gcloud config set project ALEX_PROJECT_ID
Exclua a conta de serviço que descriptografa os dados do Alex:
gcloud iam service-accounts delete \ ALEX_SERVICE_ACCOUNT_NAME@ALEX_PROJECT_ID.iam.gserviceaccount.com
Exclua o pool de identidades de carga de trabalho do Alex:
gcloud iam workload-identity-pools delete ALEX_POOL_NAME \ --location=global
Exclua os buckets do Cloud Storage do Alex:
gcloud storage rm gs://ALEX_INPUT_BUCKET_NAME \ gs://ALEX_RESULTS_BUCKET_NAME --recursive
Exclua os arquivos de salário do Alex e o código do Go:
rm ALEX_SALARY.txt \ ALEX_ENCRYPTED_SALARY_FILE \ salary.go salary \ go.mod go.sum
Opcional: desative ou destrua a chave do Cloud Key Management Service do Alex.
Opcional: encerre o projeto do Alex.
Limpar os recursos do Bola
Mude para o projeto do Bola:
gcloud config set project BOLA_PROJECT_ID
Exclua a VM de carga de trabalho:
gcloud compute instances delete WORKLOAD_VM_NAME --zone=us-west1-b
Exclua a conta de serviço que descriptografa os dados do Bola e a conta que executa a carga de trabalho:
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
Exclua o pool de identidades da carga de trabalho do Bola:
gcloud iam workload-identity-pools delete BOLA_POOL_NAME \ --location=global
Exclua os buckets do Cloud Storage do Bola:
gcloud storage rm gs://BOLA_INPUT_BUCKET_NAME \ gs://BOLA_RESULTS_BUCKET_NAME --recursive
Exclua os arquivos de salário do Bola:
rm BOLA_SALARY.txt \ BOLA_ENCRYPTED_SALARY_FILE
Opcional: desative ou destrua a chave do Cloud Key Management Service do Bola.
Opcional: encerre o projeto do Bola.