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

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. Activez les API requises par Alex en tant que collaborateur de données et auteur de charge de travail :

    gcloud services enable \
        artifactregistry.googleapis.com \
        cloudkms.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_OUTPUT_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. Activez les API requises par Bola en tant que collaborateur de données et opérateur de charge de travail :

    gcloud services enable \
        cloudkms.googleapis.com \
        compute.googleapis.com \
        confidentialcomputing.googleapis.com \
        iamcredentials.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_OUTPUT_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 qui est 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 ultérieurement le compte de service à la 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://ALEX_OUTPUT_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_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_OUTPUT_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. 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 :

    package main
    
    import (
      "context"
      "fmt"
      "io"
      "os"
      "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
    }
    
    // The following values are pulled from environment variables
    
    // Alex's values
    var collaborator1Name string = os.Getenv("COLLAB_1_NAME") // Alex's name
    var collaborator1EncryptedSalaryFileName string = os.Getenv("COLLAB_1_ENCRYPTED_SALARY") // The name of Alex's encrypted salary file.
    var collaborator1BucketInputName string = os.Getenv("COLLAB_1_INPUT_BUCKET") // The name of the storage bucket that contains Alex's encrypted salary file.
    var collaborator1BucketOutputName string = os.Getenv("COLLAB_1_OUTPUT_BUCKET") // The name of the storage bucket to store Alex's results in.
    var collaborator1KMSKeyringName string = os.Getenv("COLLAB_1_KEYRING_NAME") // Alex's Key Management Service key ring.
    var collaborator1KMSKeyName string = os.Getenv("COLLAB_1_KEY_NAME") // Alex's Key Management Service key.
    var collaborator1ProjectName string = os.Getenv("COLLAB_1_PROJECT_ID") // Alex's project ID.
    var collaborator1ProjectNumber string = os.Getenv("COLLAB_1_PROJECT_NUMBER") // Alex's project number.
    var collaborator1PoolName string = os.Getenv("COLLAB_1_POOL_NAME") // Alex's workload identity pool name.
    
    // Bola's values
    var collaborator2Name string = os.Getenv("COLLAB_2_NAME") // Bola's name
    var collaborator2EncryptedSalaryFileName string = os.Getenv("COLLAB_2_ENCRYPTED_SALARY") // The name of Bola's encrypted salary file.
    var collaborator2BucketInputName string = os.Getenv("COLLAB_2_INPUT_BUCKET") // The name of the storage bucket that contains Bola's encrypted salary file.
    var collaborator2BucketOutputName string = os.Getenv("COLLAB_2_OUTPUT_BUCKET") // The name of the storage bucket to store Bola's results in.
    var collaborator2KMSKeyringName string = os.Getenv("COLLAB_2_KEYRING_NAME") // Bola's Key Management Service key ring.
    var collaborator2KMSKeyName string = os.Getenv("COLLAB_2_KEY_NAME") // Bola's Key Management Service key.
    var collaborator2ProjectName string = os.Getenv("COLLAB_2_PROJECT_ID") // Bola's project ID.
    var collaborator2ProjectNumber string = os.Getenv("COLLAB_2_PROJECT_NUMBER") // Bola's project number.
    var collaborator2PoolName string = os.Getenv("COLLAB_2_POOL_NAME") // Bola's workload identity pool name.
    
    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,
      },
      {
        collaborator2Name,
        "projects/" + collaborator2ProjectNumber + "/locations/global/workloadIdentityPools/" + collaborator2PoolName + "/providers/attestation-verifier",
        "projects/" + collaborator2ProjectName + "/locations/global/keyRings/" + collaborator2KMSKeyringName + "/cryptoKeys/" + collaborator2KMSKeyName,
        collaborator2BucketInputName,
        collaborator2EncryptedSalaryFileName,
        collaborator2BucketOutputName,
      },
    }
    
    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 = "You earn the same!\n"
      }
    
      now := time.Now()
      for _, cw := range collaborators {
        outputWriter := storageClient.Bucket(cw.outputBucket).Object(fmt.Sprintf("comparison-result-%d", 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
    }
    
  3. Assurez-vous que toutes les parties lisent et auditent le code source.

  4. Créez un fichier nommé Dockerfile dans l'éditeur Cloud Shell et comprenant le contenu suivant :

    # Compile the provided Go code to a statically linked binary
    FROM golang:latest AS build
    WORKDIR /build
    COPY salary.go .
    RUN go mod init salary
    RUN 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
    RUN CGO_ENABLED=0 go build -trimpath
    
    # Build the workload container image
    FROM alpine:latest AS run
    WORKDIR /test
    COPY --from=build /build/salary /test/salary
    ENTRYPOINT ["/test/salary"]
    CMD []
    
    # Allow the workload to access the following environment variables
    LABEL "tee.launch_policy.allow_env_override"="\
    COLLAB_1_NAME,\
    COLLAB_2_NAME,\
    COLLAB_1_ENCRYPTED_SALARY,\
    COLLAB_2_ENCRYPTED_SALARY,\
    COLLAB_1_INPUT_BUCKET,\
    COLLAB_2_INPUT_BUCKET,\
    COLLAB_1_OUTPUT_BUCKET,\
    COLLAB_2_OUTPUT_BUCKET,\
    COLLAB_1_KEYRING_NAME,\
    COLLAB_2_KEYRING_NAME,\
    COLLAB_1_KEY_NAME,\
    COLLAB_2_KEY_NAME,\
    COLLAB_1_PROJECT_ID,\
    COLLAB_2_PROJECT_ID,\
    COLLAB_1_PROJECT_NUMBER,\
    COLLAB_2_PROJECT_NUMBER,\
    COLLAB_1_POOL_NAME,\
    COLLAB_2_POOL_NAME"
    

    Ce Dockerfile utilise une compilation en plusieurs étapes pour compiler d'abord le code Go, puis copie la version compilée de ce code dans le conteneur de charge de travail final. Il permet également d'utiliser des variables d'environnement spécifiques dans ce conteneur de charge de travail. Les valeurs de ces variables d'environnement sont mappées ultérieurement aux ressources spécifiques sur lesquelles la charge de travail doit fonctionner.

  5. Cliquez sur Ouvrir le terminal pour revenir à Cloud Shell ou appelez le terminal intégré à l'éditeur Cloud Shell depuis le menu Afficher.

  6. Créez un dépôt Docker dans Artifact Registry :

    gcloud artifacts repositories create REPOSITORY_NAME \
        --repository-format=docker \
        --location=us
    
  7. 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
    
  8. Mettez à jour vos identifiants Docker pour inclure le nom de domaine us-docker.pkg.dev :

    gcloud auth configure-docker us-docker.pkg.dev
    
  9. 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" .
    
  10. Transférer l'image Docker dans Artifact Registry

    docker push \
        us-docker.pkg.dev/ALEX_PROJECT_ID/REPOSITORY_NAME/WORKLOAD_CONTAINER_NAME
    
  11. La réponse push Docker liste le condensé SHA256 de l'image, qui sera nécessaire ultérieurement pour autoriser la charge de travail. Le condensé ressemble à l'exemple suivant :

    sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
    

    Copiez le condensé de l'image (y compris le préfixe sha256:) dans un endroit où vous pourrez le référencer. Vous pouvez également saisir le résumé dans l'exemple de code suivant pour préremplir le reste des exemples de code de ce guide qui en ont besoin :

    WORKLOAD_CONTAINER_IMAGE_DIGEST
    
  12. 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 et 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'"
    
  2. Obtenez le numéro de projet d'Alex pour la prochaine commande :

    gcloud projects describe ALEX_PROJECT_ID --format="value(projectNumber)"
    
  3. 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
    
  4. Basculez vers le projet de Bola :

    gcloud config set project BOLA_PROJECT_ID
    
  5. 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'"
    
  6. Obtenez le numéro de projet de Bola pour la prochaine commande :

    gcloud projects describe BOLA_PROJECT_ID --format="value(projectNumber)"
    
  7. 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
    

Tester la charge de travail

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

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

Saisissez la commande suivante dans la session Cloud Shell de Bola pour tester la charge de travail :

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\
~tee-env-COLLAB_1_NAME=Alex\
~tee-env-COLLAB_2_NAME=Bola\
~tee-env-COLLAB_1_ENCRYPTED_SALARY=ALEX_ENCRYPTED_SALARY_FILE\
~tee-env-COLLAB_2_ENCRYPTED_SALARY=BOLA_ENCRYPTED_SALARY_FILE\
~tee-env-COLLAB_1_INPUT_BUCKET=ALEX_INPUT_BUCKET_NAME\
~tee-env-COLLAB_2_INPUT_BUCKET=BOLA_INPUT_BUCKET_NAME\
~tee-env-COLLAB_1_OUTPUT_BUCKET=ALEX_OUTPUT_BUCKET_NAME\
~tee-env-COLLAB_2_OUTPUT_BUCKET=BOLA_OUTPUT_BUCKET_NAME\
~tee-env-COLLAB_1_KEYRING_NAME=ALEX_KEYRING_NAME\
~tee-env-COLLAB_2_KEYRING_NAME=BOLA_KEYRING_NAME\
~tee-env-COLLAB_1_KEY_NAME=ALEX_KEY_NAME\
~tee-env-COLLAB_2_KEY_NAME=BOLA_KEY_NAME\
~tee-env-COLLAB_1_PROJECT_ID=ALEX_PROJECT_ID\
~tee-env-COLLAB_2_PROJECT_ID=BOLA_PROJECT_ID\
~tee-env-COLLAB_1_PROJECT_NUMBER=ALEX_PROJECT_NUMBER\
~tee-env-COLLAB_2_PROJECT_NUMBER=BOLA_PROJECT_NUMBER\
~tee-env-COLLAB_1_POOL_NAME=ALEX_POOL_NAME\
~tee-env-COLLAB_2_POOL_NAME=BOLA_POOL_NAME"

Afficher la progression

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 n'afficher que les entrées de journal 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. Vous pouvez également faire défiler l'écran vers le haut pour afficher les résultats précédents, puis de nouveau vers le bas pour charger les dernières entrées.

Afficher les résultats

Si la tâche de charge de travail se termine et renvoie 0, cela signifie qu'aucune erreur ne s'est produite. Il est temps de vérifier le résultat dans les buckets de sortie d'Alex et de Bola :

  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_OUTPUT_BUCKET_NAME
    
  3. Lisez le dernier fichier listé, en remplaçant ALEX_OUTPUT_CLOUD_STORAGE_PATH par le chemin d'accès au fichier, y compris gs:// :

    gcloud storage cat ALEX_OUTPUT_CLOUD_STORAGE_PATH
    

    Si aucun fichier n'est présent, vous devez déboguer votre charge de travail.

  4. Basculez vers le projet de Bola :

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

    gcloud storage ls gs://BOLA_OUTPUT_BUCKET_NAME
    
  6. Lisez le dernier fichier listé, en remplaçant BOLA_RESULTS_CLOUD_STORAGE_PATH par le chemin d'accès au fichier, y compris gs:// :

    gcloud storage cat BOLA_RESULTS_CLOUD_STORAGE_PATH
    

    Si aucun fichier n'est présent, vous devez déboguer votre charge de travail.

  7. Une fois que vous avez lu les résultats, arrêtez l'instance de VM :

    gcloud compute instances stop WORKLOAD_VM_2_NAME --zone=us-west1-b
    

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

Déboguer et redémarrer la charge de travail

Un environnement Confidential Space comporte de nombreux éléments. Il est possible qu'un élément ait été mal configuré, ce qui entraîne l'échec de la charge de travail.

Contrairement à l'image Confidential Space de production, l'image de débogage maintient l'instance de VM en cours d'exécution une fois la charge de travail terminée. Cela signifie que si les journaux ne révèlent pas suffisamment d'informations pour résoudre votre problème, l'étape suivante consiste à vous connecter à votre instance de VM via SSH et à continuer le débogage.

Une fois le débogage terminé, arrêtez l'instance de VM :

gcloud compute instances stop WORKLOAD_VM_2_NAME --zone=us-west1-b

Pour exécuter la charge de travail dans l'environnement débogué, redémarrez la VM :

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

Renforcer l'environnement pour la production

Une fois que vous avez testé la charge de travail, il est temps de renforcer l'environnement Confidential Space pour le déploiement en production. Alex et Bola doivent ajouter une assertion support_attributes à leurs fournisseurs pour vérifier que l'image Confidential Space de production est utilisée pour la charge de travail :

  1. Passez au projet d'Alex :

    gcloud config set project ALEX_PROJECT_ID
    
  2. Saisissez la commande suivante pour mettre à jour le fournisseur pour Alex :

    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' \
            && 'STABLE' in assertion.submods.confidential_space.support_attributes"
    
  3. Basculez vers le projet de Bola :

    gcloud config set project BOLA_PROJECT_ID
    
  4. Saisissez la commande suivante pour mettre à jour le fournisseur pour Bola :

    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' \
            && 'STABLE' in assertion.submods.confidential_space.support_attributes"
    

Déployer la charge de travail de production

Bola doit créer une instance de VM distincte pour exécuter la charge de travail de production. Les éléments suivants sont différents de la charge de travail de test :

  • L'OS est basé sur l'image de production Confidential Space. Le protocole SSH est désactivé et l'instance de VM s'arrête une fois la charge de travail terminée.

  • La redirection de la journalisation est supprimée. Seuls les journaux de base qui n'exposent aucune information sensible sont affichés dans Cloud Logging.

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

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\
~tee-env-COLLAB_1_NAME=Alex\
~tee-env-COLLAB_2_NAME=Bola\
~tee-env-COLLAB_1_ENCRYPTED_SALARY=ALEX_ENCRYPTED_SALARY_FILE\
~tee-env-COLLAB_2_ENCRYPTED_SALARY=BOLA_ENCRYPTED_SALARY_FILE\
~tee-env-COLLAB_1_INPUT_BUCKET=ALEX_INPUT_BUCKET_NAME\
~tee-env-COLLAB_2_INPUT_BUCKET=BOLA_INPUT_BUCKET_NAME\
~tee-env-COLLAB_1_OUTPUT_BUCKET=ALEX_OUTPUT_BUCKET_NAME\
~tee-env-COLLAB_2_OUTPUT_BUCKET=BOLA_OUTPUT_BUCKET_NAME\
~tee-env-COLLAB_1_KEYRING_NAME=ALEX_KEYRING_NAME\
~tee-env-COLLAB_2_KEYRING_NAME=BOLA_KEYRING_NAME\
~tee-env-COLLAB_1_KEY_NAME=ALEX_KEY_NAME\
~tee-env-COLLAB_2_KEY_NAME=BOLA_KEY_NAME\
~tee-env-COLLAB_1_PROJECT_ID=ALEX_PROJECT_ID\
~tee-env-COLLAB_2_PROJECT_ID=BOLA_PROJECT_ID\
~tee-env-COLLAB_1_PROJECT_NUMBER=ALEX_PROJECT_NUMBER\
~tee-env-COLLAB_2_PROJECT_NUMBER=BOLA_PROJECT_NUMBER\
~tee-env-COLLAB_1_POOL_NAME=ALEX_POOL_NAME\
~tee-env-COLLAB_2_POOL_NAME=BOLA_POOL_NAME"

La façon dont vous affichez la progression et les résultats est la même que lorsque vous avez testé la charge de travail.

Une fois la charge de travail de production terminée, l'instance de VM s'arrête. Pour afficher différents résultats, vous pouvez modifier les salaires, les chiffrer de nouveau, les importer de nouveau dans les buckets Cloud Storage respectifs, puis redémarrer l'instance de VM pour exécuter à nouveau la charge de travail :

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

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_OUTPUT_BUCKET_NAME --recursive
    
  4. Supprimez les fichiers de salaire d'Alex, le code Go et Dockerfile :

    rm ALEX_SALARY.txt \
        ALEX_ENCRYPTED_SALARY_FILE \
        salary.go \
        Dockerfile
    
  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 qui a exécuté le workflow de test :

    gcloud compute instances delete WORKLOAD_VM_2_NAME --zone=us-west1-b
    
  3. Supprimez la VM qui a exécuté le workflow de production :

    gcloud compute instances delete WORKLOAD_VM_NAME --zone=us-west1-b
    
  4. Supprimez le compte de service qui a exécuté la charge de travail :

    gcloud iam service-accounts delete \
        WORKLOAD_SERVICE_ACCOUNT_NAME@BOLA_PROJECT_ID.iam.gserviceaccount.com
    
  5. Supprimez le pool d'identités de charge de travail de Bola :

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

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

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

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