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 au maximum ce 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 de 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 (projets d'Alex et Bola)

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 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 de compte de service (roles/iam.serviceAccountAdmin) pour 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 Gérer l'accès aux projets, aux dossiers et aux organisations.

Vous pouvez également obtenir les autorisations requises avec 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 pool d'identités de charge de travail. La charge de travail qui traite les données confidentielles utilise le pool pour accéder aux données privées et les déchiffrer.

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

Accéder à 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 le pool d'identités de charge de travail d'Alex :

    gcloud iam workload-identity-pools create ALEX_POOL_NAME \
        --location=global
  8. 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
  9. Créez un fichier contenant uniquement le salaire d'Alex en tant que nombre :

    echo 123456 > ALEX_SALARY.txt
  10. 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 iamcredentials.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 le pool d'identités de charge de travail de Bola :

    gcloud iam workload-identity-pools create BOLA_POOL_NAME \
        --location=global
  7. 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
  8. Créez un fichier contenant uniquement le salaire de Bola sous forme de nombre :

    echo 111111 > BOLA_SALARY.txt
  9. 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

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. L'instance de VM que Bola crée pour exécuter la charge de travail est associée à un compte de service autorisé à générer des jetons d'attestation, à écrire des journaux, à lire les données chiffrées d'Alex et de Bola, et à écrire les résultats dans des buckets Cloud Storage spécifiques.

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 qu'il puisse associer le compte de service à la VM de charge de travail ultérieurement :

    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 (
      "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. 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
    
    // 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
    

    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. Elle sera utilisée ultérieurement pour autoriser la charge de travail.

  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 Google Cloud Attestation en tant que fournisseur à leurs pools d'identités de charge de travail. Le fournisseur spécifie le service d'attestation à utiliser, ainsi que les propriétés auxquelles la charge de travail doit correspondre pour être autorisée à fonctionner sur les données d'Alex ou de Bola. Si un individu malveillant modifie l'image Docker ou une autre propriété mesurée, l'accès à la charge de travail est refusé.

Ce guide utilise des mappages d'attributs pour fournir un accès direct aux ressources à la charge de travail en fonction du résumé de l'image. Toutefois, dans d'autres situations, vous préférerez peut-être utiliser l'emprunt d'identité de compte de service pour accéder aux ressources. Pour en savoir plus, consultez Accès aux charges de travail externes.

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=\"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. Accordez à l'identité fédérée définie par le fournisseur d'Alex le rôle cloudkms.cryptoKeyDecrypter, en spécifiant l'attribut image_digest afin que seuls les conteneurs de charge de travail avec le résumé spécifié puissent déchiffrer leurs clés 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. Basculez vers le projet de Bola :

    gcloud config set project BOLA_PROJECT_ID
    
  4. 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=\"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. Attribuez le rôle cloudkms.cryptoKeyDecrypter à l'identité fédérée définie par le fournisseur de Bola, en spécifiant l'attribut image_digest afin que seuls les conteneurs de charge de travail avec le résumé spécifié puissent déchiffrer leurs clés 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
    

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 Confidential VM dans le projet de Bola qui possède les propriétés suivantes :

  • Une configuration compatible pour une instance de Confidential VM avec AMD SEV, Intel TDX ou Intel TDX avec NVIDIA Confidential Computing (preview).

  • 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 \
    --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 déployer à nouveau la charge de travail, il vous suffit de démarrer la VM existante :

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

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.

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=\"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. 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=\"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. 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.

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 pool d'identités de charge de travail d'Alex :

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

    gcloud storage rm gs://ALEX_INPUT_BUCKET_NAME \
        gs://ALEX_RESULTS_BUCKET_NAME --recursive
    
  4. 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
    
  5. Facultatif : Désactivez ou détruisez la clé Cloud Key Management Service d'Alex.

  6. 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 exécute la charge de travail :

    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.