Crea tu primer entorno de Confidential Space


En esta guía, Alex y Bola quieren saber quién tiene el salario más alto sin revelar números entre sí. Deciden usar Confidential Space para mantener la confidencialidad de sus datos y aceptan asumir los siguientes roles:

  • Alex: Colaborador de datos, autor de la carga de trabajo

  • Bola: Colaborador de datos, operador de la carga de trabajo

Este acuerdo está diseñado para que esta guía sea lo más sencilla posible. Sin embargo, es posible que el autor y el operador de la carga de trabajo sean completamente independientes de los colaboradores de datos, y puedes tener tantos colaboradores como desees.

Antes de comenzar

En esta guía, se muestra una situación de Confidential Space mediante una sola cuenta en una sola organización con acceso a varios proyectos, para que puedas experimentar todo el proceso. En una implementación de producción, los colaboradores, los autores de cargas de trabajo y los operadores de cargas de trabajo tienen cuentas separadas y sus propios proyectos contenidos en organizaciones discretas, a las que no pueden acceder entre sí y que mantienen sus datos confidenciales separados.

Confidential Space puede interactuar con muchos de los servicios de Google Cloudpara producir sus resultados, incluidos, sin limitaciones, los siguientes:

En esta guía, se supone que tienes conocimientos básicos de todas estas funciones y se las usa.

APIs requeridas

Debes habilitar las siguientes APIs en los proyectos especificados para poder completar esta guía.

Nombre de la API Título de la API Habilita en estos proyectos
cloudkms.googleapis.com Cloud KMS Colaboradores de datos (proyectos de Alex y Bola)
iamcredentials.googleapis.com API de IAM Service Account Credentials

Colaboradores de datos (proyecto de Alex)

En esta guía, solo Alex necesita esta API habilitada. Sin embargo, si hay más de dos partes involucradas, la API de Service Account Credentials de IAM debe estar habilitada en el proyecto de cada colaborador de datos que no aloja la cuenta de servicio de carga de trabajo.

artifactregistry.googleapis.com Artifact Registry Autor de la carga de trabajo (proyecto de Alex)
compute.googleapis.com Compute Engine Operador de carga de trabajo (proyecto de Bola)
confidentialcomputing.googleapis.com Confidential Computing Operador de carga de trabajo (proyecto de Bola)

Roles obligatorios

Para obtener los permisos que necesitas a fin de completar esta guía, pídele a tu administrador que te otorgue los siguientes roles de IAM en el proyecto:

  • Administrador de Cloud KMS (roles/cloudkms.admin) para los colaboradores de datos (Alex y Bola).
  • Administrador de grupos de Workload Identity de IAM (roles/iam.workloadIdentityPoolAdmin) para los colaboradores de datos (Alex y Bola).
  • Administrador de Service Usage (roles/serviceusage.serviceUsageAdmin) para los colaboradores de datos (Alex y Bola).
  • Administrador de cuentas de servicio (roles/iam.serviceAccountAdmin) para los colaboradores de datos (Alex y Bola).
  • Administrador de almacenamiento (roles/storage.admin) para los colaboradores de datos (Alex y Bola) y el operador de carga de trabajo (Bola).
  • Administrador de Compute (roles/compute.admin) para el operador de la carga de trabajo (Bola).
  • Administrador de seguridad (roles/securityAdmin) para el operador de la carga de trabajo (Bola).
  • Administrador de Artifact Registry (roles/artifactregistry.admin) para el autor de la carga de trabajo (Alex).

Para obtener más información sobre cómo otorgar roles, consulta Administra el acceso a proyectos, carpetas y organizaciones.

También puedes obtener los permisos necesarios mediante roles personalizados o cualquier otro rol predefinido.

Configura los recursos de los colaboradores de datos

Tanto Alex como Bola necesitan proyectos independientes que contengan los siguientes recursos:

  • Los datos confidenciales en sí

  • Una clave de encriptación para encriptar esos datos y mantenerlos confidenciales.

  • Un bucket de Cloud Storage en el que se almacenarán los datos encriptados

  • Una cuenta de servicio que tenga acceso a la clave de encriptación para que pueda desencriptar los datos confidenciales

  • Un grupo de Workload Identity con esa cuenta de servicio conectada a él. La carga de trabajo que procesa los datos confidenciales usa el grupo para suplantar la identidad de la cuenta de servicio y recuperar los datos sin encriptar.

Para comenzar, ve a la consola de Google Cloud :

Ve a la consola de Google Cloud

Configura los recursos de Alex

Para configurar los recursos de Alex, completa las siguientes instrucciones.

  1. Haz clic en Activar Cloud Shell.
  2. En Cloud Shell, ingresa el comando siguiente a fin de crear un proyecto para Alex y reemplaza ALEX_PROJECT_ID por el nombre que elijas:

    gcloud projects create ALEX_PROJECT_ID
  3. Cambia al proyecto recién creado:

    gcloud config set project ALEX_PROJECT_ID
  4. Si aún no lo hiciste, habilita las APIs de Alex que son colaboradores de datos y autores de cargas de trabajo:

    gcloud services enable cloudkms.googleapis.com artifactregistry.googleapis.com iamcredentials.googleapis.com
  5. Crea un llavero de claves y una clave de encriptación con 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. Otorga a Alex el rol cloudkms.cryptoKeyEncrypter para que pueda usar la clave de encriptación recién creada a fin de encriptar datos:

    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. Crea una cuenta de servicio que la carga de trabajo use más adelante para desencriptar los datos:

    gcloud iam service-accounts create ALEX_SERVICE_ACCOUNT_NAME
  8. Otorga a la cuenta de servicio el rol cloudkms.cryptoKeyDecrypter para que pueda usar la clave de encriptación que acabas de crear para desencriptar datos:

    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
  9. Crea un grupo de Workload Identity y, luego, conecta la cuenta de servicio con un rol 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
  10. Crea un bucket de Cloud Storage para los datos de entrada y otro para almacenar los resultados:

    gcloud storage buckets create gs://ALEX_INPUT_BUCKET_NAME \
        gs://ALEX_RESULTS_BUCKET_NAME
  11. Crea un archivo que solo contenga el salario de Alex como un número:

    echo 123456 > ALEX_SALARY.txt
  12. Encripta el archivo y, luego, súbelo al bucket 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

Configura los recursos de Bola

A fin de configurar los recursos para Bola, completa las siguientes instrucciones.

  1. En Cloud Shell, ingresa el siguiente comando a fin de crear un proyecto para Bola y reemplaza BOLA_PROJECT_ID por el nombre que elijas:

    gcloud projects create BOLA_PROJECT_ID
  2. Cambia al proyecto recién creado:

    gcloud config set project BOLA_PROJECT_ID
  3. Si aún no lo hiciste, habilita las APIs de Bola como colaborador de datos y operador de carga de trabajo:

    gcloud services enable cloudkms.googleapis.com compute.googleapis.com confidentialcomputing.googleapis.com
  4. Crea un llavero de claves y una clave de encriptación con 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. Otorga a Bola el rol cloudkms.cryptoKeyEncrypter para que pueda usar la clave de encriptación recién creada para encriptar datos:

    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. Crea una cuenta de servicio que la carga de trabajo use más adelante para desencriptar los datos:

    gcloud iam service-accounts create BOLA_SERVICE_ACCOUNT_NAME
  7. Otorga a la cuenta de servicio el rol cloudkms.cryptoKeyDecrypter para que pueda usar la clave de encriptación que acabas de crear para desencriptar datos:

    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
  8. Crea un grupo de Workload Identity y, luego, conecta la cuenta de servicio con un rol 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
  9. Crea un bucket de Cloud Storage para los datos de entrada y otro para almacenar los resultados:

    gcloud storage buckets create gs://BOLA_INPUT_BUCKET_NAME \
        gs://BOLA_RESULTS_BUCKET_NAME
  10. Crea un archivo que solo contenga el salario de Bola como un número:

    echo 111111 > BOLA_SALARY.txt
  11. Encripta el archivo y, luego, súbelo al bucket 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

Crea una cuenta de servicio para la carga de trabajo

Además de las cuentas de servicio que Alex y Bola configuraron para desencriptar sus datos, se necesita otra cuenta de servicio para ejecutar la carga de trabajo. Dado que las cuentas de servicio se usan para desencriptar los datos confidenciales y procesarlos, la visibilidad de los datos se limita a sus propietarios.

En esta guía, Bola opera y ejecuta la carga de trabajo, pero cualquier persona puede asumir estos roles, incluido un tercero.

Completa los siguientes pasos en el proyecto de Bola para configurar la cuenta de servicio:

  1. Crea una cuenta de servicio para ejecutar la carga de trabajo:

    gcloud iam service-accounts create WORKLOAD_SERVICE_ACCOUNT_NAME
    
  2. Otorga a Bola el rol iam.serviceAccountUser para actuar en nombre de la cuenta de servicio, que es necesaria a fin de que pueda crear una VM de carga de trabajo más adelante:

    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. Otorga a la cuenta de servicio la función confidentialcomputing.workloadUser para que pueda generar un token de certificación:

    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. Otorga a la cuenta de servicio el rol logging.logWriter para escribir registros en Cloud Logging a fin de que puedas verificar el progreso de la carga de trabajo:

    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. Otorga a la cuenta de servicio acceso de lectura a los buckets de Alex y Bola que contengan sus datos encriptados, y acceso de escritura a cada uno de sus 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
    

    Esto supone que el usuario que otorga el acceso tiene el rol de administrador de almacenamiento (roles/storage.admin) para el proyecto que contiene el bucket de Cloud Storage en el que se opera.

Crea la carga de trabajo

En esta guía, Alex proporciona el código para la carga de trabajo y compila una imagen de Docker que la contiene, pero cualquiera puede asumir estos roles, incluido un tercero.

Alex debe crear los siguientes recursos para la carga de trabajo:

  • Es el código que realiza la carga de trabajo.

  • Un repositorio de Docker en Artifact Registry al que tenga acceso la cuenta de servicio que ejecuta la carga de trabajo

  • Una imagen de Docker que contiene y ejecuta el código de la carga de trabajo.

Para crear y configurar los recursos, completa los siguientes pasos en el proyecto de Alex:

  1. Cambia al proyecto de Alex:

    gcloud config set project ALEX_PROJECT_ID
    
  2. Crea un repositorio de Docker en Artifact Registry:

    gcloud artifacts repositories create REPOSITORY_NAME \
        --repository-format=docker \
        --location=us
    
  3. Otorga a la cuenta de servicio que ejecutará la carga de trabajo el rol de lector de Artifact Registry (roles/artifactregistry.reader) para que pueda leer desde el repositorio:

    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
    
  4. Haz clic en Abrir editor para abrir el editor de Cloud Shell y, luego, crea un archivo nuevo llamado salary.go. Copia el siguiente código en el archivo y guárdalo:

    // 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
    }
    
  5. Modifica la USER VARIABLES SECTION en el código fuente, reemplazando los valores vacíos de const con los nombres de los recursos relevantes como se describe en los comentarios del código. Si editaste las variables de marcador de posición como ALEX_PROJECT_ID que se muestran en esta guía, los valores se incluyen en la siguiente muestra de código, que puedes copiar y pegar en el 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
    

    Asegúrate de actualizar los números de proyecto para Alex y Bola. Puedes recuperarlas con el siguiente comando:

    gcloud projects describe PROJECT_ID --format="value(projectNumber)"
    
  6. Asegúrate de que todas las partes lean y auditen el código fuente.

  7. Haz clic en Terminal > Nuevo Terminal para abrir una terminal dentro del editor de Cloud Shell.

  8. Ingresa los siguientes comandos en la terminal para configurar el entorno de 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
    
  9. Ingresa el siguiente comando para compilar el código fuente en un objeto binario vinculado de forma estática:

    CGO_ENABLED=0 go build -trimpath
    
  10. Crea un archivo llamado Dockerfile en el editor de Cloud Shell que incluya el siguiente contenido:

    FROM alpine:latest
    WORKDIR /test
    COPY salary /test
    ENTRYPOINT ["/test/salary"]
    CMD []
    
  11. Actualiza tus credenciales de Docker para incluir el nombre de dominio us-docker.pkg.dev:

    gcloud auth configure-docker us-docker.pkg.dev
    
  12. Para crear una imagen de Docker a partir de Dockerfile, ingresa el siguiente comando en la terminal:

    docker build -t \
        us-docker.pkg.dev/ALEX_PROJECT_ID/REPOSITORY_NAME/WORKLOAD_CONTAINER_NAME:latest .
    
  13. Envía la imagen de Docker a Artifact Registry:

    docker push \
        us-docker.pkg.dev/ALEX_PROJECT_ID/REPOSITORY_NAME/WORKLOAD_CONTAINER_NAME
    
  14. En la respuesta de envío de Docker, copia el resumen de la imagen de Docker (incluido el prefijo sha256:) en un lugar seguro para usarlo más adelante.

  15. Asegúrate de que todas las partes auditen la imagen de Docker y verifiquen que sea confiable antes de autorizar su uso.

Autoriza la carga de trabajo

Con la carga de trabajo aprobada por ambas partes, Alex y Bola deben agregar el servicio de verificador de certificación de Confidential Space como proveedor a sus grupos de Workload Identity. Esto permite que la cuenta de servicio conectada a la carga de trabajo usurpe la identidad de las cuentas de servicio conectadas a sus grupos y acceda a sus datos, siempre que se cumplan ciertas condiciones de atributos. Esto significa que las condiciones de los atributos actúan como políticas de certificación.

Las condiciones de los atributos que se usan en esta guía son las siguientes:

  • El resumen de la imagen de Docker que se ejecuta

  • La dirección de correo electrónico de la cuenta de servicio que ejecuta la carga de trabajo

Si una persona que actúa de mala fe cambia la imagen de Docker o se adjunta una cuenta de servicio diferente a la carga de trabajo, esta no podrá acceder a los datos de Alex ni de Bola.

Para ver las condiciones de los atributos disponibles, consulta Declaraciones de certificación.

Para configurar los proveedores de Alex y Bola con las condiciones requeridas, completa los siguientes pasos:

  1. Ingresa el siguiente comando para crear el proveedor de 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"
    
  2. Cambia al proyecto de Bola:

    gcloud config set project BOLA_PROJECT_ID
    
  3. Ingresa el siguiente comando para crear el proveedor de 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"
    

Implementa la carga de trabajo

Con los proveedores agregados a los grupos de Workload Identity de Alex y Bola, y los recursos necesarios, es momento de que el operador de la carga de trabajo ejecute la carga de trabajo.

Para implementar la carga de trabajo, crea una nueva instancia de Confidential VM en el proyecto de Bola que tenga las siguientes propiedades:

  • Una configuración compatible para una instancia de VM confidencial de AMD SEV o Intel TDX

  • Un SO basado en la imagen de Confidential Space.

  • El inicio seguro está habilitado.

  • La imagen de Docker adjunta que Alex creó antes.

  • La cuenta de servicio adjunta que ejecuta la carga de trabajo

Ingresa el siguiente comando en Cloud Shell de Bola para implementar la carga de trabajo:

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"

Puedes ver el progreso de la carga de trabajo en el proyecto de Bola en el Explorador de registros.

Ir al Explorador de registros

Para encontrar los registros de Confidential Space, filtra por los siguientes Campos de registro si están disponibles:

  • Tipo de recurso: Instancia de VM

  • ID de instancia: El ID de instancia de VM

  • Nombre del registro: confidential-space-launcher

Para actualizar el registro, haz clic en Pasar a los más recientes.

Cuando se finaliza la carga de trabajo, la instancia de VM se detiene. Si deseas cambiar los archivos de salario encriptados y volver a implementar la carga de trabajo, solo necesitas iniciar la VM existente:

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

Depura la carga de trabajo

Puedes usar el Explorador de registros para solucionar problemas, como recursos que no se configuraron de forma correcta, o condiciones de atributos en proveedores que no coinciden con las reclamaciones realizadas por la carga de trabajo de Confidential Space.

Para hacerlo, debes realizar los siguientes cambios:

  • Actualiza los proveedores de grupos de Workload Identity de Alex y Bola para quitar la aserción support_attributes. Debes usar la imagen de depuración de Confidential Space para realizar una solución de problemas más detallada, y esa imagen no tiene atributos de compatibilidad que verificar.

  • Crea la VM de carga de trabajo con la imagen de depuración de Confidential Space y configura los metadatos de la VM para redireccionar STDOUT y STDERR a Cloud Logging a fin de capturar todos los resultados de la carga de trabajo.

Para realizar los cambios, completa los siguientes pasos:

  1. Cambia al proyecto de Alex:

    gcloud config set project ALEX_PROJECT_ID
    
  2. Actualiza el proveedor de Alex para quitar la aserción 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'"
    
  3. Cambia al proyecto de Bola:

    gcloud config set project BOLA_PROJECT_ID
    
  4. Actualiza el proveedor de Bola para quitar la aserción 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'"
    
  5. Crea una VM nueva con la imagen de depuración de Confidential Space y tee-container-log-redirect configurado como true en los metadatos.

    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"
    

A diferencia de la imagen de producción, la imagen de depuración mantiene la VM en ejecución después de que finaliza la carga de trabajo. Esto significa que puedes usar SSH a fin de conectarte a tu VM para continuar con la depuración.

Vea los resultados

Una vez que la carga de trabajo se complete correctamente, Alex y Bola podrán ver los resultados en sus respectivos buckets de resultados:

  1. Cambia al proyecto de Alex:

    gcloud config set project ALEX_PROJECT_ID
    
  2. Obtén una lista de todos los archivos en su bucket de resultados:

    gcloud storage ls gs://ALEX_RESULTS_BUCKET_NAME
    

    Luego, lee el archivo más reciente:

    gcloud storage cat gs://ALEX_RESULTS_BUCKET_NAME/ALEX_RESULTS_FILE_NAME
    
  3. Cambia al proyecto de Bola:

    gcloud config set project BOLA_PROJECT_ID
    
  4. Para Bola, enumera todos los archivos en su bucket de resultados:

    gcloud storage ls gs://BOLA_RESULTS_BUCKET_NAME
    

    Luego, lee el archivo más reciente:

    gcloud storage cat gs://BOLA_RESULTS_BUCKET_NAME/BOLA_RESULTS_FILE_NAME
    

Al leer los archivos, Alex y Bola descubren quién gana más sin revelar sus salarios entre sí.

Limpieza

Para quitar los recursos creados en esta guía, completa las siguientes instrucciones.

Limpia los recursos de Alex

  1. Cambia al proyecto de Alex:

    gcloud config set project ALEX_PROJECT_ID
    
  2. Borra la cuenta de servicio que desencripta los datos de Alex:

    gcloud iam service-accounts delete \
        ALEX_SERVICE_ACCOUNT_NAME@ALEX_PROJECT_ID.iam.gserviceaccount.com
    
  3. Borra el grupo de Workload Identity de Alex:

    gcloud iam workload-identity-pools delete ALEX_POOL_NAME \
        --location=global
    
  4. Borra los buckets de Cloud Storage de Alex:

    gcloud storage rm gs://ALEX_INPUT_BUCKET_NAME \
        gs://ALEX_RESULTS_BUCKET_NAME --recursive
    
  5. Borra los archivos de salario de Alex y el código Go:

    rm ALEX_SALARY.txt \
        ALEX_ENCRYPTED_SALARY_FILE \
        salary.go salary \
        go.mod go.sum
    
  6. Opcional: Inhabilita o destruye la clave de Cloud Key Management Service de Alex.

  7. Opcional: Cierra el proyecto de Alex.

Limpia los recursos de Bola

  1. Cambia al proyecto de Bola:

    gcloud config set project BOLA_PROJECT_ID
    
  2. Borra la VM de la carga de trabajo:

    gcloud compute instances delete WORKLOAD_VM_NAME --zone=us-west1-b
    
  3. Borra la cuenta de servicio que desencripta los datos de Bola y la cuenta de servicio que ejecuta la carga de trabajo:

    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
    
  4. Borra el grupo de Workload Identity de Bola:

    gcloud iam workload-identity-pools delete BOLA_POOL_NAME \
        --location=global
    
  5. Borra los buckets de Cloud Storage de Bola:

    gcloud storage rm gs://BOLA_INPUT_BUCKET_NAME \
        gs://BOLA_RESULTS_BUCKET_NAME --recursive
    
  6. Borra los archivos de salario de Bola:

    rm BOLA_SALARY.txt \
        BOLA_ENCRYPTED_SALARY_FILE
    
  7. Opcional: Inhabilita o destruye la clave de Cloud Key Management Service de Bola.

  8. Opcional: Cierra el proyecto de Bola.