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 las cifras. Deciden usar Confidential Space para mantener la confidencialidad de sus datos y aceptan asumir los siguientes roles:

  • Alex: colaborador de datos y autor de cargas de trabajo

  • Bola: colaborador de datos, operador de cargas de trabajo

Este diseño se ha creado 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 los datos, y puedes tener tantos colaboradores como quieras.

Antes de empezar

En esta guía se muestra un caso práctico de Confidential Space con 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 independientes y sus propios proyectos contenidos en organizaciones independientes, a los que no pueden acceder entre sí y que mantienen sus datos confidenciales separados.

Espacio Confidencial puede interactuar con muchos servicios de Google Cloudpara generar sus resultados, entre los que se incluyen los siguientes:

En esta guía se utilizan todas estas funciones y se presupone que el lector tiene conocimientos básicos sobre ellas.

API obligatorias

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

Nombre de la API Título de la API Habilitar en estos proyectos
cloudkms.googleapis.com Cloud KMS Colaboradores de datos (proyectos de Alex y Bola)
iamcredentials.googleapis.com API Service Account Credentials de Gestión de Identidades y Accesos

Colaboradores de datos (proyectos de Alex y Bola)

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

Roles obligatorios

Para obtener los permisos que necesitas para completar esta guía, pide a tu administrador que te conceda los siguientes roles de gestión de identidades y accesos 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 Uso de Servicio (roles/serviceusage.serviceUsageAdmin) 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 cargas de trabajo (Bola).
  • Administrador de cuentas de servicio (roles/iam.serviceAccountAdmin) del operador de cargas de trabajo (Bola).
  • Administrador de Compute (roles/compute.admin) del operador de cargas de trabajo (Bola).
  • Administrador de seguridad (roles/securityAdmin) del 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 conceder roles, consulta el artículo Gestionar el acceso a proyectos, carpetas y organizaciones.

También puedes conseguir los permisos necesarios a través de roles personalizados u otros roles predefinidos.

Configurar recursos de colaborador de datos

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

  • Los datos confidenciales en sí.

  • Una clave de cifrado para cifrar esos datos y mantener su confidencialidad.

  • Un segmento de Cloud Storage para almacenar los datos cifrados.

  • Un grupo de identidades de carga de trabajo. La carga de trabajo que procesa los datos confidenciales usa el grupo para acceder a los datos privados y descifrarlos.

Para empezar, ve a la Google Cloud consola:

Ir a la Google Cloud consola

Configurar los recursos de Alex

Para configurar los recursos de Alex, sigue estas instrucciones.

  1. Haz clic en Activar Cloud Shell.
  2. En Cloud Shell, introduce el siguiente comando para crear un proyecto para Alex. Sustituye ALEX_PROJECT_ID por el nombre que quieras:

    gcloud projects create ALEX_PROJECT_ID
  3. Cambia al proyecto que acabas de crear:

    gcloud config set project ALEX_PROJECT_ID
  4. Si aún no lo has hecho, habilita las APIs que requiere Alex como colaborador de datos y creador de cargas de trabajo:

    gcloud services enable cloudkms.googleapis.com artifactregistry.googleapis.com iamcredentials.googleapis.com
  5. Crea un conjunto de claves y una clave de cifrado 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. Asigna a Alex el rol cloudkms.cryptoKeyEncrypter para que pueda usar la clave de cifrado que acabas de crear para cifrar 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 el grupo de identidades de carga de trabajo de Alex:

    gcloud iam workload-identity-pools create ALEX_POOL_NAME \
        --location=global
  8. Crea un segmento 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
  9. Crea un archivo que contenga solo el salario de Alex como número:

    echo 123456 > ALEX_SALARY.txt
  10. Cifra el archivo y, a continuación, súbelo al segmento 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

Configurar los recursos de Bola

Para configurar los recursos de Bola, sigue estas instrucciones.

  1. En Cloud Shell, introduce el siguiente comando para crear un proyecto para Bola. Sustituye BOLA_PROJECT_ID por el nombre que quieras:

    gcloud projects create BOLA_PROJECT_ID
  2. Cambia al proyecto que acabas de crear:

    gcloud config set project BOLA_PROJECT_ID
  3. Si aún no lo has hecho, habilita las APIs que requiere Bola como colaborador de datos y operador de cargas de trabajo:

    gcloud services enable cloudkms.googleapis.com iamcredentials.googleapis.com compute.googleapis.com confidentialcomputing.googleapis.com
  4. Crea un conjunto de claves y una clave de cifrado 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. Asigna a Bola el rol cloudkms.cryptoKeyEncrypter para que pueda usar la clave de cifrado que acabas de crear para cifrar 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 el grupo de identidades de carga de trabajo de Bola:

    gcloud iam workload-identity-pools create BOLA_POOL_NAME \
        --location=global
  7. Crea un segmento 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
  8. Crea un archivo que contenga solo el salario de Bola como número:

    echo 111111 > BOLA_SALARY.txt
  9. Cifra el archivo y, a continuación, súbelo al segmento 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

Crear una cuenta de servicio para la carga de trabajo

En esta guía, Bola opera y ejecuta la carga de trabajo, pero cualquier persona puede asumir estos roles, incluido un tercero. La instancia de VM que crea Bola para ejecutar la carga de trabajo tiene asociada una cuenta de servicio que tiene permiso para generar tokens de certificación, escribir registros, leer los datos cifrados de Alex y Bola, y escribir resultados en segmentos de Cloud Storage específicos.

Sigue estos 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. Asigna a Bola el rol iam.serviceAccountUser para que pueda asociar la cuenta de servicio a la 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. Concede a la cuenta de servicio el rol confidentialcomputing.workloadUser para que pueda generar un token de atestació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. Concede a la cuenta de servicio el rol logging.logWriter para escribir registros en Cloud Logging y, así, poder comprobar 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. Da a la cuenta de servicio acceso de lectura a los segmentos de Alex y Bola que contienen sus datos cifrados, y acceso de escritura a cada uno de sus segmentos 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
    

    Se presupone que el usuario que concede el acceso tiene el rol Administrador de Storage (roles/storage.admin) en el proyecto que contiene el segmento de Cloud Storage en el que se está realizando la operación.

Crear la carga de trabajo

En esta guía, Alex proporciona el código de la carga de trabajo y crea una imagen de Docker para contenerla, pero cualquier persona puede asumir estos roles, incluido un tercero.

Alex tiene que crear los siguientes recursos para la carga de trabajo:

  • 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, sigue estos 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. Concede a la cuenta de servicio que va a ejecutar la carga de trabajo el rol Lector de Artifact Registry (roles/artifactregistry.reader) para que pueda leer del 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, a continuación, crea un archivo llamado salary.go. Copia el siguiente código en el archivo y, a continuación, guárdalo:

    // READ ME FIRST: Before compiling, customize the details in the USER VARIABLES
    // SECTION starting at line 30.
    
    package main
    
    import (
      "context"
      "fmt"
      "io"
      "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
      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
    
    // 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
    
    // END USER VARIABLES SECTION
    // ==========================
    
    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,
        collaborator1BucketOutputFileName,
      },
      {
        collaborator2Name,
        "projects/" + collaborator2ProjectNumber + "/locations/global/workloadIdentityPools/" + collaborator2PoolName + "/providers/attestation-verifier",
        "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"
            }
            }`
    
    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.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
    }
    
  5. Modifica el USER VARIABLES SECTION del código fuente y sustituye los valores const vacíos por los nombres de recursos correspondientes, tal como se describe en los comentarios del código. Si ha editado las variables de marcador de posición como ALEX_PROJECT_ID que se muestran en esta guía, los valores se incluyen en el siguiente código de ejemplo, que puede copiar y pegar en el código actual:

    // 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
    
    // 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
    

    Asegúrate de actualizar también los números de proyecto de Alex y de Bola. Puedes recuperarlos 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 un terminal en el editor de Cloud Shell.

  8. Introduce los siguientes comandos en el 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. Introduce el siguiente comando para compilar el código fuente en un archivo binario vinculado de forma estática:

    CGO_ENABLED=0 go build -trimpath
    
  10. Crea un archivo llamado Dockerfile en el editor de Cloud Shell con 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. Crea una imagen de Docker a partir de Dockerfile introduciendo el siguiente comando en el terminal:

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

    docker push \
        us-docker.pkg.dev/ALEX_PROJECT_ID/REPOSITORY_NAME/WORKLOAD_CONTAINER_NAME
    
  14. En la respuesta de la inserción de Docker, copia el digest de la imagen de Docker (incluido el prefijo sha256:) en un lugar seguro. Se usa más adelante para autorizar la carga de trabajo.

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

Autorizar la carga de trabajo

Una vez que ambas partes han aprobado la carga de trabajo, Alex y Bola deben añadir Google Cloud Attestation como proveedor a sus grupos de identidades de carga de trabajo. El proveedor especifica el servicio de certificación que se va a usar y las propiedades que debe cumplir la carga de trabajo para que se le permita operar con los datos de Alex o Bola. Si un agente malintencionado cambia la imagen de Docker o altera otra propiedad medida, se deniega el acceso a la carga de trabajo.

En esta guía se usan asignaciones de atributos para proporcionar acceso directo a los recursos de la carga de trabajo en función del digest de la imagen. Sin embargo, en otras situaciones, puede que prefieras usar la suplantación de identidad de la cuenta de servicio para acceder a los recursos. Consulta más información en el artículo sobre el acceso a cargas de trabajo externas.

Para configurar los proveedores de Alex y Bola con las condiciones necesarias, sigue estos pasos:

  1. Introduce 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=\"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"
    
  2. Asigna el rol cloudkms.cryptoKeyDecrypter a la identidad federada definida por el proveedor de Alex. Para ello, especifica el atributo image_digest de forma que solo los contenedores de cargas de trabajo con el digest especificado puedan descifrar sus claves de 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
    
  3. Cambia al proyecto de Bola:

    gcloud config set project BOLA_PROJECT_ID
    
  4. Introduce 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=\"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"
    
  5. Asigna el rol cloudkms.cryptoKeyDecrypter a la identidad federada definida por el proveedor de Bola. Para ello, especifica el atributo image_digest de forma que solo los contenedores de cargas de trabajo con el digest especificado puedan descifrar sus claves de 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
    

Desplegar la carga de trabajo

Ahora que se han añadido proveedores a los grupos de identidades de carga de trabajo de Alex y Bola, y que se han configurado los recursos necesarios, es el momento de que el operador de la carga de trabajo ejecute la carga de trabajo.

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

  • Una configuración admitida para una instancia de VM confidencial con AMD SEV, Intel TDX o Intel TDX con Confidential Computing de NVIDIA (vista previa).

  • Un SO basado en la imagen de Confidential Space.

  • Arranque seguro habilitado.

  • La imagen de Docker adjunta que Alex ha creado antes.

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

Introduce el siguiente comando en Cloud Shell de Bola para desplegar 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 \
    --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"

Para ver el progreso de la carga de trabajo en el proyecto de Bola, ve a Explorador de registros.

Ir a 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: ID de la VM.

  • Nombre del registro: confidential-space-launcher

Para actualizar el registro, haz clic en Ir a ahora.

Cuando finaliza la carga de trabajo, la instancia de VM se detiene. Si quieres cambiar los archivos de salario cifrados y volver a implementar la carga de trabajo, solo tienes que iniciar la VM que ya tienes:

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

Ver los resultados

Una vez que la carga de trabajo se haya completado correctamente, tanto Alex como Bola podrán ver los resultados en sus respectivos contenedores de resultados:

  1. Cambia al proyecto de Alex:

    gcloud config set project ALEX_PROJECT_ID
    
  2. Lista todos los archivos del contenedor de resultados:

    gcloud storage ls gs://ALEX_RESULTS_BUCKET_NAME
    

    A continuación, 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. En el caso de Bola, enumera todos los archivos de su contenedor de resultados:

    gcloud storage ls gs://BOLA_RESULTS_BUCKET_NAME
    

    A continuación, 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.

Depurar la carga de trabajo

Puedes usar el explorador de registros para solucionar problemas como que los recursos no estén configurados correctamente o que las condiciones de los atributos de los proveedores no coincidan con las reclamaciones realizadas por la carga de trabajo de Espacio Confidencial.

Para ello, debes hacer los siguientes cambios:

  • Actualiza los proveedores de grupos de identidades de carga de trabajo de Alex y Bola para quitar la aserción support_attributes. Necesitas usar la imagen de depuración de Confidential Space para llevar a cabo una solución de problemas más detallada, y esa imagen no tiene atributos de asistencia que verificar.

  • Crea la máquina virtual de carga de trabajo con la imagen de depuración de Confidential Space y define los metadatos de la máquina virtual para redirigir STDOUT y STDERR a Cloud Logging y capturar toda la salida de la carga de trabajo.

Para hacer los cambios, sigue estos 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=\"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'"
    
  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=\"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'"
    
  5. Crea una VM con la imagen de depuración de Confidential Space y asigna el valor true a tee-container-log-redirect 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 haya finalizado la carga de trabajo. Esto significa que puedes usar SSH para conectarte a tu VM y seguir depurando.

Limpieza

Para eliminar los recursos creados en esta guía, sigue las instrucciones que se indican a continuación.

Limpiar los recursos de Alex

  1. Cambia al proyecto de Alex:

    gcloud config set project ALEX_PROJECT_ID
    
  2. Elimina el grupo de identidades de carga de trabajo de Alex:

    gcloud iam workload-identity-pools delete ALEX_POOL_NAME \
        --location=global
    
  3. Elimina los segmentos de Cloud Storage de Alex:

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

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

  6. Opcional: Cierra el proyecto de Alex.

Limpiar los recursos de Bola

  1. Cambia al proyecto de Bola:

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

    gcloud compute instances delete WORKLOAD_VM_NAME --zone=us-west1-b
    
  3. Elimina la cuenta de servicio que ejecuta la carga de trabajo:

    gcloud iam service-accounts delete \
        WORKLOAD_SERVICE_ACCOUNT_NAME@BOLA_PROJECT_ID.iam.gserviceaccount.com
    
  4. Elimina el grupo de identidades de carga de trabajo de Bola:

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

    gcloud storage rm gs://BOLA_INPUT_BUCKET_NAME \
        gs://BOLA_RESULTS_BUCKET_NAME --recursive
    
  6. Elimina 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.