יצירת סביבת Confidential Space ראשונה

במדריך הזה, אלכס ודניאל רוצים לגלות למי יש את המשכורת הגבוהה ביותר בלי לחשוף את המספרים אחד לשני. הם מחליטים להשתמש ב-Confidential Space כדי לשמור על סודיות הנתונים, ומסכימים לקחת על עצמם את התפקידים הבאים:

  • אלכס: שותף לעריכת נתונים, יוצר עומסי עבודה

  • בלה: שותפה בנתונים, מפעילה של עומס עבודה

הסידור הזה נועד לשמור על פשטות ככל האפשר במדריך הזה. עם זאת, יכול להיות שיוצר עומס העבודה והמפעיל שלו יהיו בלתי תלויים לחלוטין בשותפי העריכה של הנתונים, ויכולים להיות כמה שותפי עריכה שרוצים.

לפני שמתחילים

במדריך הזה מוצג תרחיש של Confidential Space באמצעות חשבון אחד בארגון אחד עם גישה לכמה פרויקטים, כדי שתוכלו להתנסות בכל התהליך. בפריסה בסביבת ייצור, לשותפי העריכה, ליוצרי עומסי העבודה ולמפעילי עומסי העבודה יש חשבונות נפרדים ופרויקטים משלהם שכלולים בארגונים נפרדים, שאין להם גישה זה לזה והנתונים הסודיים שלהם נשמרים בנפרד.

כדי להפיק את התוצאות, Confidential Space יכול לקיים אינטראקציה עם הרבה מהשירותים של Google Cloud, כולל, בין היתר:

המדריך הזה מתבסס על הבנה בסיסית של כל התכונות האלה.

התפקידים הנדרשים

כדי לקבל את ההרשאות שדרושות להשלמת המדריך, בקשו מהאדמין להקצות לכם את התפקידים הבאים ב-IAM ברמת הפרויקט:

  • אדמין של Cloud KMS‏ (roles/cloudkms.admin) לשותפי המידע (אלכס ובלה).
  • אדמין של מאגר זהויות של עומסי עבודה ב-IAM‏ (roles/iam.workloadIdentityPoolAdmin) לשותפי המידע (אלכס ובולה).
  • אדמין של שימוש בשירות (roles/serviceusage.serviceUsageAdmin) לשותפי המידע (אלכס ודני).
  • אדמין של Storage‏ (roles/storage.admin) לשותפי הנתונים (אלכס ובולה) ולמפעיל עומס העבודה (בולה).
  • אדמין בחשבון שירות (roles/iam.serviceAccountAdmin) למפעיל עומס העבודה (Bola).
  • אדמין של Compute‏ (roles/compute.admin) למפעיל עומס העבודה (בלה).
  • אדמין לענייני אבטחה (roles/securityAdmin) למפעיל עומס העבודה (Bola).
  • אדמין של Artifact Registry‏ (roles/artifactregistry.admin) ליוצר של עומס העבודה (אלכס).

להסבר על מתן תפקידים, ראו איך מנהלים את הגישה ברמת הפרויקט, התיקייה והארגון.

יכול להיות שאפשר לקבל את ההרשאות הנדרשות גם באמצעות תפקידים בהתאמה אישית או תפקידים מוגדרים מראש.

הגדרת משאבים של שותפי נתונים

גם אלכס וגם בולה צריכים פרויקטים נפרדים שמכילים את המשאבים הבאים:

  • המידע הסודי עצמו.

  • מפתח הצפנה כדי להצפין את הנתונים ולשמור על הסודיות שלהם.

  • קטגוריה של Cloud Storage לאחסון הנתונים המוצפנים.

  • מאגר זהויות של עומסי עבודה. עומס העבודה שמעבד את הנתונים הסודיים משתמש במאגר כדי לגשת לנתונים הפרטיים ולפענח אותם.

כדי להתחיל, נכנסים למסוף Google Cloud :

כניסה למסוף Google Cloud

הגדרת המשאבים של אלכס

כדי להגדיר את המשאבים של אלכס, מבצעים את ההוראות הבאות.

  1. לוחצים על Activate Cloud Shell.
  2. ב-Cloud Shell, מזינים את הפקודה הבאה כדי ליצור פרויקט לאלכס, ומחליפים את ALEX_PROJECT_ID בשם שבוחרים:

    gcloud projects create ALEX_PROJECT_ID
  3. עוברים לפרויקט החדש שנוצר:

    gcloud config set project ALEX_PROJECT_ID
  4. מפעילים את ממשקי ה-API שאלכס צריך בתור שותף מידע ויוצר עומסי עבודה:

    gcloud services enable \
        artifactregistry.googleapis.com \
        cloudkms.googleapis.com \
        iamcredentials.googleapis.com
  5. יוצרים אוסף מפתחות ומפתח הצפנה באמצעות 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. נותנים לאלכס את התפקיד cloudkms.cryptoKeyEncrypter כדי שיוכל להשתמש במפתח ההצפנה החדש שנוצר להצפנת נתונים:

    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. יוצרים את מאגר הזהויות של עומסי העבודה של אלכס:

    gcloud iam workload-identity-pools create ALEX_POOL_NAME \
        --location=global
  8. יוצרים קטגוריה של Cloud Storage לנתוני הקלט, וקטגוריה נוספת לאחסון התוצאות:

    gcloud storage buckets create gs://ALEX_INPUT_BUCKET_NAME \
        gs://ALEX_OUTPUT_BUCKET_NAME
  9. יוצרים קובץ שמכיל רק את המשכורת של אלכס כמספר:

    echo 123456 > ALEX_SALARY.txt
  10. מצפינים את הקובץ ומעלים אותו לקטגוריה של אלכס:

    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

הגדרת המשאבים של Bola

כדי להגדיר את המשאבים של Bola, פועלים לפי ההוראות הבאות.

  1. ב-Cloud Shell, מזינים את הפקודה הבאה כדי ליצור פרויקט בשביל בלה, ומחליפים את BOLA_PROJECT_ID בשם שבוחרים:

    gcloud projects create BOLA_PROJECT_ID
  2. עוברים לפרויקט החדש שנוצר:

    gcloud config set project BOLA_PROJECT_ID
  3. מפעילים את ממשקי ה-API שנדרשים ל-Bola כשותף מידע וכמפעיל עומסי עבודה:

    gcloud services enable \
        cloudkms.googleapis.com \
        compute.googleapis.com \
        confidentialcomputing.googleapis.com \
        iamcredentials.googleapis.com
  4. יוצרים אוסף מפתחות ומפתח הצפנה באמצעות 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. נותנים ל-Bola את התפקיד cloudkms.cryptoKeyEncrypter כדי שיוכל להשתמש במפתח ההצפנה החדש שנוצר להצפנת נתונים:

    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. יוצרים את מאגר הזהויות של עומסי העבודה של Bola:

    gcloud iam workload-identity-pools create BOLA_POOL_NAME \
        --location=global
  7. יוצרים קטגוריה של Cloud Storage לנתוני הקלט, וקטגוריה נוספת לאחסון התוצאות:

    gcloud storage buckets create gs://BOLA_INPUT_BUCKET_NAME \
        gs://BOLA_OUTPUT_BUCKET_NAME
  8. יוצרים קובץ שמכיל רק את השכר של Bola כמספר:

    echo 111111 > BOLA_SALARY.txt
  9. מצפינים את הקובץ ומעלים אותו לקטגוריה של 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

יצירת חשבון שירות לעומס העבודה

במדריך הזה, בולה מפעילה ומריצה את עומס העבודה, אבל כל אחד יכול לקחת על עצמו את התפקידים האלה, כולל צד שלישי. למכונה הווירטואלית שנוצרה על ידי בועז כדי להריץ את עומס העבודה, מצורף חשבון שירות שיש לו הרשאה ליצור אסימוני אימות, לכתוב יומנים, לקרוא את הנתונים המוצפנים של אלכס ובועז ולכתוב תוצאות לקטגוריות ספציפיות של Cloud Storage.

כדי להגדיר את חשבון השירות, מבצעים את השלבים הבאים בפרויקט של בלה:

  1. יוצרים חשבון שירות כדי להריץ את עומס העבודה:

    gcloud iam service-accounts create WORKLOAD_SERVICE_ACCOUNT_NAME
    
  2. מקצים ל-Bola את התפקיד iam.serviceAccountUser, כדי שהיא תוכל לצרף את חשבון השירות למכונה הווירטואלית של עומס העבודה בהמשך:

    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. מקצים לחשבון השירות את התפקיד confidentialcomputing.workloadUser כדי שיוכל לבקש אסימון אימות:

    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. מקצים לחשבון השירות את התפקיד logging.logWriter כדי לכתוב יומנים ב-Cloud Logging, וכך לבדוק את התקדמות עומס העבודה:

    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. נותנים לחשבון השירות גישת קריאה לקטגוריות של אלכס ושל בולה שמכילות את הנתונים המוצפנים שלהם, וגישת כתיבה לכל אחת מקטגוריות התוצאות שלהם:

    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
    

    ההנחה היא שלמשתמש שמעניק את הגישה יש את התפקיד "אדמין של Storage" (roles/storage.admin) בפרויקט שמכיל את הקטגוריה של Cloud Storage שבה מתבצעת הפעולה.

יצירת עומס העבודה

במדריך הזה, אלכס מספק את הקוד לעומס העבודה ויוצר קובץ אימג' של Docker שיכיל אותו, אבל כל אחד יכול לקחת על עצמו את התפקידים האלה, כולל צד שלישי.

אלכס צריך ליצור את המשאבים הבאים לעומס העבודה:

  • הקוד שמבצע את עומס העבודה.

  • מאגר Docker ב-Artifact Registry, שלחשבון השירות שמריץ את עומס העבודה יש גישה אליו.

  • קובץ אימג' של Docker שמכיל ומריץ את קוד עומס העבודה.

כדי ליצור ולהגדיר את המשאבים, מבצעים את השלבים הבאים בפרויקט של אלכס:

  1. עוברים לפרויקט של אלכס:

    gcloud config set project ALEX_PROJECT_ID
    
  2. לוחצים על Open editor כדי לפתוח את Cloud Shell Editor, ויוצרים קובץ חדש בשם salary.go. מעתיקים את הקוד הבא לקובץ ושומרים אותו:

    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. חשוב לוודא שכל הצדדים קוראים את קוד המקור ובודקים אותו.

  4. יוצרים קובץ בשם Dockerfile ב-Cloud Shell Editor עם התוכן הבא:

    # 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"
    
    # Restrict the container to run only in non-root mode.
    LABEL "tee.launch_policy.nonroot_container"="nonrootonly"
    

    הקובץ Dockerfile משתמש ב-build בכמה שלבים כדי לקמפל קודם את קוד ה-Go, ואז מעתיק את הגרסה המקומפלת של הקוד הזה למאגר עומס העבודה הסופי. בנוסף, היא מאפשרת להשתמש במשתני סביבה ספציפיים בקונטיינר של עומס העבודה, ומגבילה את הקונטיינר להפעלה במצב שאינו בסיס. הערכים של משתני הסביבה האלה ממופים בהמשך למשאבים הספציפיים שעומס העבודה צריך לפעול בהם.

  5. לוחצים על Open Terminal כדי לחזור ל-Cloud Shell, או מפעילים את הטרמינל המובנה של Cloud Shell Editor מהתפריט View.

  6. יוצרים מאגר Docker ב-Artifact Registry:

    gcloud artifacts repositories create REPOSITORY_NAME \
        --repository-format=docker \
        --location=us
    
  7. מקצים לחשבון השירות שיריץ את עומס העבודה את התפקיד "קריאה מ-Artifact Registry" (roles/artifactregistry.reader), כדי שיוכל לקרוא מהמאגר:

    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. מעדכנים את פרטי הכניסה ל-Docker כך שיכללו את שם הדומיין של us-docker.pkg.dev:

    gcloud auth configure-docker us-docker.pkg.dev
    
  9. יוצרים קובץ אימג' של Docker מ-Dockerfile על ידי הזנת הפקודה הבאה במסוף:

    docker build -t \
        "us-docker.pkg.dev/ALEX_PROJECT_ID/\
    REPOSITORY_NAME/WORKLOAD_CONTAINER_NAME:latest" .
    
  10. מעבירים את קובץ האימג' של Docker ל-Artifact Registry:

    docker push \
        us-docker.pkg.dev/ALEX_PROJECT_ID/REPOSITORY_NAME/WORKLOAD_CONTAINER_NAME
    
  11. בתגובה של Docker לפקודת ה-push מופיע תקציר SHA256 של קובץ האימג', שנדרש בהמשך כדי לאשר את עומס העבודה. התקציר נראה בערך כמו בדוגמה הבאה:

    sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
    

    מעתיקים את תקציר התמונה (כולל הקידומת sha256:) למקום שבו תוכלו להשתמש בו. אפשר גם להזין את התקציר בדוגמת הקוד הבאה כדי למלא מראש את שאר דוגמאות הקוד במדריך הזה שצריכות את הערך:

    WORKLOAD_CONTAINER_IMAGE_DIGEST
    
  12. חשוב לוודא שכל הצדדים בודקים את קובץ האימג' של Docker ומוודאים שהוא מהימן לפני שהם מאשרים את השימוש בו.

אישור עומס העבודה

אחרי ששני הצדדים אישרו את עומס העבודה, אלכס ובולה צריכים להוסיף את Google Cloud Attestation כספק למאגרי הזהויות של עומסי העבודה שלהם. הספק מציין את שירות האימות שבו צריך להשתמש, ואת המאפיינים שצריכים להתאים לעומס העבודה כדי שיוכל לפעול על הנתונים של אלכס או של בולה. אם גורם זדוני משנה את קובץ האימג' של Docker או מאפיין נמדד אחר, עומס העבודה לא מקבל גישה.

במדריך הזה משתמשים במיפוי מאפיינים כדי לספק גישה ישירה למשאבים לעומס העבודה, על סמך תקציר התמונה. אבל במצבים אחרים, יכול להיות שתעדיפו להשתמש בהתחזות לחשבון שירות כדי לגשת למשאבים. מידע נוסף זמין במאמר גישה לעומסי עבודה חיצוניים.

כדי להגדיר את הספקים של אלכס ושל בולה עם התנאים הנדרשים, מבצעים את השלבים הבאים:

  1. מזינים את הפקודה הבאה כדי ליצור את הספק של אלכס:

    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. משיגים את מספר הפרויקט של אלכס בשביל הפקודה הבאה:

    gcloud projects describe ALEX_PROJECT_ID --format="value(projectNumber)"
    
  3. מקצים לזהות המאוחדת שהוגדרה על ידי הספק של אלכס את התפקיד cloudkms.cryptoKeyDecrypter, ומציינים את המאפיין image_digest כדי שרק קונטיינרים של עומסי עבודה עם ה-digest שצוין יוכלו לפענח את מפתחות ה-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. מעבר לפרויקט של בולה:

    gcloud config set project BOLA_PROJECT_ID
    
  5. מזינים את הפקודה הבאה כדי ליצור את הספק של 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. משיגים את מספר הפרויקט של Bola בשביל הפקודה הבאה:

    gcloud projects describe BOLA_PROJECT_ID --format="value(projectNumber)"
    
  7. מקצים לזהות המאוחדת שמוגדרת על ידי הספק של בולה את התפקיד cloudkms.cryptoKeyDecrypter, ומציינים את המאפיין image_digest כדי שרק קונטיינרים של עומסי עבודה עם סיכום הנתונים שצוין יוכלו לפענח את מפתחות ה-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
    

בדיקת עומס העבודה

אחרי שהוספנו ספקים למאגרי הזהויות של עומסי העבודה של אלכס ושל בלה, והמשאבים הנדרשים נמצאים במקום, מפעיל עומס העבודה יכול לבדוק את עומס העבודה.

כדי לבדוק את עומס העבודה, אתם יוצרים מכונה חדשה של Confidential VM בפרויקט של בולה, עם המאפיינים הבאים:

כדי לבדוק את עומס העבודה, מזינים את הפקודה הבאה ב-Cloud Shell של בלה:

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-nonroot-container=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"

הצגת ההתקדמות

אפשר לראות את ההתקדמות של עומס העבודה בפרויקט של בלה ב-Logs Explorer.

כניסה לדף Logs Explorer

כדי להציג רק את רשומות היומן של Confidential Space, מסננים לפי שדות היומן הבאים, אם הם זמינים:

  • ‫Resource type: ‏VM Instance

  • ‫Instance ID: מזהה המכונה הווירטואלית

  • שם היומן: confidential-space-launcher

כדי לרענן את היומן, לוחצים על מעבר לעכשיו. אפשר גם לגלול לתוצאות קודמות, ואז לגלול שוב לסוף היומן כדי לטעון את הרשומות האחרונות.

צפייה בתוצאות

אם המשימה של עומס העבודה מסתיימת ומחזירה את הערך 0, סימן שלא היו שגיאות ואפשר לבדוק את הפלט בקטגוריות הפלט של אלכס ושל בולה:

  1. עוברים לפרויקט של אלכס:

    gcloud config set project ALEX_PROJECT_ID
    
  2. מציגים את כל הקבצים בקטגוריית התוצאות:

    gcloud storage ls gs://ALEX_OUTPUT_BUCKET_NAME
    
  3. קוראים את הקובץ האחרון שמופיע ברשימה, ומחליפים את ALEX_OUTPUT_CLOUD_STORAGE_PATH בנתיב של הקובץ, כולל gs://:

    gcloud storage cat ALEX_OUTPUT_CLOUD_STORAGE_PATH
    

    אם אין קובץ, צריך לנפות באגים בעומס העבודה.

  4. מעבר לפרויקט של בולה:

    gcloud config set project BOLA_PROJECT_ID
    
  5. מציגים את כל הקבצים בקטגוריית התוצאות:

    gcloud storage ls gs://BOLA_OUTPUT_BUCKET_NAME
    
  6. קוראים את הקובץ האחרון שמופיע ברשימה, ומחליפים את BOLA_RESULTS_CLOUD_STORAGE_PATH בנתיב של הקובץ, כולל gs://:

    gcloud storage cat BOLA_RESULTS_CLOUD_STORAGE_PATH
    

    אם אין קובץ, צריך לנפות באגים בעומס העבודה.

  7. אחרי שקוראים את התוצאות, עוצרים את מכונת ה-VM:

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

כשהם קוראים את הקבצים, אלכס ודני מגלים מי מרוויח יותר בלי לחשוף את המשכורות שלהם אחד לשני.

ניפוי באגים והפעלה מחדש של עומס העבודה

סביבת Confidential Space מורכבת מהרבה חלקים, ויכול להיות שמשהו הוגדר באופן שגוי וגורם לעומס העבודה להיכשל.

בניגוד לתמונה של Confidential Space בסביבת הייצור, תמונת ניפוי הבאגים שומרת על מכונת ה-VM פועלת אחרי שעומס העבודה מסתיים. כלומר, אם היומנים לא מספקים מספיק מידע כדי לפתור את הבעיה, השלב הבא הוא להתחבר למכונה הווירטואלית באמצעות SSH ולהמשיך בניפוי הבאגים.

אחרי שמסיימים את ניפוי הבאגים, עוצרים את מכונת ה-VM:

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

כדי להריץ את עומס העבודה בסביבה שעברה ניפוי באגים, מפעילים מחדש את ה-VM:

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

הקשחת הסביבה לייצור

אחרי בדיקת עומס העבודה, הגיע הזמן להקשיח את סביבת Confidential Space לפריסה בסביבת הייצור. אלכס ובולה צריכים להוסיף טענת נכוֹנוּת (assertion) של support_attributes לספקים שלהם כדי לוודא שמשתמשים בתמונת הייצור של Confidential Space לעומס העבודה:

  1. עוברים לפרויקט של אלכס:

    gcloud config set project ALEX_PROJECT_ID
    
  2. מזינים את הפקודה הבאה כדי לעדכן את הספק של אלכס:

    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. מעבר לפרויקט של בולה:

    gcloud config set project BOLA_PROJECT_ID
    
  4. מזינים את הפקודה הבאה כדי לעדכן את הספק של 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"
    

פריסת עומס העבודה בסביבת הייצור

היא צריכה ליצור מכונה נפרדת של VM כדי להריץ את עומס העבודה בסביבת הייצור. הדברים הבאים שונים בהשוואה לעומס העבודה לבדיקה:

  • מערכת ההפעלה מבוססת על התמונה של Confidential Space בסביבת הייצור. במכונה הזו מושבת SSH, והיא מפסיקה לפעול אחרי שעומס העבודה מסתיים.

  • ההפניה האוטומטית של הרישום ביומן הוסרה. ב-Cloud Logging מוצגים רק יומנים בסיסיים שלא חושפים מידע רגיש.

מזינים את הפקודה הבאה ב-Cloud Shell של Bola כדי לפרוס את עומס העבודה בסביבת הייצור:

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-nonroot-container=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"

הדרך שבה צופים בהתקדמות וצופים בתוצאות זהה לדרך שבה בדקתם את עומס העבודה.

כשהעומס של סביבת הייצור מסתיים, המכונה הווירטואלית נעצרת. כדי לראות תוצאות שונות, אתם יכולים לשנות את המשכורות, להצפין אותן מחדש, להעלות אותן מחדש לקטגוריות המתאימות של Cloud Storage ואז להפעיל מחדש את מכונת ה-VM כדי להריץ שוב את עומס העבודה:

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

הסרת המשאבים

כדי להסיר את המשאבים שנוצרו במדריך הזה, מבצעים את ההוראות הבאות.

מחיקת המשאבים של אלכס

  1. עוברים לפרויקט של אלכס:

    gcloud config set project ALEX_PROJECT_ID
    
  2. מחיקת מאגר הזהויות של עומסי העבודה של אלכס:

    gcloud iam workload-identity-pools delete ALEX_POOL_NAME \
        --location=global
    
  3. מחיקת הקטגוריות של אלכס ב-Cloud Storage:

    gcloud storage rm gs://ALEX_INPUT_BUCKET_NAME \
        gs://ALEX_OUTPUT_BUCKET_NAME --recursive
    
  4. מוחקים את קובצי השכר של אלכס, את קוד Go ואת Dockerfile:

    rm ALEX_SALARY.txt \
        ALEX_ENCRYPTED_SALARY_FILE \
        salary.go \
        Dockerfile
    
  5. אופציונלי: משביתים או משמידים את המפתח של אלכס ב-Cloud Key Management Service.

  6. אופציונלי: השבתת הפרויקט של אלכס.

מחיקת המשאבים של בולה

  1. מעבר לפרויקט של בולה:

    gcloud config set project BOLA_PROJECT_ID
    
  2. מוחקים את ה-VM שהריצה את תהליך העבודה לבדיקה:

    gcloud compute instances delete WORKLOAD_VM_2_NAME --zone=us-west1-b
    
  3. מוחקים את ה-VM שהריץ את תהליך העבודה בסביבת הייצור:

    gcloud compute instances delete WORKLOAD_VM_NAME --zone=us-west1-b
    
  4. מוחקים את חשבון השירות שהריץ את עומס העבודה:

    gcloud iam service-accounts delete \
        WORKLOAD_SERVICE_ACCOUNT_NAME@BOLA_PROJECT_ID.iam.gserviceaccount.com
    
  5. מחיקת מאגר הזהויות של עומסי העבודה של Bola:

    gcloud iam workload-identity-pools delete BOLA_POOL_NAME \
        --location=global
    
  6. מחיקת הקטגוריות של בלה ב-Cloud Storage:

    gcloud storage rm gs://BOLA_INPUT_BUCKET_NAME \
        gs://BOLA_OUTPUT_BUCKET_NAME --recursive
    
  7. מחיקת קובצי השכר של בולה:

    rm BOLA_SALARY.txt \
        BOLA_ENCRYPTED_SALARY_FILE
    
  8. אופציונלי: להשבית או להשמיד את המפתח של בלה ב-Cloud Key Management Service.

  9. אופציונלי: סגירת הפרויקט של בלה.