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.
- Haz clic en Activar Cloud Shell.
-
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
-
Cambia al proyecto recién creado:
gcloud config set project ALEX_PROJECT_ID
-
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
-
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
-
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
-
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
-
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
-
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
-
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
-
Crea un archivo que solo contenga el salario de Alex como un número:
echo 123456 > ALEX_SALARY.txt
-
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.
-
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
-
Cambia al proyecto recién creado:
gcloud config set project BOLA_PROJECT_ID
-
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
-
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
-
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
-
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
-
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
-
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
-
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
-
Crea un archivo que solo contenga el salario de Bola como un número:
echo 111111 > BOLA_SALARY.txt
-
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:
Crea una cuenta de servicio para ejecutar la carga de trabajo:
gcloud iam service-accounts create WORKLOAD_SERVICE_ACCOUNT_NAME
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
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
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
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:
Cambia al proyecto de Alex:
gcloud config set project ALEX_PROJECT_ID
Crea un repositorio de Docker en Artifact Registry:
gcloud artifacts repositories create REPOSITORY_NAME \ --repository-format=docker \ --location=us
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
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 }
Modifica la
USER VARIABLES SECTION
en el código fuente, reemplazando los valores vacíos deconst
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 comoALEX_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)"
Asegúrate de que todas las partes lean y auditen el código fuente.
Haz clic en Terminal > Nuevo Terminal para abrir una terminal dentro del editor de Cloud Shell.
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
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
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 []
Actualiza tus credenciales de Docker para incluir el nombre de dominio
us-docker.pkg.dev
:gcloud auth configure-docker us-docker.pkg.dev
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 .
Envía la imagen de Docker a Artifact Registry:
docker push \ us-docker.pkg.dev/ALEX_PROJECT_ID/REPOSITORY_NAME/WORKLOAD_CONTAINER_NAME
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.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:
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"
Cambia al proyecto de Bola:
gcloud config set project BOLA_PROJECT_ID
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.
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
ySTDERR
a Cloud Logging a fin de capturar todos los resultados de la carga de trabajo.
Para realizar los cambios, completa los siguientes pasos:
Cambia al proyecto de Alex:
gcloud config set project ALEX_PROJECT_ID
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'"
Cambia al proyecto de Bola:
gcloud config set project BOLA_PROJECT_ID
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'"
Crea una VM nueva con la imagen de depuración de Confidential Space y
tee-container-log-redirect
configurado comotrue
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:
Cambia al proyecto de Alex:
gcloud config set project ALEX_PROJECT_ID
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
Cambia al proyecto de Bola:
gcloud config set project BOLA_PROJECT_ID
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
Cambia al proyecto de Alex:
gcloud config set project ALEX_PROJECT_ID
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
Borra el grupo de Workload Identity de Alex:
gcloud iam workload-identity-pools delete ALEX_POOL_NAME \ --location=global
Borra los buckets de Cloud Storage de Alex:
gcloud storage rm gs://ALEX_INPUT_BUCKET_NAME \ gs://ALEX_RESULTS_BUCKET_NAME --recursive
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
Opcional: Inhabilita o destruye la clave de Cloud Key Management Service de Alex.
Opcional: Cierra el proyecto de Alex.
Limpia los recursos de Bola
Cambia al proyecto de Bola:
gcloud config set project BOLA_PROJECT_ID
Borra la VM de la carga de trabajo:
gcloud compute instances delete WORKLOAD_VM_NAME --zone=us-west1-b
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
Borra el grupo de Workload Identity de Bola:
gcloud iam workload-identity-pools delete BOLA_POOL_NAME \ --location=global
Borra los buckets de Cloud Storage de Bola:
gcloud storage rm gs://BOLA_INPUT_BUCKET_NAME \ gs://BOLA_RESULTS_BUCKET_NAME --recursive
Borra los archivos de salario de Bola:
rm BOLA_SALARY.txt \ BOLA_ENCRYPTED_SALARY_FILE
Opcional: Inhabilita o destruye la clave de Cloud Key Management Service de Bola.
Opcional: Cierra el proyecto de Bola.