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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  • הנתונים הסודיים עצמם.

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

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

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

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

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

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

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

  1. לוחצים על הפעלת 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, פועלים לפי ההוראות הבאות.

  1. ב-Cloud Shell, מזינים את הפקודה הבאה כדי ליצור פרויקט עבור Bola, ומחליפים את 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. נותנים לבולה את ההרשאה 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. יוצרים קובץ שמכיל רק את השכר של בולה כמספר:

    echo 111111 > BOLA_SALARY.txt
  9. מצפינים את הקובץ ואז מעלים אותו לקטגוריה של בולה:

    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
    

    ההנחה היא שלמשתמש שמעניק את הגישה יש את התפקיד 'אדמין לניהול אחסון' (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"
    

    ה-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:) למקום שאפשר יהיה להפנות אליו. אפשר גם להזין את ה-digest בדוגמת הקוד הבאה כדי למלא מראש את שאר דוגמאות הקוד במדריך הזה שצריכות את הערך:

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

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

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

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

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

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

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

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

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

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

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

מזינים את הפקודה הבאה ב-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-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

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

  • סוג המשאב: מכונת VM

  • Instance ID: מזהה המופע של מכונת ה-VM

  • שם היומן: 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

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

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

הגברת האבטחה של הסביבה לצורך ייצור

אחרי שבודקים את workload ומוודאים שהוא פועל כמו שצריך, הגיע הזמן להקשיח את סביבת Confidential Space לקראת פריסה בסביבת הייצור. אלכס ובולה צריכים להוסיף הצהרת 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"
    

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

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

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

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

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

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"

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

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

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

הסרת המשאבים

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

פינוי משאבי הבדיקה של אלכס

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

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

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

לפנות את המשאבים של Bola

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

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

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

    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. מחיקת הקטגוריות של Bola ב-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. אופציונלי: משביתים או משמידים את המפתח של Bola ב-Cloud Key Management Service.

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