গোপনীয় ভার্চুয়াল মেশিনে vTPM রিমোট প্রত্যয়ন

১. সংক্ষিপ্ত বিবরণ

কনফিডেনশিয়াল ভার্চুয়াল মেশিন (CVM) হলো এক ধরনের কম্পিউট ইঞ্জিন ভার্চুয়াল মেশিন যা হার্ডওয়্যার-ভিত্তিক মেমরি এনক্রিপশন ব্যবহার করে। এটি নিশ্চিত করতে সাহায্য করে যে, ব্যবহারের সময় আপনার ডেটা এবং অ্যাপ্লিকেশনগুলো পড়া বা পরিবর্তন করা যাবে না। এই কোডল্যাবে, আপনাকে রিমোট অ্যাটেস্টেশন দেখানো হয়েছে, যা কোনো দূরবর্তী পক্ষকে CVM নোডগুলো যাচাই করার সুযোগ দেয়। এছাড়াও, আরও নিরীক্ষণের জন্য আপনি ক্লাউড লগিং সম্পর্কে জানবেন।

fcc043c716119bd6.png

উপরের চিত্রে যেমন দেখানো হয়েছে, এই কোডল্যাবটিতে নিম্নলিখিত ধাপগুলো অন্তর্ভুক্ত রয়েছে:

  • CVM সেটআপ এবং রিমোট অ্যাটেস্টেশন
  • CVM রিমোট অ্যাটেস্টেশনে ক্লাউড লগিং অনুসন্ধান
  • সিক্রেট রিলিজ ওয়েব সার্ভার সেটআপ

উপরের চিত্রে প্রদর্শিত উপাদানগুলোর মধ্যে একটি go-tpm টুল এবং গুগল ক্লাউড অ্যাটেস্টেশন অন্তর্ভুক্ত রয়েছে:

  • go-tpm টুল : একটি ওপেন সোর্স টুল যা CVM-এ থাকা vTPM (ভার্চুয়াল ট্রাস্টেড প্ল্যাটফর্ম মডিউল) থেকে অ্যাটেস্টেশন এভিডেন্স সংগ্রহ করে এবং অ্যাটেস্টেশন টোকেনের জন্য তা গুগল ক্লাউড অ্যাটেস্টেশন-এ পাঠায়।
  • গুগল ক্লাউড অ্যাটেস্টেশন: প্রাপ্ত অ্যাটেস্টেশন প্রমাণ যাচাই ও বৈধতা প্রদান করে এবং অনুরোধকারীর CVM সম্পর্কিত তথ্য সম্বলিত একটি অ্যাটেস্টেশন টোকেন ফেরত দেয়।

আপনি যা শিখবেন

  • CVM-এ কনফিডেনশিয়াল কম্পিউটিং এপিআই ব্যবহার করে কীভাবে রিমোট অ্যাটেস্টেশন সম্পাদন করবেন
  • CVM রিমোট অ্যাটেস্টেশন নিরীক্ষণ করতে ক্লাউড লগিং কীভাবে ব্যবহার করবেন

আপনার যা যা লাগবে

২. সেটআপ এবং প্রয়োজনীয়তা

প্রয়োজনীয় API-গুলো সক্রিয় করতে, ক্লাউড কনসোল অথবা আপনার স্থানীয় ডেভেলপমেন্ট এনভায়রনমেন্টে নিম্নলিখিত কমান্ডটি চালান:

gcloud auth login

gcloud services enable \
    cloudapis.googleapis.com \
    cloudshell.googleapis.com \
    confidentialcomputing.googleapis.com \
    compute.googleapis.com

৩. CVM এবং vTPM রিমোট অ্যাটেস্টেশন স্থাপন করা

এই ধাপে, আপনি একটি CVM তৈরি করবেন এবং CVM-টিতে একটি অ্যাটেস্টেশন টোকেন (OIDC টোকেন) পুনরুদ্ধার করার জন্য একটি vTPM রিমোট অ্যাটেস্টেশন সম্পাদন করবেন।

ক্লাউড কনসোল অথবা আপনার লোকাল ডেভেলপমেন্ট এনভায়রনমেন্টে যান। নিম্নলিখিতভাবে একটি CVM তৈরি করুন (দেখুন: Create a Confidential VM instance | Google Cloud )। কনফিডেনশিয়াল কম্পিউটিং এপিআই (Confidential Computing APIs) অ্যাক্সেস করার জন্য স্কোপ (Scopes) প্রয়োজন।

gcloud config set project <project-id>

gcloud compute instances create cvm-attestation-codelab \
    --machine-type=n2d-standard-2 \
    --min-cpu-platform="AMD Milan" \
    --zone=us-central1-c \
    --confidential-compute \
    --image=ubuntu-2204-jammy-v20240228 \
    --image-project=ubuntu-os-cloud \
    --scopes https://www.googleapis.com/auth/cloud-platform

কনফিডেনশিয়াল কম্পিউটিং এপিআই অ্যাক্সেস করার জন্য সিভিএম ডিফল্ট সার্ভিস অ্যাকাউন্টকে অনুমোদন দিন (কনফিডেনশিয়াল কম্পিউটিং এপিআই থেকে অ্যাটেস্টেশন টোকেন আনার জন্য সিভিএম-এর নিম্নলিখিত অনুমতিগুলির প্রয়োজন):

১) গোপনীয় কম্পিউটিং এপিআই-গুলিতে অ্যাক্সেস দেওয়ার জন্য একটি রোল তৈরি করুন।

gcloud iam roles create Confidential_Computing_User --project=<project-id> \
    --title="CVM User" --description="Grants the ability to generate an attestation token in a CVM." \
 --permissions="confidentialcomputing.challenges.create,confidentialcomputing.challenges.verify,confidentialcomputing.locations.get,confidentialcomputing.locations.list" --stage=GA

নিম্নলিখিতগুলি প্রতিস্থাপন করুন:

  • project-id হলো প্রজেক্টের অনন্য শনাক্তকারী।

২) রোলটিতে ভিএম ডিফল্ট সার্ভিস অ্যাকাউন্টটি যুক্ত করুন।

gcloud projects add-iam-policy-binding <project-id> \
    --member serviceAccount:$(gcloud iam service-accounts list --filter="email ~ compute@developer.gserviceaccount.com$" --format='value(email)'
) \
    --role "projects/<project-id>/roles/Confidential_Computing_User"

নিম্নলিখিতগুলি প্রতিস্থাপন করুন:

  • project-id হলো প্রজেক্টের অনন্য শনাক্তকারী।

৩) CVM-এর সাথে সংযোগ করুন এবং Google Cloud Attestation কর্তৃক প্রদত্ত Confidential Computing API থেকে Attestation Token সংগ্রহের জন্য go-tpm টুল বাইনারিটি সেট আপ করুন।

  1. CVM-এর সাথে সংযোগ করুন।
gcloud compute ssh --zone us-central1-c cvm-attestation-codelab
  1. একটি Go পরিবেশ সেট আপ করুন:
wget https://go.dev/dl/go1.22.0.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.22.0.linux-amd64.tar.gz
export PATH=$PATH:/usr/local/go/bin
  1. go-tpm টুল বাইনারিটি বিল্ড করুন। go-tpm টুল বাইনারিটি CVM-এ থাকা vTPM থেকে অ্যাটেস্টেশন এভিডেন্স সংগ্রহ করে এবং একটি অ্যাটেস্টেশন টোকেনের জন্য তা গুগল ক্লাউড অ্যাটেস্টেশন-এ পাঠায়।
git clone https://github.com/google/go-tpm-tools.git --depth 1
cd go-tpm-tools/cmd/gotpm/
go build
  1. go-tpm টুল কমান্ডটি vTPM থেকে CVM-এর অ্যাটেস্টেশন এভিডেন্স সংগ্রহ করে এবং তা Google Cloud Attestation-এ পাঠায়। Google Cloud Attestation সেই অ্যাটেস্টেশন এভিডেন্স যাচাই ও বৈধতা প্রদান করে এবং একটি অ্যাটেস্টেশন টোকেন ফেরত দেয়। এই কমান্ডটি একটি attestation_token ফাইল তৈরি করে, যেটিতে আপনার attestation-token থাকে। পরবর্তীতে একটি সিক্রেট আনার জন্য আপনি আপনার attestation-token ব্যবহার করবেন। ক্লেইমগুলো দেখার জন্য আপনি jwt.io- তে অ্যাটেস্টেশন টোকেনটি ডিকোড করতে পারেন।
sudo ./gotpm token > attestation_token
  1. (ঐচ্ছিক) বিকল্পভাবে go-tpm টুল এবং গুগল ক্লাউড অ্যাটেস্টেশন ব্যবহার করে রিমোট অ্যাটেস্টেশন সম্পাদন করার জন্য, আমরা vTPM অ্যাটেস্টেশন এভিডেন্স আনার কমান্ডগুলো প্রদর্শন করছি। এইভাবে, আপনি অ্যাটেস্টেশন এভিডেন্স যাচাই ও বৈধতা প্রদানের জন্য গুগল ক্লাউড অ্যাটেস্টেশনের মতো একটি পরিষেবা তৈরি করতে পারেন:
nonce=$(head -c 16 /dev/urandom | xxd -p)
sudo ./gotpm attest --nonce $nonce --format textproto --output quote.dat
sudo ./gotpm verify debug --nonce $nonce --format textproto --input quote.dat --output vtpm_report

vtpm_report যাচাইকৃত ইভেন্টলগটি থাকে। আপনি আপনার পছন্দের এডিটর ব্যবহার করে এটি দেখতে পারেন। মনে রাখবেন যে, verify কমান্ডটি কোটটির অ্যাটেস্টেশন কী সার্টিফিকেট যাচাই করে না।

৪. ক্লাউড লগিং সক্ষম করুন এবং রিমোট অ্যাটেস্টেশন লগটি পর্যালোচনা করুন।

আপনি আপনার cvm-attestation-codelab CVM-এ নিম্নলিখিত কমান্ডটি চালাতে পারেন। এবার, এটি ক্লাউড লগিং-এ কার্যকলাপটি লগ করে।

sudo ./gotpm token --cloud-log --audience "https://api.cvm-attestation-codelab.com"

আপনার ক্লাউড কনসোল অথবা স্থানীয় ডেভেলপমেন্ট এনভায়রনমেন্টে cvm-attestation-codelab <instance-id> ডাউনলোড করুন।

gcloud compute instances describe cvm-attestation-codelab --zone us-central1-c --format='value(id)'

ক্লাউড লগিং সম্পর্কে জানতে, নিম্নলিখিত URL-টি খুলুন: https://console.cloud.google.com/logs । ক্যোয়ারি ফিল্ডে নিম্নলিখিতটি লিখুন:

resource.type="gce_instance" resource.labels.zone="us-central1-c" resource.labels.instance_id=<instance-id> log_name="projects/<project-id>/logs/gotpm" severity>=DEFAULT

নিম্নলিখিতগুলি প্রতিস্থাপন করুন:

  • project-id হলো প্রজেক্টের অনন্য শনাক্তকারী।
  • instance-id হলো ইনস্ট্যান্সটির অনন্য শনাক্তকারী।

গুগল ক্লাউড অ্যাটেস্টেশনে পাঠানো অ্যাটেস্টেশন টোকেন, এর ক্লেইমস, মূল প্রমাণ এবং ননস আপনার খুঁজে পাওয়া উচিত।

৫. একটি সিক্রেট রিলিজ ওয়েব সার্ভার সেটআপ করুন

এই ধাপে, আপনি আপনার পূর্ববর্তী SSH সেশন থেকে বেরিয়ে এসে আরেকটি VM সেট আপ করবেন। এই VM-এ, আপনি একটি সিক্রেট রিলিজ ওয়েব সার্ভার সেট আপ করবেন। ওয়েব সার্ভারটি প্রাপ্ত অ্যাটেস্টেশন টোকেন এবং এর ক্লেইমগুলো যাচাই করে। যদি যাচাইকরণ সফল হয়, তবে এটি অনুরোধকারীর কাছে সিক্রেটটি রিলিজ করে দেয়।

১) ক্লাউড কনসোল অথবা আপনার লোকাল ডেভেলপমেন্ট এনভায়রনমেন্টে যান। একটি ভার্চুয়াল মেশিন তৈরি করুন।

gcloud config set project <project-id>

gcloud compute instances create cvm-attestation-codelab-web-server \
    --machine-type=n2d-standard-2 \
    --zone=us-central1-c \
    --image=ubuntu-2204-jammy-v20240228 \
    --image-project=ubuntu-os-cloud

নিম্নলিখিতগুলি প্রতিস্থাপন করুন:

  • project-id হলো প্রজেক্টের অনন্য শনাক্তকারী।

২) আপনার নতুন ভিএম-এ SSH করুন।

gcloud compute ssh --zone us-central1-c cvm-attestation-codelab-web-server

৩) Go পরিবেশটি প্রস্তুত করুন।

wget https://go.dev/dl/go1.22.0.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.22.0.linux-amd64.tar.gz
export PATH=$PATH:/usr/local/go/bin

৪) সিক্রেট রিলিজ ওয়েব সার্ভারের সোর্স কোড সংরক্ষণের জন্য নিম্নলিখিত দুটি ফাইল তৈরি করুন (ন্যানো দিয়ে কপি/পেস্ট করুন)।

main.go

package main

import (
        "fmt"
        "net/http"
        "strings"
        "time"
        "log"

        "github.com/golang-jwt/jwt/v4"
)

const (
        theSecret = "This is the super secret information!"
)

func homePage(w http.ResponseWriter, r *http.Request) {
        tokenString := r.Header.Get("Authorization")
        if tokenString != "" {
                tokenString, err := extractToken(tokenString)
                if err != nil {
                        http.Error(w, err.Error(), http.StatusUnauthorized)
                }

                tokenBytes := []byte(tokenString)
                // A method to return a public key from the well-known endpoint
                keyFunc := getRSAPublicKeyFromJWKsFile

                token, err := decodeAndValidateToken(tokenBytes, keyFunc)
                if err != nil {
                        http.Error(w, "Invalid JWT Token", http.StatusUnauthorized)
                }

                if ok, err := isValid(token.Claims.(jwt.MapClaims)); ok {
                        fmt.Fprintln(w, theSecret)
                } else {
                        if err != nil {
                                http.Error(w, "Error validating JWT claims: "+err.Error(), http.StatusUnauthorized)
                        } else {
                                http.Error(w, "Invalid JWT token Claims", http.StatusUnauthorized)
                        }
                }
        } else {
                http.Error(w, "Authorization token required", http.StatusUnauthorized)
        }
}

func extractToken(tokenString string) (string, error) {
        if strings.HasPrefix(tokenString, "Bearer ") {
                return strings.TrimPrefix(tokenString, "Bearer "), nil
        }
        return "", fmt.Errorf("invalid token format")
}

func isValid(claims jwt.MapClaims) (bool, error) {
        // 1. Evaluating Standard Claims:
        subject, ok := claims["sub"].(string)
        if !ok {
                return false, fmt.Errorf("missing or invalid 'sub' claim")
        }
        fmt.Println("Subject:", subject)
        // e.g. "sub":"https://www.googleapis.com/compute/v1/projects/<project_id>/zones/<project_zone>/instances/<instance_name>"

        issuedAt, ok := claims["iat"].(float64)
        if !ok {
                return false, fmt.Errorf("missing or invalid 'iat' claim")
        }
        fmt.Println("Issued At:", time.Unix(int64(issuedAt), 0))

        // 2. Evaluating Remote Attestation Claims:
        hwModel, ok := claims["hwmodel"].(string)
        if !ok || hwModel != "GCP_AMD_SEV" {
                return false, fmt.Errorf("missing or invalid 'hwModel'")
        }
        fmt.Println("hwmodel:", hwModel)

        swName, ok := claims["swname"].(string)
        if !ok || swName != "GCE" {
                return false, fmt.Errorf("missing or invalid 'hwModel'")
        }
        fmt.Println("swname:", swName)

        return true, nil
}

func main() {
        http.HandleFunc("/", homePage)
        fmt.Println("Server listening on :8080")
        err := http.ListenAndServe(":8080", nil)
        if err != nil {
                log.Fatalf("Server failed to start: %v", err)
        }
}

helper.go

package main

import (
        "crypto/rsa"
        "encoding/base64"
        "encoding/json"
        "errors"
        "fmt"
        "io"
        "math/big"
        "net/http"

        "github.com/golang-jwt/jwt/v4"
)

const (
        socketPath     = "/run/container_launcher/teeserver.sock"
        expectedIssuer = "https://confidentialcomputing.googleapis.com"
        wellKnownPath  = "/.well-known/openid-configuration"
)

type jwksFile struct {
        Keys []jwk `json:"keys"`
}

type jwk struct {
        N   string `json:"n"`   // "nMMTBwJ7H6Id8zUCZd-L7uoNyz9b7lvoyse9izD9l2rtOhWLWbiG-7pKeYJyHeEpilHP4KdQMfUo8JCwhd-OMW0be_XtEu3jXEFjuq2YnPSPFk326eTfENtUc6qJohyMnfKkcOcY_kTE11jM81-fsqtBKjO_KiSkcmAO4wJJb8pHOjue3JCP09ZANL1uN4TuxbM2ibcyf25ODt3WQn54SRQTV0wn098Y5VDU-dzyeKYBNfL14iP0LiXBRfHd4YtEaGV9SBUuVhXdhx1eF0efztCNNz0GSLS2AEPLQduVuFoUImP4s51YdO9TPeeQ3hI8aGpOdC0syxmZ7LsL0rHE1Q",
        E   string `json:"e"`   // "AQAB" or 65537 as an int
        Kid string `json:"kid"` // "1f12fa916c3a0ef585894b4b420ad17dc9d6cdf5",

        // Unused fields:
        // Alg string `json:"alg"` // "RS256",
        // Kty string `json:"kty"` // "RSA",
        // Use string `json:"use"` // "sig",
}

type wellKnown struct {
        JwksURI string `json:"jwks_uri"` // "https://www.googleapis.com/service_accounts/v1/metadata/jwk/signer@confidentialspace-sign.iam.gserviceaccount.com"

        // Unused fields:
        // Iss                                   string `json:"issuer"`                                // "https://confidentialcomputing.googleapis.com"
        // Subject_types_supported               string `json:"subject_types_supported"`               // [ "public" ]
        // Response_types_supported              string `json:"response_types_supported"`              // [ "id_token" ]
        // Claims_supported                      string `json:"claims_supported"`                      // [ "sub", "aud", "exp", "iat", "iss", "jti", "nbf", "dbgstat", "eat_nonce", "google_service_accounts", "hwmodel", "oemid", "secboot", "submods", "swname", "swversion" ]
        // Id_token_signing_alg_values_supported string `json:"id_token_signing_alg_values_supported"` // [ "RS256" ]
        // Scopes_supported                      string `json:"scopes_supported"`                      // [ "openid" ]
}

func getWellKnownFile() (wellKnown, error) {
        httpClient := http.Client{}
        resp, err := httpClient.Get(expectedIssuer + wellKnownPath)
        if err != nil {
                return wellKnown{}, fmt.Errorf("failed to get raw .well-known response: %w", err)
        }

        wellKnownJSON, err := io.ReadAll(resp.Body)
        if err != nil {
                return wellKnown{}, fmt.Errorf("failed to read .well-known response: %w", err)
        }

        wk := wellKnown{}
        json.Unmarshal(wellKnownJSON, &wk)
        return wk, nil
}

func getJWKFile() (jwksFile, error) {
        wk, err := getWellKnownFile()
        if err != nil {
                return jwksFile{}, fmt.Errorf("failed to get .well-known json: %w", err)
        }

        // Get JWK URI from .wellknown
        uri := wk.JwksURI
        fmt.Printf("jwks URI: %v\n", uri)

        httpClient := http.Client{}
        resp, err := httpClient.Get(uri)
        if err != nil {
                return jwksFile{}, fmt.Errorf("failed to get raw JWK response: %w", err)
        }

        jwkbytes, err := io.ReadAll(resp.Body)
        if err != nil {
                return jwksFile{}, fmt.Errorf("failed to read JWK body: %w", err)
        }

        file := jwksFile{}
        err = json.Unmarshal(jwkbytes, &file)
        if err != nil {
                return jwksFile{}, fmt.Errorf("failed to unmarshall JWK content: %w", err)
        }

        return file, nil
}

// N and E are 'base64urlUInt' encoded: https://www.rfc-editor.org/rfc/rfc7518#section-6.3
func base64urlUIntDecode(s string) (*big.Int, error) {
        b, err := base64.RawURLEncoding.DecodeString(s)
        if err != nil {
                return nil, err
        }
        z := new(big.Int)
        z.SetBytes(b)
        return z, nil
}

func getRSAPublicKeyFromJWKsFile(t *jwt.Token) (any, error) {
        keysfile, err := getJWKFile()
        if err != nil {
                return nil, fmt.Errorf("failed to fetch the JWK file: %w", err)
        }

        // Multiple keys are present in this endpoint to allow for key rotation.
        // This method finds the key that was used for signing to pass to the validator.
        kid := t.Header["kid"]
        for _, key := range keysfile.Keys {
                if key.Kid != kid {
                        continue // Select the key used for signing
                }

                n, err := base64urlUIntDecode(key.N)
                if err != nil {
                        return nil, fmt.Errorf("failed to decode key.N %w", err)
                }
                e, err := base64urlUIntDecode(key.E)
                if err != nil {
                        return nil, fmt.Errorf("failed to decode key.E %w", err)
                }

                // The parser expects an rsa.PublicKey: https://github.com/golang-jwt/jwt/blob/main/rsa.go#L53
                // or an array of keys. We chose to show passing a single key in this example as its possible
                // not all validators accept multiple keys for validation.
                return &rsa.PublicKey{
                        N: n,
                        E: int(e.Int64()),
                }, nil
        }

        return nil, fmt.Errorf("failed to find key with kid '%v' from well-known endpoint", kid)
}

func decodeAndValidateToken(tokenBytes []byte, keyFunc func(t *jwt.Token) (any, error)) (*jwt.Token, error) {
        var err error
        fmt.Println("Unmarshalling token and checking its validity...")
        token, err := jwt.NewParser().Parse(string(tokenBytes), keyFunc)

        fmt.Printf("Token valid: %v\n", token.Valid)
        if token.Valid {
                return token, nil
        }
        if ve, ok := err.(*jwt.ValidationError); ok {
                if ve.Errors&jwt.ValidationErrorMalformed != 0 {
                        return nil, fmt.Errorf("token format invalid. Please contact the Confidential Space team for assistance")
                }
                if ve.Errors&(jwt.ValidationErrorNotValidYet) != 0 {
                        // If device time is not synchronized with the Attestation Service you may need to account for that here.
                        return nil, errors.New("token is not active yet")
                }
                if ve.Errors&(jwt.ValidationErrorExpired) != 0 {
                        return nil, fmt.Errorf("token is expired")
                }
                return nil, fmt.Errorf("unknown validation error: %v", err)
        }

        return nil, fmt.Errorf("couldn't handle this token or couldn't read a validation error: %v", err)
}

৫) ওয়েব সার্ভারটি বিল্ড ও রান করতে নিম্নলিখিত কমান্ডগুলো চালান। এটি :8080 পোর্টে সিক্রেট রিলিজ ওয়েবসার্ভারটি চালু করবে।

go mod init google.com/codelab
go mod tidy
go get github.com/golang-jwt/jwt/v4
go build
./codelab

সমস্যা সমাধান: go mod tidy:

go: finding module for package github.com/golang-jwt/jwt/v4
go: downloading github.com/golang-jwt/jwt v3.2.2+incompatible
go: downloading github.com/golang-jwt/jwt/v4 v4.5.0
go: found github.com/golang-jwt/jwt/v4 in github.com/golang-jwt/jwt/v4 v4.5.0
go: google.com/codelab/go/pkg/mod/github.com/golang-jwt/jwt@v3.2.2+incompatible: import path "google.com/codelab/go/pkg/mod/github.com/golang-jwt/jwt@v3.2.2+incompatible" should not have @version
go: google.com/codelab/go/pkg/mod/github.com/golang-jwt/jwt@v3.2.2+incompatible/cmd/jwt: import path "google.com/codelab/go/pkg/mod/github.com/golang-jwt/jwt@v3.2.2+incompatible/cmd/jwt" should not have @version
go: google.com/codelab/go/pkg/mod/github.com/golang-jwt/jwt@v3.2.2+incompatible/request: import path "google.com/codelab/go/pkg/mod/github.com/golang-jwt/jwt@v3.2.2+incompatible/request" should not have @version
go: google.com/codelab/go/pkg/mod/github.com/golang-jwt/jwt@v3.2.2+incompatible/test: import path "google.com/codelab/go/pkg/mod/github.com/golang-jwt/jwt@v3.2.2+incompatible/test" should not have @version

৬) এখন আরেকটি ক্লাউড কনসোল ট্যাব অথবা লোকাল ডেভেলপমেন্ট এনভায়রনমেন্ট সেশন চালু করুন এবং নিম্নলিখিত কমান্ডটি চালান। এর মাধ্যমে আপনি < cvm-attestation-codelab-web-server-internal-ip > পেয়ে যাবেন।

gcloud compute instances describe cvm-attestation-codelab-web-server --format='get(networkInterfaces[0].networkIP)' --zone=us-central1-c

৭) আপনার cvm-attestation-codelab ভিএম ইনস্ট্যান্সে SSH করুন।

gcloud compute ssh --zone "us-central1-c" "cvm-attestation-codelab"

৮) নিম্নলিখিত কমান্ডটি পূর্বে প্রাপ্ত attestation-token (যা ~/go-tpm-tools/cmd/gotpm/ রয়েছে) প্রতিস্থাপন করে। এটি আপনাকে সিক্রেট রিলিজ ওয়েব সার্ভারে থাকা সিক্রেটটি এনে দেবে!

cd ~/go-tpm-tools/cmd/gotpm/
curl http://<cvm-attestation-codelab-web-server-internal-ip>:8080 -H "Authorization: Bearer $(cat ./attestation_token)"

নিম্নলিখিতগুলি প্রতিস্থাপন করুন:

  • cvm-attestation-codelab-web-server-internal-ip হলো cvm-attestation-codelab-web-server VM ইনস্ট্যান্সটির অভ্যন্তরীণ আইপি।

আপনি আপনার স্ক্রিনে "এটি অতি গোপনীয় তথ্য!" দেখতে পাবেন।

আপনি যদি একটি ভুল বা মেয়াদোত্তীর্ণ attestation-token প্রবেশ করান, তাহলে আপনি "curl: (52) Empty reply from server" দেখতে পাবেন। এছাড়াও আপনি cvm-attestation-codelab-web-server VM ইনস্ট্যান্সে আপনার সিক্রেট রিলিজ ওয়েব সার্ভার লগে "Token valid: false" দেখতে পাবেন।

৬. পরিষ্কার-পরিচ্ছন্নতা

ক্লাউড কনসোল অথবা আপনার স্থানীয় ডেভেলপমেন্ট এনভায়রনমেন্টে নিম্নলিখিত কমান্ডগুলো চালান:

# Delete the role binding
gcloud projects remove-iam-policy-binding <project-id> \
    --member serviceAccount:$(gcloud iam service-accounts list --filter="email ~ compute@developer.gserviceaccount.com$" --format='value(email)'
) \
    --role "projects/<project-id>/roles/Confidential_Computing_User"

# Delete the role
gcloud iam roles delete Confidential_Computing_User --project=<project-id>

# Delete the web server VM instance
gcloud compute instances delete cvm-attestation-codelab-web-server --zone=us-central1-c

# Delete the CVM instance
gcloud compute instances delete cvm-attestation-codelab --zone=us-central1-c

নিম্নলিখিতগুলি প্রতিস্থাপন করুন:

  • project-id হলো প্রজেক্টের অনন্য শনাক্তকারী।

৭. এরপর কী?

কনফিডেনশিয়াল ভিএম এবং কম্পিউট ইঞ্জিন সম্পর্কে আরও জানুন।