Créer votre premier environnement Confidential Space


Dans ce guide, Alex et Bola veulent savoir qui a le salaire le plus élevé sans se révéler les uns aux autres. Il décide d'utiliser Confidential Space pour préserver la confidentialité de ses données, et accepte d'assumer les rôles suivants:

  • Alex : collaborateur de données, auteur de la charge de travail

  • Bola : collaborateur de données, opérateur de charge de travail

Cette configuration est conçue pour simplifier le présent guide. Cependant, l'auteur et l'opérateur de la charge de travail peuvent être totalement indépendants des collaborateurs de données, et vous pouvez avoir autant de collaborateurs que vous le souhaitez.

Avant de commencer

Ce guide illustre un scénario Confidential Space qui utilise un seul compte dans une seule organisation ayant accès à plusieurs projets, afin que vous puissiez découvrir l'ensemble du processus. Dans un déploiement en production, les collaborateurs, les auteurs de charges de travail et les opérateurs de charge de travail ont des comptes distincts et leurs propres projets contenus dans des organisations discrètes, inaccessibles les unes aux autres et conservant leurs données confidentielles séparées.

Confidential Space peut interagir avec de nombreux services Google Cloudpour produire ses résultats, y compris, mais sans s'y limiter:

Dans ce guide, nous partons du principe que vous maîtrisez toutes les fonctionnalités de toutes ces fonctionnalités.

API requises

Pour pouvoir suivre ce guide, vous devez activer les API suivantes dans les projets spécifiés.

Nom de l'API Titre de l'API Activer dans ces projets
cloudkms.googleapis.com Cloud KMS Collaborateurs pour les données (projets d'Alex et Bola)
iamcredentials.googleapis.com API IAM Service Account Credentials

collaborateurs pour les données (projet d'Alex)

Dans ce guide, seul Alex a besoin d'activer cette API. Toutefois, si plus de deux parties sont impliquées, l'API IAM Service Account Credentials doit être activée sur le projet de chaque collaborateur pour les données qui n'héberge pas le compte de service de charge de travail.

artifactregistry.googleapis.com Artifact Registry Auteur de la charge de travail (projet d'Alex)
compute.googleapis.com Compute Engine Opérateur de charge de travail (projet de Bola)
confidentialcomputing.googleapis.com Informatique confidentielle Opérateur de charge de travail (projet de Bola)

Rôles requis

Pour obtenir les autorisations nécessaires pour suivre ce guide, demandez à votre administrateur de vous accorder les rôles IAM suivants sur le projet :

  • Administrateur Cloud KMS (roles/cloudkms.admin) pour les collaborateurs de données (Alex et Bola).
  • Rôle IAM d'administrateur de pools Workload Identity (roles/iam.workloadIdentityPoolAdmin) pour les collaborateurs de données (Alex et Bola).
  • Administrateur de Service Usage (roles/serviceusage.serviceUsageAdmin) pour les collaborateurs de données (Alex et Bola).
  • Administrateur de compte de service (roles/iam.serviceAccountAdmin) pour les collaborateurs de données (Alex et Bola).
  • Administrateur de l'espace de stockage (roles/storage.admin) pour les collaborateurs de données (Alex et Bola) et l'opérateur de charge de travail (Bola).
  • Administrateur Compute (roles/compute.admin) pour l'opérateur de charge de travail (Bola).
  • Administrateur de sécurité (roles/securityAdmin) pour l'opérateur de charge de travail (Bola).
  • Administrateur Artifact Registry (roles/artifactregistry.admin) pour l'auteur de la charge de travail (Alex).

Pour en savoir plus sur l'attribution de rôles, consultez la page Gérer l'accès aux projets, aux dossiers et aux organisations.

Vous pouvez également obtenir les autorisations requises via des rôles personnalisés ou d'autres rôles prédéfinis.

Configurer les ressources des collaborateurs

Alex et Bola ont tous deux besoin de projets indépendants contenant les ressources suivantes :

  • Les données confidentielles elles-mêmes.

  • Une clé de chiffrement pour chiffrer ces données et les garder confidentielles.

  • Un bucket Cloud Storage dans lequel stocker les données chiffrées.

  • Un compte de service ayant accès à la clé de chiffrement afin qu'il puisse déchiffrer les données confidentielles.

  • Un pool d'identités de charge de travail auquel ce compte de service est associé. La charge de travail qui traite les données confidentielles utilise le pool pour emprunter l'identité du compte de service et récupérer les données non chiffrées.

Pour commencer, accédez à la console Google Cloud :

Accédez à la console Google Cloud .

Configurer les ressources d'Alex

Pour configurer les ressources pour Alex, suivez les instructions ci-dessous.

  1. Cliquez sur Activer Cloud Shell.
  2. Dans Cloud Shell, saisissez la commande suivante pour créer un projet pour Alex, en remplaçant ALEX_PROJECT_ID par le nom de votre choix :

    gcloud projects create ALEX_PROJECT_ID
  3. Basculez vers le nouveau projet :

    gcloud config set project ALEX_PROJECT_ID
  4. Si vous ne l'avez pas déjà fait, activez les API requises par Alex en tant que collaborateur et auteur de charge de travail :

    gcloud services enable cloudkms.googleapis.com artifactregistry.googleapis.com iamcredentials.googleapis.com
  5. Créez un trousseau de clés et une clé de chiffrement avec 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. Attribuez à Alex le rôle cloudkms.cryptoKeyEncrypter afin qu'il puisse utiliser la clé de chiffrement nouvellement créée pour chiffrer des données :

    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. Créez un compte de service qui sera utilisé ultérieurement par la charge de travail pour déchiffrer les données :

    gcloud iam service-accounts create ALEX_SERVICE_ACCOUNT_NAME
  8. Attribuez au compte de service le rôle cloudkms.cryptoKeyDecrypter afin qu'il puisse utiliser la clé de chiffrement que vous venez de créer pour déchiffrer des données :

    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. Créez un pool d'identités de charge de travail, puis connectez-y le compte de service avec le rôle 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. Créez un bucket Cloud Storage pour les données d'entrée, et un autre pour y stocker les résultats :

    gcloud storage buckets create gs://ALEX_INPUT_BUCKET_NAME \
        gs://ALEX_RESULTS_BUCKET_NAME
  11. Créez un fichier contenant uniquement le salaire d'Alex en tant que nombre :

    echo 123456 > ALEX_SALARY.txt
  12. Chiffrez le fichier, puis importez-le dans le bucket d'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

Configurer les ressources de Bola

Pour configurer les ressources pour Bola, suivez les instructions ci-dessous.

  1. Dans Cloud Shell, saisissez la commande suivante pour créer un projet pour Bola, en remplaçant BOLA_PROJECT_ID par le nom de votre choix :

    gcloud projects create BOLA_PROJECT_ID
  2. Basculez vers le nouveau projet :

    gcloud config set project BOLA_PROJECT_ID
  3. Si vous ne l'avez pas déjà fait, activez les API requises par Bola en tant que collaborateur et opérateur de charge de travail :

    gcloud services enable cloudkms.googleapis.com compute.googleapis.com confidentialcomputing.googleapis.com
  4. Créez un trousseau de clés et une clé de chiffrement avec 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. Attribuez à Boba le rôle cloudkms.cryptoKeyEncrypter afin qu'il puisse utiliser la clé de chiffrement nouvellement créée pour chiffrer des données :

    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. Créez un compte de service qui sera utilisé ultérieurement par la charge de travail pour déchiffrer les données :

    gcloud iam service-accounts create BOLA_SERVICE_ACCOUNT_NAME
  7. Attribuez au compte de service le rôle cloudkms.cryptoKeyDecrypter afin qu'il puisse utiliser la clé de chiffrement que vous venez de créer pour déchiffrer des données :

    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. Créez un pool d'identités de charge de travail, puis connectez-y le compte de service avec le rôle 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. Créez un bucket Cloud Storage pour les données d'entrée, et un autre pour y stocker les résultats :

    gcloud storage buckets create gs://BOLA_INPUT_BUCKET_NAME \
        gs://BOLA_RESULTS_BUCKET_NAME
  10. Créez un fichier contenant uniquement le salaire de Bola sous forme de nombre :

    echo 111111 > BOLA_SALARY.txt
  11. Chiffrez le fichier, puis importez-le dans le bucket 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

Créer un compte de service pour la charge de travail

En plus des comptes de service configurés par Alex et Bola pour configurer leurs données, un autre compte de service est nécessaire pour exécuter la charge de travail. Étant donné que les comptes de service sont utilisés à la fois pour déchiffrer les données confidentielles et les traiter, la visibilité des données est limitée à ses propriétaires.

Dans ce guide, Bola opère et exécute la charge de travail, mais tout le monde peut assumer ces rôles, y compris en tant que tiers.

Suivez les étapes ci-dessous dans le projet de Bola pour configurer le compte de service :

  1. Créez un compte de service pour exécuter la charge de travail :

    gcloud iam service-accounts create WORKLOAD_SERVICE_ACCOUNT_NAME
    
  2. Attribuez à Bola le rôle iam.serviceAccountUser pour emprunter l'identité du compte de service, qui est nécessaire pour pouvoir créer ultérieurement une VM de charge de travail :

    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. Attribuez au compte de service le rôle confidentialcomputing.workloadUser afin qu'il puisse générer un jeton d'attestation :

    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. Attribuez au compte de service le rôle logging.logWriter pour écrire des journaux dans Cloud Logging, afin de pouvoir vérifier la progression de la charge de travail:

    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. Accordez au compte de service un accès en lecture aux buckets Alex et Bola contenant leurs données chiffrées, et écrivez un accès à chacun des buckets de résultats :

    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
    

    Cela suppose que l'utilisateur bénéficiant de l'accès dispose du rôle Administrateur de l'espace de stockage (roles/storage.admin) pour le projet contenant le bucket Cloud Storage en cours d'exploitation.

Créer la charge de travail

Dans ce guide, Alex fournit le code de la charge de travail et crée une image Docker pour le contenir, mais tout le monde peut assumer ces rôles, y compris en tant que tiers.

Alex doit créer les ressources suivantes pour la charge de travail :

  • Le code qui exécute la charge de travail.

  • Un dépôt Docker dans Artifact Registry auquel le compte de service exécutant la charge de travail a accès.

  • Une image Docker contenant et exécutant le code de charge de travail.

Pour créer et configurer les ressources, procédez comme suit dans le projet d'Alex :

  1. Passez au projet d'Alex :

    gcloud config set project ALEX_PROJECT_ID
    
  2. Créez un dépôt Docker dans Artifact Registry :

    gcloud artifacts repositories create REPOSITORY_NAME \
        --repository-format=docker \
        --location=us
    
  3. Attribuez au compte de service qui exécutera la charge de travail le rôle Lecteur Artifact Registry (roles/artifactregistry.reader) afin qu'il puisse lire les données du dépôt :

    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. Cliquez sur Ouvrir l'éditeur pour ouvrir l'éditeur Cloud Shell, puis créez un fichier appelé salary.go. Copiez le code suivant dans le fichier, puis enregistrez-le :

    // 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. Modifiez USER VARIABLES SECTION dans le code source en remplaçant les valeurs vides const par les noms de ressources pertinents, comme décrit dans les commentaires du code. Si vous avez modifié les variables d'espace réservé telles que ALEX_PROJECT_ID présentées dans ce guide, les valeurs sont incluses dans l'exemple de code suivant, que vous pouvez copier et coller sur le code existant:

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

    Assurez-vous également de mettre à jour les numéros de projet pour Alex et Bola. Vous pouvez les récupérer à l'aide de la commande suivante :

    gcloud projects describe PROJECT_ID --format="value(projectNumber)"
    
  6. Assurez-vous que toutes les parties lisent et auditent le code source.

  7. Cliquez sur Terminal > Nouveau terminal pour ouvrir un terminal dans l'éditeur Cloud Shell.

  8. Saisissez les commandes suivantes dans le terminal pour configurer l'environnement 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. Saisissez la commande suivante pour compiler le code source en un binaire statiquement lié :

    CGO_ENABLED=0 go build -trimpath
    
  10. Créez un fichier nommé Dockerfile dans l'éditeur Cloud Shell et comprenant le contenu suivant :

    FROM alpine:latest
    WORKDIR /test
    COPY salary /test
    ENTRYPOINT ["/test/salary"]
    CMD []
    
  11. Mettez à jour vos identifiants Docker pour inclure le nom de domaine us-docker.pkg.dev :

    gcloud auth configure-docker us-docker.pkg.dev
    
  12. Créez une image Docker à partir de Dockerfile en saisissant la commande suivante dans le terminal :

    docker build -t \
        us-docker.pkg.dev/ALEX_PROJECT_ID/REPOSITORY_NAME/WORKLOAD_CONTAINER_NAME:latest .
    
  13. Transférer l'image Docker dans Artifact Registry

    docker push \
        us-docker.pkg.dev/ALEX_PROJECT_ID/REPOSITORY_NAME/WORKLOAD_CONTAINER_NAME
    
  14. À partir de la réponse push Docker, copiez le condensé de l'image Docker (y compris le préfixe sha256:) dans un endroit sûr.

  15. Assurez-vous que toutes les parties auditent l'image Docker et vérifiez qu'elle est fiable avant d'autoriser son utilisation.

Autoriser la charge de travail

Une fois la charge de travail approuvée par les deux parties, Alex et Bola doivent ajouter le service de vérification d'attestation Confidential Space en tant que fournisseur à leurs pools d'identités de charge de travail. Cela permet au compte de service associé à la charge de travail d'emprunter l'identité des comptes de service connectés à leurs pools et d'accéder à leurs données, à condition que certaines conditions d'attributs soient remplies. Cela signifie que les conditions d'attribut agissent comme des règles d'attestation.

Les conditions d'attribut utilisées dans ce guide sont les suivantes :

  • Condensé de l'image Docker en cours d'exécution

  • L'adresse e-mail du compte de service qui exécute la charge de travail

Si un individu malveillant modifie l'image Docker ou si un autre compte de service est associé à la charge de travail, celle-ci n'est pas autorisée à accéder aux données d'Alex ou de Bola.

Pour afficher les conditions d'attribut disponibles, consultez la section Attestations.

Pour configurer les fournisseurs pour Alex et Bola avec les conditions requises, procédez comme suit :

  1. Saisissez la commande suivante pour créer le fournisseur pour 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. Basculez vers le projet de Bola :

    gcloud config set project BOLA_PROJECT_ID
    
  3. Saisissez la commande suivante pour créer le fournisseur pour 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"
    

Déployer la charge de travail

Une fois les fournisseurs ajoutés aux pools d'identités de charge de travail Alex et Bola et aux ressources requises en place, l'opérateur de charge de travail doit lancer l'exécution de la charge de travail.

Pour déployer la charge de travail, créez une instance de Confidential VM dans le projet de Bola qui possède les propriétés suivantes:

  • Une configuration compatible pour une instance AMD SEV ou une instance de VM Confidential VM Intel TDX.

  • OS basé sur l'image Confidential Space.

  • Démarrage sécurisé activé.

  • L'image Docker associée que Alex a créée précédemment.

  • Compte de service associé qui exécute la charge de travail.

Saisissez la commande suivante dans la session Cloud Shell de Bola pour déployer la charge de travail:

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"

Vous pouvez suivre la progression de la charge de travail dans le projet de Bola en accédant à l'explorateur de journaux.

Accéder à l'explorateur de journaux

Pour rechercher les journaux Confidential Space, filtrez les champs de journal suivants s'ils sont disponibles:

  • Type de ressource : instance de VM

  • ID d'instance : ID d'instance de la VM.

  • Nom du journal : confidential-space-launcher

Pour actualiser le journal, cliquez sur Heure actuelle.

Une fois la charge de travail terminée, l'instance de VM s'arrête. Si vous souhaitez modifier les fichiers salariaux chiffrés et redéployer la charge de travail, il vous suffit de démarrer la VM existante:

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

Déboguer la charge de travail

Vous pouvez utiliser l'explorateur de journaux pour résoudre des problèmes tels que les ressources mal configurées, ou pour attribuer des conditions aux fournisseurs qui ne correspondent pas aux revendications effectuées par la charge de travail de Confidential Space.

Pour ce faire, vous devez apporter les modifications suivantes :

  • Mettez à jour les fournisseurs de pools d'identités de charge de travail d'Alex et de Bola pour supprimer l'assertion support_attributes. Vous devez utiliser l'image de débogage de Confidential Space pour effectuer un dépannage plus approfondi. Cette image ne comporte aucun attribut d'assistance à valider.

  • Créez la VM de charge de travail à l'aide de l'image de débogage de Confidential Space, puis définissez les métadonnées de VM pour rediriger STDOUT et STDERR vers Cloud Logging afin de capturer toutes les sorties de la charge de travail.

Pour effectuer les modifications, procédez comme suit :

  1. Passez au projet d'Alex :

    gcloud config set project ALEX_PROJECT_ID
    
  2. Mettez à jour le fournisseur d'Alex pour supprimer l'assertion 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. Basculez vers le projet de Bola :

    gcloud config set project BOLA_PROJECT_ID
    
  4. Mettez à jour le fournisseur de Bola pour supprimer l'assertion 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. Créez une VM avec l'image de débogage de Confidential Space, et tee-container-log-redirect défini sur true dans les métadonnées.

    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"
    

Contrairement à l'image de production, l'image de débogage maintient la VM en cours d'exécution une fois la charge de travail terminée. Cela signifie que vous pouvez utiliser SSH pour vous connecter à votre VM et continuer le débogage.

Afficher les résultats

Une fois la charge de travail terminée, Alex et Bola peuvent afficher les résultats dans leurs buckets de résultats respectifs :

  1. Passez au projet d'Alex :

    gcloud config set project ALEX_PROJECT_ID
    
  2. Répertoriez tous les fichiers dans leur bucket de résultats :

    gcloud storage ls gs://ALEX_RESULTS_BUCKET_NAME
    

    Lisez ensuite le dernier fichier :

    gcloud storage cat gs://ALEX_RESULTS_BUCKET_NAME/ALEX_RESULTS_FILE_NAME
    
  3. Basculez vers le projet de Bola :

    gcloud config set project BOLA_PROJECT_ID
    
  4. Pour Bola, répertoriez tous les fichiers de leur bucket de résultats :

    gcloud storage ls gs://BOLA_RESULTS_BUCKET_NAME
    

    Lisez ensuite le dernier fichier :

    gcloud storage cat gs://BOLA_RESULTS_BUCKET_NAME/BOLA_RESULTS_FILE_NAME
    

En lisant les fichiers, Alex et Bola découvrent chacun qui gagne plus, sans jamais se révéler leurs salaires respectifs.

Nettoyage

Pour supprimer les ressources créées dans ce guide, suivez les instructions suivantes.

Nettoyer les ressources d'Alex

  1. Passez au projet d'Alex :

    gcloud config set project ALEX_PROJECT_ID
    
  2. Supprimez le compte de service qui déchiffre les données d'Alex :

    gcloud iam service-accounts delete \
        ALEX_SERVICE_ACCOUNT_NAME@ALEX_PROJECT_ID.iam.gserviceaccount.com
    
  3. Supprimez le pool d'identités de charge de travail d'Alex :

    gcloud iam workload-identity-pools delete ALEX_POOL_NAME \
        --location=global
    
  4. Supprimez les buckets Cloud Storage d'Alex :

    gcloud storage rm gs://ALEX_INPUT_BUCKET_NAME \
        gs://ALEX_RESULTS_BUCKET_NAME --recursive
    
  5. Supprimez les fichiers de salaire d'Alex et le code Go :

    rm ALEX_SALARY.txt \
        ALEX_ENCRYPTED_SALARY_FILE \
        salary.go salary \
        go.mod go.sum
    
  6. Facultatif: Désactivez ou détruisez la clé Cloud Key Management Service d'Alex.

  7. Facultatif : Arrêtez le projet d'Alex.

Nettoyer les ressources de Bola

  1. Basculez vers le projet de Bola :

    gcloud config set project BOLA_PROJECT_ID
    
  2. Supprimez la VM de charge de travail :

    gcloud compute instances delete WORKLOAD_VM_NAME --zone=us-west1-b
    
  3. Supprimez le compte de service qui déchiffre les données de Bola et le compte de service qui exécute la charge de travail :

    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. Supprimez le pool d'identités de charge de travail de Bola :

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

    gcloud storage rm gs://BOLA_INPUT_BUCKET_NAME \
        gs://BOLA_RESULTS_BUCKET_NAME --recursive
    
  6. Supprimez les fichiers de salaire de Bola :

    rm BOLA_SALARY.txt \
        BOLA_ENCRYPTED_SALARY_FILE
    
  7. Facultatif: Désactivez ou détruisez la clé Cloud Key Management Service de Bola.

  8. Facultatif : Arrêtez le projet de Bola.