Crie o seu primeiro ambiente de espaço confidencial

Neste guia, o Alex e a Bola querem saber quem tem o salário mais elevado sem revelarem os números um ao outro. Decidem usar o espaço confidencial para manter os respetivos dados confidenciais e aceitam assumir as seguintes funções:

  • Alex: colaborador de dados, autor de cargas de trabalho

  • Bola: colaborador de dados, operador de carga de trabalho

Esta disposição foi concebida 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 pode ter quantos colaboradores quiser.

Antes de começar

Este guia demonstra um cenário do espaço confidencial com uma única conta numa única organização com acesso a vários projetos, para que possa experimentar todo o processo. Numa implementação de produção, os colaboradores, os autores de cargas de trabalho e os operadores de cargas de trabalho têm contas separadas e os seus próprios projetos contidos em organizações discretas, inacessíveis entre si e mantendo os respetivos dados confidenciais separados.

O espaço confidencial pode interagir com muitos dos serviços do Google Cloudpara produzir os respetivos resultados, incluindo, entre outros:

Este guia usa e pressupõe uma compreensão básica de todas estas funcionalidades.

Funções necessárias

Para receber as autorizações de que precisa para concluir este guia, peça ao seu administrador para lhe conceder as seguintes funções de IAM no projeto:

  • Administrador do Cloud KMS (roles/cloudkms.admin) para os colaboradores de dados (Alex e Bola).
  • Administrador do Workload Identity Pool do IAM (roles/iam.workloadIdentityPoolAdmin) para os colaboradores de dados (Alex e Bola).
  • Administrador de utilização de serviços (roles/serviceusage.serviceUsageAdmin) para os colaboradores de dados (Alex e Bola).
  • Administrador de armazenamento (roles/storage.admin) para os colaboradores de dados (Alex e Bola) e o operador da carga de trabalho (Bola).
  • Administrador da conta de serviço (roles/iam.serviceAccountAdmin) para o operador da carga de trabalho (Bola).
  • Administrador de computação (roles/compute.admin) para o operador de cargas de trabalho (Bola).
  • Administrador de segurança (roles/securityAdmin) para o operador da 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 atribuição de funções, consulte o artigo Faça a gestão do acesso a projetos, pastas e organizações.

Também pode conseguir as autorizações necessárias através de funções personalizadas ou outras funções predefinidas.

Configure recursos de colaborador de dados

O Alex e a Bola precisam de projetos independentes que contenham os seguintes recursos:

  • Os próprios dados confidenciais.

  • Uma chave de encriptação para encriptar esses dados e mantê-los confidenciais.

  • Um contentor do Cloud Storage para armazenar os dados encriptados.

  • Um Workload Identity Pool. A carga de trabalho que processa os dados confidenciais usa o conjunto para aceder aos dados privados e desencriptá-los.

Para começar, aceda à Google Cloud consola:

Aceda à Google Cloud consola

Configure os recursos do Alex

Para configurar os recursos para o Alex, conclua as seguintes instruções.

  1. Clique em Ativar Cloud Shell.
  2. No Cloud Shell, introduza o seguinte comando para criar um projeto para Alex, substituindo ALEX_PROJECT_ID por um nome à sua escolha:

    gcloud projects create ALEX_PROJECT_ID
  3. Mude para o projeto recém-criado:

    gcloud config set project ALEX_PROJECT_ID
  4. Ative as APIs de que o Alex precisa como colaborador de dados e autor de cargas de trabalho:

    gcloud services enable \
        artifactregistry.googleapis.com \
        cloudkms.googleapis.com \
        iamcredentials.googleapis.com
  5. Crie um conjunto de chaves e uma chave de encriptação 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
  6. Conceda à Alex a função cloudkms.cryptoKeyEncrypter para que possa usar a chave de encriptação criada recentemente para encriptar 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
  7. Crie o Workload Identity Pool do Alex:

    gcloud iam workload-identity-pools create ALEX_POOL_NAME \
        --location=global
  8. Crie um contentor 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_OUTPUT_BUCKET_NAME
  9. Crie um ficheiro que contenha apenas o salário de Alex como um número:

    echo 123456 > ALEX_SALARY.txt
  10. Encriptar o ficheiro e, de seguida, carregá-lo para o contentor de 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

Configure os recursos da Bola

Para configurar os recursos para o Bola, conclua as seguintes instruções.

  1. No Cloud Shell, introduza o seguinte comando para criar um projeto para a Bola, substituindo BOLA_PROJECT_ID por um nome à sua escolha:

    gcloud projects create BOLA_PROJECT_ID
  2. Mude para o projeto recém-criado:

    gcloud config set project BOLA_PROJECT_ID
  3. Ative as APIs que a Bola requer como colaborador de dados e operador de carga de trabalho:

    gcloud services enable \
        cloudkms.googleapis.com \
        compute.googleapis.com \
        confidentialcomputing.googleapis.com \
        iamcredentials.googleapis.com
  4. Crie um conjunto de chaves e uma chave de encriptação 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
  5. Conceda à Bola a função cloudkms.cryptoKeyEncrypter para que possa usar a chave de encriptação criada recentemente para encriptar 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
  6. Crie o Workload Identity Pool da Bola:

    gcloud iam workload-identity-pools create BOLA_POOL_NAME \
        --location=global
  7. Crie um contentor 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_OUTPUT_BUCKET_NAME
  8. Crie um ficheiro que contenha apenas o salário de Bola como um número:

    echo 111111 > BOLA_SALARY.txt
  9. Encriptar o ficheiro e, em seguida, carregá-lo para o contentor de 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

Neste guia, a Bola opera e executa a carga de trabalho, mas qualquer pessoa pode assumir estas funções, incluindo um terceiro. A instância de VM que o Bola cria para executar a carga de trabalho tem uma conta de serviço anexada, que tem autorização para gerar tokens de atestação, escrever registos, ler os dados encriptados do Alex e do Bola e escrever resultados em contentores específicos do Cloud Storage.

Conclua os passos seguintes no projeto de Bola para configurar a conta de serviço:

  1. Crie uma conta de serviço para executar a carga de trabalho:

    gcloud iam service-accounts create WORKLOAD_SERVICE_ACCOUNT_NAME
    
  2. Conceda à Bola a função iam.serviceAccountUser para que possa anexar a conta de serviço à VM da 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
    
  3. Conceda à conta de serviço a função confidentialcomputing.workloadUser para que possa gerar um token de atestação:

    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
    
  4. Conceda à conta de serviço a função logging.logWriter para escrever registos no Cloud Logging, para que possa 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
    
  5. Conceda à conta de serviço acesso de leitura aos contentores de Alex e Bola que contêm os respetivos dados encriptados e acesso de escrita aos contentores de resultados de cada um:

    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
    

    Isto pressupõe que o utilizador que concede o acesso tem a função de administrador do armazenamento (roles/storage.admin) para o projeto que contém o contentor do Cloud Storage no qual está a ser realizada a operação.

Crie a carga de trabalho

Neste guia, o Alex fornece o código para a carga de trabalho e cria uma imagem do Docker para a conter, mas qualquer pessoa pode assumir estas funções, incluindo terceiros.

O Alex tem de criar os seguintes recursos para a carga de trabalho:

  • O código que executa a carga de trabalho.

  • Um repositório Docker no Artifact Registry ao qual a conta de serviço que executa a carga de trabalho tem acesso.

  • Uma imagem do Docker que contém e executa o código da carga de trabalho.

Para criar e configurar os recursos, conclua os seguintes passos no projeto de Alex:

  1. Mudar para o projeto do Alex:

    gcloud config set project ALEX_PROJECT_ID
    
  2. Clique em Abrir editor para abrir o editor do Cloud Shell e, de seguida, crie um novo ficheiro denominado salary.go. Copie o código seguinte para o ficheiro e, de seguida, guarde-o:

    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
    }
    
  3. Certifique-se de que todas as partes leem e auditam o código-fonte.

  4. Crie um ficheiro denominado Dockerfile no editor do Cloud Shell com o seguinte conteúdo:

    # 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"
    

    Esta Dockerfile usa uma compilação de várias fases para compilar primeiro o código Go e, em seguida, copia a versão compilada desse código para o contentor de carga de trabalho final. Também permite que sejam usadas variáveis de ambiente específicas nesse contentor de carga de trabalho. Os valores destas variáveis de ambiente são mapeados posteriormente para os recursos específicos nos quais a carga de trabalho tem de operar.

  5. Clique em Abrir terminal para voltar ao Cloud Shell ou invoque o terminal integrado no editor do Cloud Shell a partir do menu Ver.

  6. Crie um repositório Docker no Artifact Registry:

    gcloud artifacts repositories create REPOSITORY_NAME \
        --repository-format=docker \
        --location=us
    
  7. Conceda à conta de serviço que vai executar a carga de trabalho a função Leitor do Artifact Registry (roles/artifactregistry.reader) para que possa ler a partir 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
    
  8. Atualize as suas credenciais do Docker para incluir o nome do domínio us-docker.pkg.dev:

    gcloud auth configure-docker us-docker.pkg.dev
    
  9. Crie uma imagem de Docker a partir de Dockerfile introduzindo o seguinte comando no terminal:

    docker build -t \
        "us-docker.pkg.dev/ALEX_PROJECT_ID/\
    REPOSITORY_NAME/WORKLOAD_CONTAINER_NAME:latest" .
    
  10. Envie a imagem de Docker para o Artifact Registry:

    docker push \
        us-docker.pkg.dev/ALEX_PROJECT_ID/REPOSITORY_NAME/WORKLOAD_CONTAINER_NAME
    
  11. A resposta do Docker push apresenta o resumo SHA256 da imagem, que é necessário posteriormente para autorizar a carga de trabalho. O resumo tem um aspeto semelhante ao do seguinte exemplo:

    sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
    

    Copie o resumo da imagem (incluindo o prefixo sha256:) para um local onde o possa consultar. Também pode introduzir o resumo no seguinte exemplo de código para pré-preencher o resto dos exemplos de código neste guia que precisam do valor:

    WORKLOAD_CONTAINER_IMAGE_DIGEST
    
  12. Certifique-se de que todas as partes auditam a imagem do Docker e verificam se é fidedigna antes de autorizar a respetiva utilização.

Autorize a carga de trabalho

Com a carga de trabalho aprovada por ambas as partes, Alex e Bola têm de adicionar a atestação do Google Cloud como um fornecedor aos respetivos conjuntos de identidades da carga de trabalho. O fornecedor especifica o serviço de atestação a usar e as propriedades que a carga de trabalho tem de corresponder para poder operar nos dados de Alex ou Bola. Se um interveniente malicioso alterar a imagem do Docker ou alterar outra propriedade medida, o acesso ao volume de trabalho é recusado.

Este guia usa mapeamentos de atributos para fornecer acesso direto aos recursos à carga de trabalho com base no resumo da imagem. No entanto, noutras situações, pode preferir usar a representação da conta de serviço para aceder aos recursos. Consulte o artigo Acesso a cargas de trabalho externas para saber mais.

Para configurar os fornecedores para o Alex e a Bola com as condições necessárias, conclua os seguintes passos:

  1. Introduza o seguinte comando para criar o fornecedor para o 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'"
    
  2. Obtenha o número do projeto de Alex para o próximo comando:

    gcloud projects describe ALEX_PROJECT_ID --format="value(projectNumber)"
    
  3. Conceda à identidade federada definida pelo fornecedor de Alex a função cloudkms.cryptoKeyDecrypter, especificando o atributo image_digest, para que apenas os contentores de carga de trabalho com o resumo especificado possam desencriptar as respetivas chaves do 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
    
  4. Mudar para o projeto de Bola:

    gcloud config set project BOLA_PROJECT_ID
    
  5. Introduza o seguinte comando para criar o fornecedor para 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'"
    
  6. Obtenha o número do projeto de Bola para o comando seguinte:

    gcloud projects describe BOLA_PROJECT_ID --format="value(projectNumber)"
    
  7. Conceda à identidade federada definida pelo fornecedor da Bola a função cloudkms.cryptoKeyDecrypter, especificando o atributo image_digest para que apenas os contentores de cargas de trabalho com o resumo especificado possam desencriptar as respetivas chaves do 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
    

Teste a carga de trabalho

Com os fornecedores adicionados aos Workload Identity Pools de Alex e Bola e os recursos necessários implementados, é altura de o operador da carga de trabalho testar a carga de trabalho.

Para testar a carga de trabalho, cria uma nova instância de VM confidencial no projeto de Bola com as seguintes propriedades:

Introduza o seguinte comando no Cloud Shell de Bola para testar a carga de trabalho:

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"

Ver progresso

Pode ver o progresso da carga de trabalho no projeto de Bola acedendo ao Explorador de registos.

Aceda ao Explorador de registos

Para mostrar apenas entradas de registo do Confidential Space, filtre pelos seguintes campos de registo, se estiverem disponíveis:

  • Tipo de recurso: instância de VM

  • ID da instância: o ID da instância da VM

  • Nome do registo: confidential-space-launcher

Para atualizar o registo, clique em Ir para agora. Também pode deslocar a página para resultados anteriores e, em seguida, deslocá-la novamente para o fim do registo para carregar as entradas mais recentes.

Veja os resultados

Se a tarefa de carga de trabalho terminar e devolver 0, significa que não ocorreram erros e que é altura de verificar o resultado nos contentores de resultados do Alex e da Bola:

  1. Mudar para o projeto do Alex:

    gcloud config set project ALEX_PROJECT_ID
    
  2. Liste todos os ficheiros no respetivo conjunto de resultados:

    gcloud storage ls gs://ALEX_OUTPUT_BUCKET_NAME
    
  3. Leia o ficheiro mais recente apresentado, substituindo ALEX_OUTPUT_CLOUD_STORAGE_PATH pelo caminho do ficheiro, incluindo gs://:

    gcloud storage cat ALEX_OUTPUT_CLOUD_STORAGE_PATH
    

    Se não existir nenhum ficheiro, tem de depurar a sua carga de trabalho.

  4. Mudar para o projeto de Bola:

    gcloud config set project BOLA_PROJECT_ID
    
  5. Liste todos os ficheiros no respetivo conjunto de resultados:

    gcloud storage ls gs://BOLA_OUTPUT_BUCKET_NAME
    
  6. Leia o ficheiro mais recente apresentado, substituindo BOLA_RESULTS_CLOUD_STORAGE_PATH pelo caminho do ficheiro, incluindo gs://:

    gcloud storage cat BOLA_RESULTS_CLOUD_STORAGE_PATH
    

    Se não existir nenhum ficheiro, tem de depurar a sua carga de trabalho.

  7. Depois de ler os resultados com êxito, pare a instância de VM:

    gcloud compute instances stop WORKLOAD_VM_2_NAME --zone=us-west1-b
    

Ao lerem os ficheiros, o Alex e a Bola descobrem quem ganha mais sem nunca revelarem os seus salários um ao outro.

Depure e reinicie a carga de trabalho

Um ambiente do espaço confidencial tem muitas partes e é possível que algo tenha sido configurado incorretamente, o que faz com que a carga de trabalho falhe.

Ao contrário da imagem do espaço confidencial de produção, a imagem de depuração mantém a instância da VM em execução após a conclusão da carga de trabalho. Isto significa que, se os registos não revelarem informações suficientes para resolver o problema, o passo seguinte é estabelecer ligação à instância de VM através de SSH e continuar a depuração.

Depois de terminar a depuração, pare a instância de VM:

gcloud compute instances stop WORKLOAD_VM_2_NAME --zone=us-west1-b

Para executar a carga de trabalho no ambiente depurado, inicie novamente a VM:

gcloud compute instances start WORKLOAD_VM_2_NAME --zone=us-west1-b

Proteja o ambiente para produção

Depois de testar com êxito a carga de trabalho, é altura de reforçar o ambiente do espaço confidencial para a implementação em produção. O Alex e a Bola têm de adicionar uma declaração support_attributes aos respetivos fornecedores para verificar se a imagem do espaço confidencial de produção é usada para a carga de trabalho:

  1. Mudar para o projeto do Alex:

    gcloud config set project ALEX_PROJECT_ID
    
  2. Introduza o seguinte comando para atualizar o fornecedor de 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"
    
  3. Mudar para o projeto de Bola:

    gcloud config set project BOLA_PROJECT_ID
    
  4. Introduza o seguinte comando para atualizar o fornecedor de 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"
    

Implemente a carga de trabalho de produção

O Bola tem de criar uma instância de VM separada para executar a carga de trabalho de produção. As seguintes aspetos são diferentes em comparação com a carga de trabalho de teste:

  • O SO baseia-se na imagem de produção do Confidential Space. Esta opção tem o SSH desativado e a instância de VM para após a conclusão da carga de trabalho.

  • O redirecionamento de registo é removido. Apenas os registos básicos que não expõem informações confidenciais são apresentados no Cloud Logging.

Introduza o seguinte comando no Cloud Shell de Bola para implementar a carga de trabalho de produção:

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"

A forma como vê o progresso e vê os resultados é a mesma que quando testou a carga de trabalho.

Quando a carga de trabalho de produção termina, a instância de VM é interrompida. Para ver resultados diferentes, pode alterar os salários, voltar a encriptá-los, voltar a carregá-los para os respetivos contentores do Cloud Storage e, em seguida, reiniciar a instância de VM para executar novamente a carga de trabalho:

gcloud compute instances start WORKLOAD_VM_NAME --zone=us-west1-b

Limpeza

Para remover os recursos criados neste guia, conclua as seguintes instruções.

Limpe os recursos do Alex

  1. Mudar para o projeto do Alex:

    gcloud config set project ALEX_PROJECT_ID
    
  2. Elimine o Workload Identity Pool do Alex:

    gcloud iam workload-identity-pools delete ALEX_POOL_NAME \
        --location=global
    
  3. Elimine os contentores do Cloud Storage de Alex:

    gcloud storage rm gs://ALEX_INPUT_BUCKET_NAME \
        gs://ALEX_OUTPUT_BUCKET_NAME --recursive
    
  4. Eliminar os ficheiros de salário do Alex, o código Go e o Dockerfile:

    rm ALEX_SALARY.txt \
        ALEX_ENCRYPTED_SALARY_FILE \
        salary.go \
        Dockerfile
    
  5. Opcional: desative ou destrua a chave do Cloud Key Management Service de Alex.

  6. Opcional: Encerra o projeto do Alex.

Limpe os recursos do Bola

  1. Mudar para o projeto de Bola:

    gcloud config set project BOLA_PROJECT_ID
    
  2. Elimine a VM que executou o fluxo de trabalho de teste:

    gcloud compute instances delete WORKLOAD_VM_2_NAME --zone=us-west1-b
    
  3. Elimine a VM que executou o fluxo de trabalho de produção:

    gcloud compute instances delete WORKLOAD_VM_NAME --zone=us-west1-b
    
  4. Elimine a conta de serviço que executou a carga de trabalho:

    gcloud iam service-accounts delete \
        WORKLOAD_SERVICE_ACCOUNT_NAME@BOLA_PROJECT_ID.iam.gserviceaccount.com
    
  5. Elimine o Workload Identity Pool da Bola:

    gcloud iam workload-identity-pools delete BOLA_POOL_NAME \
        --location=global
    
  6. Elimine os contentores do Cloud Storage de Bola:

    gcloud storage rm gs://BOLA_INPUT_BUCKET_NAME \
        gs://BOLA_OUTPUT_BUCKET_NAME --recursive
    
  7. Elimine os ficheiros de salário de Bola:

    rm BOLA_SALARY.txt \
        BOLA_ENCRYPTED_SALARY_FILE
    
  8. Opcional: desative ou destrua a chave do Cloud Key Management Service da Bola.

  9. Opcional: Encerra o projeto do Bola.