เอกสารรับรองระยะไกลของ vTPM ในเครื่องเสมือนที่เป็นความลับ

เกี่ยวกับ Codelab นี้
schedule28 นาที
subjectอัปเดตล่าสุดเมื่อ 11 พฤศจิกายน 2567
account_circleเขียนโดย Ruide Zhang

เครื่องเสมือนที่เก็บข้อมูลลับ (CVM) คือเครื่องเสมือน Compute Engine ประเภทหนึ่งที่เข้ารหัสหน่วยความจำโดยฮาร์ดแวร์ ซึ่งช่วยให้มั่นใจได้ว่าข้อมูลและแอปพลิเคชันจะไม่สามารถอ่านหรือแก้ไขได้ขณะใช้งาน ในโค้ดแล็บนี้ คุณจะเห็นการรับรองจากระยะไกลที่อนุญาตให้บุคคลระยะไกลยืนยันโหนด CVM นอกจากนี้ คุณยังสำรวจการบันทึกในระบบคลาวด์เพื่อการตรวจสอบเพิ่มเติมได้ด้วย

fcc043c716119bd6.png

ดังที่แสดงในรูปภาพด้านบน โค้ดแล็บนี้ประกอบด้วยขั้นตอนต่อไปนี้

  • การตั้งค่า CVM และการรับรองจากระยะไกล
  • การสำรวจ Cloud Logging เกี่ยวกับการรับรองระยะไกลของ CVM
  • การตั้งค่าเว็บเซิร์ฟเวอร์สำหรับรุ่นที่เป็นความลับ

คอมโพเนนต์ในรูปภาพด้านบน ได้แก่ เครื่องมือ go-tpm และการรับรองของ Google Cloud

  • เครื่องมือ go-tpm: เครื่องมือโอเพนซอร์สสำหรับดึงข้อมูลหลักฐานการรับรองจาก vTPM (Virtual Trusted Platform Module) ใน CVM และส่งไปยัง Google Cloud Attestation เพื่อรับโทเค็นการรับรอง
  • การรับรองของ Google Cloud: ยืนยันและตรวจสอบหลักฐานการรับรองที่ได้รับ และแสดงโทเค็นการรับรองที่แสดงถึงข้อเท็จจริงเกี่ยวกับ CVM ของผู้ขอ

สิ่งที่คุณจะได้เรียนรู้

  • วิธีดำเนินการรับรองระยะไกลผ่าน Confidential Computing API ใน CVM
  • วิธีใช้ Cloud Logging เพื่อตรวจสอบการตรวจสอบจากระยะไกลของ CVM

สิ่งที่คุณต้องมี

  • โปรเจ็กต์ Google Cloud Platform
  • เบราว์เซอร์ เช่น Chrome หรือ Firefox
  • ความรู้พื้นฐานเกี่ยวกับ Google Compute Engine และ Confidential VM

2. การตั้งค่าและข้อกําหนด

หากต้องการเปิดใช้ API ที่จําเป็น ให้เรียกใช้คําสั่งต่อไปนี้ในคอนโซลระบบคลาวด์หรือสภาพแวดล้อมการพัฒนาในเครื่อง

gcloud auth login

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

3. การตั้งค่าการรับรอง CVM และ vTPM จากระยะไกล

ในขั้นตอนนี้ คุณจะต้องสร้าง CVM และทำการรับรอง vTPM จากระยะไกลเพื่อเรียกข้อมูลโทเค็นการรับรอง (โทเค็น OIDC) ใน CVM

ไปที่คอนโซลระบบคลาวด์หรือสภาพแวดล้อมการพัฒนาซอฟต์แวร์ภายใน สร้าง CVM ดังนี้ (ดูสร้างอินสแตนซ์ VM ที่มีความละเอียดอ่อน | Google Cloud) คุณต้องใช้ขอบเขตเพื่อเข้าถึง Confidential Computing API

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

ให้สิทธิ์บัญชีบริการเริ่มต้นของ CVM เพื่อเข้าถึง Confidential Computing API (CVM ต้องมีสิทธิ์ต่อไปนี้เพื่อดึงข้อมูลโทเค็นการรับรองจาก Confidential Computing API)

1). สร้างบทบาทเพื่ออนุญาตให้เข้าถึง Confidential Computing API

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 คือตัวระบุที่ไม่ซ้ำกันของโปรเจ็กต์

2). เพิ่มบัญชีบริการเริ่มต้นของ VM ลงในบทบาท

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 คือตัวระบุที่ไม่ซ้ำกันของโปรเจ็กต์

3). เชื่อมต่อกับ CVM และตั้งค่าไบนารีเครื่องมือ go-tpm สำหรับการดึงข้อมูลโทเค็นการรับรองจาก Confidential Computing API ที่ Google Cloud Attestation ให้บริการ

  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 แบบไบนารีจะดึงข้อมูลหลักฐานการรับรองจาก vTPM ใน CVM และส่งไปยัง Google Cloud Attestation เพื่อรับโทเค็นการรับรอง
git clone https://github.com/google/go-tpm-tools.git --depth 1
cd go-tpm-tools/cmd/gotpm/
go build
  1. คำสั่งเครื่องมือ go-tpm จะดึงข้อมูลหลักฐานการรับรองของ CVM จาก vTPM และส่งไปยังการรับรองของ Google Cloud Google Cloud Attestation จะยืนยันและตรวจสอบหลักฐานการรับรอง แล้วแสดงผลโทเค็นการรับรอง คำสั่งนี้จะสร้างไฟล์ attestation_token ซึ่งมี attestation-token คุณจะใช้ attestation-token เพื่อดึงข้อมูลลับในภายหลัง คุณสามารถถอดรหัสโทเค็นการรับรองใน jwt.io เพื่อดูการอ้างสิทธิ์
sudo ./gotpm token > attestation_token
  1. (ไม่บังคับ) นอกเหนือจากการดำเนินการตรวจสอบสิทธิ์จากระยะไกลด้วยเครื่องมือ go-tpm และการตรวจสอบสิทธิ์ของ Google Cloud แล้ว เรายังแสดงคำสั่งในการดึงข้อมูลหลักฐานการตรวจสอบสิทธิ์ vTPM ด้วย วิธีนี้จะช่วยให้คุณสร้างบริการอย่างการรับรองของ Google Cloud เพื่อยืนยันและตรวจสอบหลักฐานการรับรองได้
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 จะไม่ตรวจสอบใบรับรองคีย์การรับรองของใบเสนอราคา

4. เปิดใช้ Cloud Logging และสำรวจบันทึกการรับรองระยะไกล

คุณเรียกใช้คำสั่งต่อไปนี้ใน 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 คือตัวระบุที่ไม่ซ้ำกันของอินสแตนซ์

คุณควรเห็นโทเค็นการรับรอง การอ้างสิทธิ์ หลักฐานดิบ และ Nonce ที่ส่งไปยังการรับรองของ Google Cloud

5. ตั้งค่าเว็บเซิร์ฟเวอร์สำหรับรุ่นที่เป็นความลับ

ในขั้นตอนนี้ คุณจะออกจากเซสชัน SSH ก่อนหน้าและตั้งค่า VM อื่น ใน VM นี้ คุณตั้งค่าเว็บเซิร์ฟเวอร์สำหรับรุ่นที่เป็นความลับ เว็บเซิร์ฟเวอร์จะตรวจสอบโทเค็นการรับรองที่ได้รับและการอ้างสิทธิ์ของโทเค็น หากการตรวจสอบสำเร็จ ระบบจะปล่อยความลับแก่ผู้ขอ

1). ไปที่คอนโซลระบบคลาวด์หรือสภาพแวดล้อมการพัฒนาซอฟต์แวร์ภายใน สร้างเครื่องเสมือน

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 คือตัวระบุที่ไม่ซ้ำกันของโปรเจ็กต์

2). SSH ไปยัง VM ใหม่

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

3). ตั้งค่าสภาพแวดล้อม 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

4). สร้างไฟล์ 2 ไฟล์ต่อไปนี้เพื่อจัดเก็บซอร์สโค้ดของเว็บเซิร์ฟเวอร์รุ่นลับ (คัดลอก/วางด้วย nano)

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)
}

5). เรียกใช้คำสั่งต่อไปนี้เพื่อสร้างและเรียกใช้เว็บเซิร์ฟเวอร์ ซึ่งจะเริ่มต้นเว็บเซิร์ฟเวอร์รุ่นลับที่พอร์ต :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

6). ตอนนี้ให้เปิดแท็บคอนโซลระบบคลาวด์หรือเซสชันสภาพแวดล้อมการพัฒนาในเครื่องอีกแท็บหนึ่ง แล้วเรียกใช้คําสั่งต่อไปนี้ ซึ่งจะทำให้คุณได้รับ <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

7) SSH ไปยังอินสแตนซ์ VM cvm-attestation-codelab

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

8). คำสั่งต่อไปนี้จะแทนที่ 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 คือ IP ภายในของอินสแตนซ์ VM cvm-attestation-codelab-web-server

คุณจะเห็นข้อความ "นี่คือข้อมูลลับสุดยอด" บนหน้าจอ

หากป้อน attestation-token ที่ไม่ถูกต้องหรือหมดอายุ คุณจะเห็นข้อความ "curl: (52) Empty reply from server" นอกจากนี้ คุณจะเห็น "โทเค็นถูกต้อง: เท็จ" ในบันทึกของเว็บเซิร์ฟเวอร์รุ่นลับในอินสแตนซ์ VM cvm-attestation-codelab-web-server ด้วย

6. ล้างข้อมูล

เรียกใช้คําสั่งต่อไปนี้ในคอนโซลระบบคลาวด์หรือสภาพแวดล้อมการพัฒนาในเครื่อง

# 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 คือตัวระบุที่ไม่ซ้ำกันของโปรเจ็กต์

7. ขั้นตอนถัดไป

ดูข้อมูลเพิ่มเติมเกี่ยวกับ VM แบบมีข้อมูลลับและ Compute Engine