ใช้ Confidential Space กับทรัพยากรที่มีการป้องกันซึ่งไม่ได้จัดเก็บไว้กับผู้ให้บริการคลาวด์

1. ภาพรวม

พื้นที่ทำงานลับช่วยให้การแชร์ข้อมูลและการทำงานร่วมกันแบบหลายฝ่ายเป็นไปอย่างปลอดภัย ในขณะเดียวกันก็ช่วยให้องค์กรสามารถรักษาความลับของข้อมูลได้ ซึ่งหมายความว่าองค์กรต่างๆ สามารถทำงานร่วมกันได้ในขณะที่ยังคงควบคุมข้อมูลของตนเองและปกป้องข้อมูลจากการเข้าถึงที่ไม่ได้รับอนุญาต

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

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

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

  • วิธีกำหนดค่าทรัพยากรระบบคลาวด์ที่จำเป็นสำหรับการเรียกใช้ Confidential Space
  • วิธีเรียกใช้เวิร์กโหลดใน Confidential VM ที่เรียกใช้อิมเมจ Confidential Space
  • วิธีให้สิทธิ์เข้าถึงทรัพยากรที่ได้รับการปกป้องตามแอตทริบิวต์ของโค้ดภาระงาน (อะไร) สภาพแวดล้อม Confidential Space (ที่ไหน) และบัญชีที่เรียกใช้ภาระงาน (ใคร)

Codelab นี้มุ่งเน้นที่วิธีใช้ Confidential Space กับทรัพยากรที่ได้รับการปกป้องซึ่งโฮสต์ในที่อื่นที่ไม่ใช่ Google Cloud คุณจะได้เรียนรู้วิธีขอโทเค็นที่กำหนดเองและมีข้อมูลครบถ้วนจากบริการการรับรองของ Google โดยการระบุ Nonce, กลุ่มเป้าหมาย และประเภทโทเค็น PKI

ในโค้ดแล็บนี้ คุณจะได้ตั้งค่า Confidential Space ระหว่างผลิตภัณฑ์สมมติอย่าง USleep ซึ่งเป็นแอปพลิเคชันที่ใช้คอนเทนเนอร์ และผลิตภัณฑ์สมมติอย่าง UWear ซึ่งเป็นอุปกรณ์สวมใส่ที่เชื่อมต่อ เพื่อคำนวณคุณภาพการนอนหลับ UWear จะแชร์ข้อมูลสุขภาพที่ได้รับการคุ้มครอง (PHI) กับ USleep ในสภาพแวดล้อมที่ปลอดภัยและแยกจากกัน (หรือที่เรียกว่าสภาพแวดล้อมการดำเนินการที่เชื่อถือได้หรือ TEE) เพื่อให้เจ้าของข้อมูลยังคงรักษาความลับได้อย่างสมบูรณ์

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

ใน Codelab นี้ USleep จะทำให้แอปพลิเคชันที่มีคอนเทนเนอร์ใช้งานได้ USleep ไม่มีสิทธิ์เข้าถึงข้อมูลที่มีความละเอียดอ่อน แต่จะเรียกใช้ภาระงานที่ได้รับอนุมัติซึ่งได้รับอนุญาตให้เข้าถึงข้อมูลที่มีความละเอียดอ่อน

Codelab นี้มีขั้นตอนต่อไปนี้

  • ขั้นตอนที่ 1: ตั้งค่าทรัพยากรระบบคลาวด์ที่จำเป็นสำหรับ Codelab ตั้งค่าโปรเจ็กต์ การเรียกเก็บเงิน และสิทธิ์ ดาวน์โหลดซอร์สโค้ดของ Codelab และตั้งค่าตัวแปรสภาพแวดล้อม
  • ขั้นตอนที่ 2: ดาวน์โหลดใบรับรองรูทและจัดเก็บไว้กับซอร์สโค้ด UWear
  • ขั้นตอนที่ 3: สร้างบัญชีบริการของเวิร์กโหลดแยกต่างหากซึ่ง VM ของเวิร์กโหลดจะใช้สำหรับ USleep และ UWear
  • ขั้นตอนที่ 4: สร้างภาระงาน USleep ซึ่งให้โทเค็นการรับรอง
  • ขั้นตอนที่ 5: สร้างภาระงาน UWear ซึ่งจะตรวจสอบโทเค็นการรับรองและส่งข้อมูลที่ละเอียดอ่อนหากโทเค็นได้รับการอนุมัติ
  • ขั้นตอนที่ 6: เรียกใช้ภาระงาน USleep และ UWear UWear จะให้ข้อมูลที่ละเอียดอ่อน และ USleep จะเรียกใช้อัลกอริทึมการนอนหลับกับข้อมูลและแสดงผลลัพธ์
  • ขั้นตอนที่ 7: (ไม่บังคับ) เรียกใช้ภาระงาน USleep ที่ไม่ได้รับอนุญาตและยืนยันว่าไม่ได้รับข้อมูลที่ละเอียดอ่อนจาก UWear
  • ขั้นตอนที่ 8: ล้างข้อมูลทรัพยากรทั้งหมด

ทำความเข้าใจเวิร์กโฟลว์

USleep จะเรียกใช้ภาระงานในพื้นที่ข้อมูลลับ หากต้องการเรียกใช้เวิร์กโหลด จะต้องมีสิทธิ์เข้าถึง PHI ของ UWear หากต้องการเข้าถึง เวิร์กโหลด USleep จะสร้างเซสชัน TLS ที่ปลอดภัยก่อน จากนั้น USleep จะขอโทเค็นการรับรองจากบริการการรับรองของ Google พร้อมเพย์โหลดด้วย

USleep จะขอโทเค็นการรับรองที่มีเพย์โหลด JSON ซึ่งจะมี 3 สิ่งต่อไปนี้

  1. โทเค็นการรับรองที่เชื่อมโยงกับเซสชัน TLS หากต้องการเชื่อมโยงโทเค็นการรับรองกับเซสชัน TLS ค่า Nonce จะเป็นแฮชของวัสดุคีย์ที่ส่งออกของ TLS การเชื่อมโทเค็นกับเซสชัน TLS ช่วยให้มั่นใจได้ว่าจะไม่มีการโจมตีแบบ MITM เนื่องจากมีเพียง 2 ฝ่ายที่เกี่ยวข้องในเซสชัน TLS เท่านั้นที่จะสร้างค่า Nonce ได้
  2. ระบบจะระบุกลุ่มเป้าหมายของ "uwear" UWear จะยืนยันว่าตนเป็นกลุ่มเป้าหมายที่ต้องการสำหรับโทเค็นการรับรอง
  3. ประเภทโทเค็นคือ "PKI" ประเภทโทเค็น "PKI" หมายความว่า USleep ต้องการขอโทเค็นแบบสแตนด์อโลน คุณสามารถยืนยันโทเค็นแบบสแตนด์อโลนได้ว่า Google เป็นผู้ลงนามโดยใช้รูทที่ดาวน์โหลดจากปลายทาง PKI ที่รู้จักกันดีของ Confidential Space ซึ่งแตกต่างจากประเภทโทเค็น OIDC เริ่มต้นซึ่งมีการยืนยันลายเซ็นโดยใช้คีย์สาธารณะที่หมุนเวียนเป็นประจำ

bb013916a3222ce7.png

เวิร์กโหลด USleep จะได้รับโทเค็นการรับรอง จากนั้น UWear จะเข้าร่วมการเชื่อมต่อ TLS กับ USleep และดึงโทเค็นการรับรองของ USleep UWear จะตรวจสอบโทเค็นโดยการตรวจสอบการอ้างสิทธิ์ x5c กับใบรับรองรูท

UWear จะอนุมัติภาระงาน USleep ในกรณีต่อไปนี้

  1. โทเค็นผ่านตรรกะการตรวจสอบ PKI
  2. UWear จะตรวจสอบโทเค็นโดยการตรวจสอบการอ้างสิทธิ์ x5c กับใบรับรองรูท ตรวจสอบว่าโทเค็นลงนามโดยใบรับรองลีฟ และสุดท้ายคือใบรับรองรูทที่ดาวน์โหลดมาเป็นรูทเดียวกันกับการอ้างสิทธิ์ x5c
  3. การอ้างสิทธิ์การวัดภาระงานในโทเค็นตรงกับเงื่อนไขแอตทริบิวต์ที่ระบุในนโยบาย OPA OPA เป็นเครื่องมือด้านนโยบายแบบโอเพนซอร์สอเนกประสงค์ที่รวมการบังคับใช้นโยบายในทุกระดับ OPA ใช้เอกสารที่มีไวยากรณ์คล้ายกับ JSON เพื่อตั้งค่าพื้นฐานที่นโยบายจะได้รับการตรวจสอบ ดูตัวอย่างค่าที่นโยบายตรวจสอบได้ที่ค่าเกณฑ์พื้นฐานของ OPA
  4. ค่า Nonce ตรงกับ Nonce ที่คาดไว้ (Exported Keying Material ของ TLS) ซึ่งได้รับการยืนยันในนโยบาย OPA ด้านบน

เมื่อการตรวจสอบทั้งหมดเสร็จสมบูรณ์และผ่านแล้ว UWear จะยืนยันได้ว่าระบบจะส่งและประมวลผลข้อมูลอย่างปลอดภัย จากนั้น UWear จะตอบกลับด้วย PHI ที่มีความละเอียดอ่อนผ่านเซสชัน TLS เดียวกัน และ USleep จะใช้ข้อมูลดังกล่าวเพื่อคำนวณคุณภาพการนอนหลับของลูกค้าได้

2. ตั้งค่าทรัพยากรระบบคลาวด์

ก่อนเริ่มต้น

  1. ตั้งค่าโปรเจ็กต์ Google Cloud 2 โปรเจ็กต์ โปรเจ็กต์หนึ่งสำหรับ USleep และอีกโปรเจ็กต์หนึ่งสำหรับ UWear ดูข้อมูลเพิ่มเติมเกี่ยวกับการสร้างโปรเจ็กต์ Google Cloud ได้ที่ Codelab"ตั้งค่าและไปยังส่วนต่างๆ ของโปรเจ็กต์ Google แรก" คุณดูรายละเอียดเกี่ยวกับวิธีดึงข้อมูลรหัสโปรเจ็กต์และวิธีที่รหัสโปรเจ็กต์แตกต่างจากชื่อโปรเจ็กต์และหมายเลขโปรเจ็กต์ได้ที่หัวข้อการสร้างและการจัดการโปรเจ็กต์
  2. เปิดใช้การเรียกเก็บเงินสำหรับโปรเจ็กต์
  3. ใน Cloud Shell ของโปรเจ็กต์ Google รายการใดรายการหนึ่ง ให้ตั้งค่าตัวแปรสภาพแวดล้อมของโปรเจ็กต์ที่จำเป็นตามที่แสดงด้านล่าง
export UWEAR_PROJECT_ID=<Google Cloud project id of UWear>
export USLEEP_PROJECT_ID=<Google Cloud project id of USleep>
  1. เปิดใช้ Confidential Computing API และ API ต่อไปนี้สำหรับทั้ง 2 โปรเจ็กต์
gcloud config set project $UWEAR_PROJECT_ID
gcloud services enable \
    cloudapis.googleapis.com \
    cloudshell.googleapis.com \
    container.googleapis.com \
    containerregistry.googleapis.com \
    confidentialcomputing.googleapis.com

gcloud config set project $USLEEP_PROJECT_ID
gcloud services enable \
    cloudapis.googleapis.com \
    cloudshell.googleapis.com \
    container.googleapis.com \
    containerregistry.googleapis.com \
    confidentialcomputing.googleapis.com
  1. ดึงตัวระบุหลักโดยใช้
gcloud auth list

# Output should contain
# ACCOUNT: <Principal Identifier>

# Set your member variable
export MEMBER='user:<Principal Identifier>'
  1. เพิ่มสิทธิ์สำหรับโปรเจ็กต์ทั้ง 2 รายการนี้ คุณเพิ่มสิทธิ์ได้โดยทำตามรายละเอียดในหน้าเว็บให้บทบาท IAM
gcloud config set project $UWEAR_PROJECT_ID

# Add Artifact Registry Administrator role
gcloud projects add-iam-policy-binding $UWEAR_PROJECT_ID --member=$MEMBER --role='roles/iam.serviceAccountAdmin'

# Add Service Account Administrator role
gcloud projects add-iam-policy-binding $UWEAR_PROJECT_ID --member=$MEMBER --role='roles/artifactregistry.admin'
gcloud config set project $USLEEP_PROJECT_ID

# Add Service Account Administrator role
gcloud projects add-iam-policy-binding $USLEEP_PROJECT_ID --member=$MEMBER --role='roles/iam.serviceAccountAdmin'

# Add Artifact Registry Administrator role
gcloud projects add-iam-policy-binding $USLEEP_PROJECT_ID --member=$MEMBER --role='roles/artifactregistry.admin'

# Add Compute Administrator role
gcloud projects add-iam-policy-binding $USLEEP_PROJECT_ID --member=$MEMBER --role='roles/compute.admin'

# Add Storage Administrator role
gcloud projects add-iam-policy-binding $USLEEP_PROJECT_ID --member=$MEMBER --role='roles/compute.storageAdmin'
  1. ใน Cloud Shell ของโปรเจ็กต์ Google Cloud โปรเจ็กต์ใดโปรเจ็กต์หนึ่ง ให้โคลนที่เก็บ Confidential Space Codelab Github โดยใช้คำสั่งด้านล่างเพื่อรับสคริปต์ที่จำเป็นซึ่งใช้เป็นส่วนหนึ่งของ Codelab นี้
git clone https://github.com/GoogleCloudPlatform/confidential-space.git
  1. เปลี่ยนไดเรกทอรีเป็นไดเรกทอรีสคริปต์สำหรับ Codelab ข้อมูลสุขภาพ
cd confidential-space/codelabs/health_data_analysis_codelab/scripts
  1. อัปเดต2 บรรทัดนี้ในสคริปต์ config_env.sh ซึ่งอยู่ในไดเรกทอรี codelabs/health_data_analysis_codelab/scripts อัปเดตรหัสโปรเจ็กต์ด้วยรหัสโปรเจ็กต์สำหรับ USleep และ UWear อย่าลืมนำสัญลักษณ์ความคิดเห็น "#" ที่จุดเริ่มต้นของบรรทัดออก
# TODO: Populate UWear and USleep Project IDs
export UWEAR_PROJECT_ID=your-uwear-project-id
export USLEEP_PROJECT_ID=your-usleep-project-id
  1. ไม่บังคับ: ตั้งค่าตัวแปรที่มีอยู่ก่อนแล้ว คุณลบล้างชื่อทรัพยากรได้โดยใช้ตัวแปรเหล่านี้ (เช่น export UWEAR_ARTIFACT_REPOSITORY='my-artifact-repository')
  • คุณตั้งค่าตัวแปรต่อไปนี้ได้โดยใช้ชื่อทรัพยากรระบบคลาวด์ที่มีอยู่ หากตั้งค่าตัวแปรไว้ ระบบจะใช้ทรัพยากรระบบคลาวด์ที่มีอยู่ซึ่งสอดคล้องกันจากโปรเจ็กต์ หากไม่ได้ตั้งค่าตัวแปร ระบบจะสร้างชื่อทรัพยากรระบบคลาวด์จากค่าในสคริปต์ config_env.sh
  1. เรียกใช้สคริปต์ config_env.sh เพื่อตั้งชื่อตัวแปรที่เหลือเป็นค่าตามรหัสโปรเจ็กต์สำหรับชื่อทรัพยากร
# Navigate to the scripts folder
cd ~/confidential-space/codelabs/health_data_analysis_codelab/scripts

# Run the config_env script
source config_env.sh

# Verify the variables were set
# Expected output for default variable should be `workload-sa`
echo $USLEEP_WORKLOAD_SERVICE_ACCOUNT

3. ดาวน์โหลดใบรับรองรูท

  1. หากต้องการตรวจสอบโทเค็นแบบสแตนด์อโลนที่ส่งคืนจากบริการการรับรอง UWear จะต้องตรวจสอบลายเซ็นกับใบรับรองรูทของ Confidential Space UWear จะต้องดาวน์โหลดใบรับรองรูท และจัดเก็บไว้ในเครื่อง เรียกใช้คำสั่งต่อไปนี้ในคอนโซลของโปรเจ็กต์ Google Cloud รายการใดรายการหนึ่ง
cd ~/confidential-space/codelabs/health_data_analysis_codelab/src/uwear

wget https://confidentialcomputing.googleapis.com/.well-known/confidential_space_root.crt -O confidential_space_root.pem
  1. สร้างลายนิ้วมือของใบรับรองรูทที่ดาวน์โหลด
openssl x509 -fingerprint -in confidential_space_root.pem -noout
  1. ตรวจสอบว่าลายนิ้วมือตรงกับข้อมูลสรุป SHA-1 ต่อไปนี้
B9:51:20:74:2C:24:E3:AA:34:04:2E:1C:3B:A3:AA:D2:8B:21:23:21

4. สร้างบัญชีบริการของเวิร์กโหลด

ตอนนี้คุณจะสร้างบัญชีบริการ 2 บัญชี บัญชีหนึ่งสำหรับภาระงานของ USleep และอีกบัญชีสำหรับภาระงานของ UWear เรียกใช้สคริปต์ create_service_accounts.sh เพื่อสร้างบัญชีบริการของเวิร์กโหลดในโปรเจ็กต์ USleep และ UWear VM ที่เรียกใช้เวิร์กโหลดจะใช้บัญชีบริการเหล่านี้

# Navigate to the scripts folder
cd ~/confidential-space/codelabs/health_data_analysis_codelab/scripts

# Run the create_service_accounts script
./create_service_accounts.sh

สคริปต์

  • มอบบทบาท iam.serviceAccountUser ซึ่งแนบบัญชีบริการกับภาระงาน
  • มอบบทบาท confidentialcomputing.workloadUser ให้กับบัญชีบริการของภาระงาน ซึ่งจะช่วยให้บัญชีผู้ใช้สร้างโทเค็นการรับรองได้
  • มอบสิทธิ์logging.logWriterให้กับสิทธิ์ของบัญชีบริการของภาระงาน ซึ่งจะช่วยให้สภาพแวดล้อม Confidential Space เขียนบันทึกลงใน Cloud Logging นอกเหนือจากคอนโซลอนุกรมได้ เพื่อให้บันทึกพร้อมใช้งานหลังจากที่ VM สิ้นสุดลงแล้ว สร้างเวิร์กโหลด

5. สร้างภาระงาน USleep

ในขั้นตอนนี้ คุณจะได้สร้างอิมเมจ Docker สำหรับเวิร์กโหลดที่ใช้ใน Codelab นี้ ภาระงาน USleep เป็นแอปพลิเคชัน Golang แบบง่ายที่กำหนดคุณภาพการนอนหลับของลูกค้าโดยใช้ข้อมูลสุขภาพส่วนบุคคลในอุปกรณ์ที่สวมใส่ได้

เกี่ยวกับภาระงาน USleep

ภาระงาน USleep เป็นแอปพลิเคชัน Golang อย่างง่ายที่กำหนดคุณภาพการนอนหลับของลูกค้าโดยใช้ข้อมูลสุขภาพส่วนบุคคลในอุปกรณ์ที่สวมใส่ได้ ภาระงานของ USleep มีส่วนประกอบหลัก 3 ส่วน ได้แก่

  1. การตั้งค่าเซสชัน TLS และการแยกวัสดุคีย์ที่ส่งออก
func handleConnectionRequest(w http.ResponseWriter, r *http.Request) {
  // Upgrade HTTP Connection to a websocket.
  conn, err := upgrader.Upgrade(w, r, nil)
  if err != nil {
    fmt.Printf("failed to upgrade connection to a websocket with err: %v\n", err)
    return
  }
  defer conn.Close()

  // Get EKM
  hash, err := getEKMHashFromRequest(r)
  if err != nil {
    fmt.Printf("Failed to get EKM: %v", err)
  }
  ...
}

func getEKMHashFromRequest(r *http.Request) (string, error) {
  ekm, err := r.TLS.ExportKeyingMaterial("testing_nonce", nil, 32)
  if err != nil {
    err := fmt.Errorf("failed to get EKM from inbound http request: %w", err)
    return "", err
  }

  sha := sha256.New()
  sha.Write(ekm)
  hash := base64.StdEncoding.EncodeToString(sha.Sum(nil))

  fmt.Printf("EKM: %v\nSHA hash: %v", ekm, hash)
  return hash, nil
}
  1. ขอโทเค็นจากบริการรับรองโดยมีประเภทกลุ่มเป้าหมาย Nonce และโทเค็น PKI
func handleConnectionRequest(w http.ResponseWriter, r *http.Request) {
  ...

  // Request token with TLS Exported Keying Material (EKM) hashed.
  token, err := getCustomToken(hash)
  if err != nil {
    fmt.Printf("failed to get custom token from token endpoint: %v", err)
    return
  }

  // Respond to the client with the token.
  conn.WriteMessage(websocket.TextMessage, token)

  ...
}

var (
        socketPath    = "/run/container_launcher/teeserver.sock"
        tokenEndpoint = "http://localhost/v1/token"
        contentType   = "application/json"
)


func getCustomToken(nonce string) ([]byte, error) {
  httpClient := http.Client{
    Transport: &http.Transport{
      // Set the DialContext field to a function that creates
      // a new network connection to a Unix domain socket
      DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
        return net.Dial("unix", socketPath)
      },
    },
  }

  body := fmt.Sprintf(`{
    "audience": "uwear",
    "nonces": ["%s"],
    "token_type": "PKI"
  }`, nonce)

  resp, err := httpClient.Post(tokenEndpoint, contentType, strings.NewReader(body))
  if err != nil {
    return nil, err
  }

  fmt.Printf("Response from launcher: %v\n", resp)
  text, err := io.ReadAll(resp.Body)
  if err != nil {
    return nil, fmt.Errorf("Failed to read resp.Body: %w", err)
  }
  fmt.Printf("Token from the attestation service: %s\n", text)

  return text, nil
}
  1. รับข้อมูลที่ละเอียดอ่อนและคำนวณคุณภาพการนอนหลับของผู้ใช้
func handleConnectionRequest(w http.ResponseWriter, r *http.Request) {
  ...

  // Read the sensitive data
  _, content, err := conn.ReadMessage()
  if err != nil {
    fmt.Printf("failed to read message from the connection: %v\n", err)
  }
  fmt.Printf("Received content from other side, %v\n", string(content))

 // TODO: Handle sensitive data
  ...
}

ขั้นตอนในการสร้างภาระงาน USleep

  1. เรียกใช้สคริปต์ create_usleep_workload.sh เพื่อสร้างภาระงาน USleep สคริปต์นี้จะทำสิ่งต่อไปนี้
  • สร้าง Artifact Registry ($USLEEP_ARTIFACT_REPOSITORY) ที่ UWear เป็นเจ้าของซึ่งจะเผยแพร่เวิร์กโหลด
  • สร้างโค้ด usleep/workload.go และแพ็กเกจในอิมเมจ Docker ดูการกำหนดค่า Dockerfile สำหรับ USleep
  • เผยแพร่อิมเมจ Docker ไปยัง Artifact Registry ($USLEEP_ARTIFACT_REPOSITORY) ที่ UWear เป็นเจ้าของ
  • มอบ$USLEEP_WORKLOAD_SERVICE_ACCOUNTสิทธิ์อ่านสำหรับ Artifact Registry ($USLEEP_ARTIFACT_REPOSITORY) ให้กับบัญชีบริการ
./create_usleep_workload.sh
  1. สำคัญ: ในบันทึกเอาต์พุต ให้ดึงข้อมูลสรุปของรูปภาพสำหรับ USleep
latest: digest: sha256:<USLEEP_IMAGE_DIGEST> size: 945
  1. ไปยังไดเรกทอรี UWear
cd ~/confidential-space/codelabs/health_data_analysis_codelab/src/uwear
  1. แทนที่ค่าในส่วน "allowed_submods_container_image_digest" ใน opa_validation_values.json ด้วย USLEEP_IMAGE_DIGEST
# Replace the image digest
sed -i 's/sha256:bc4c32cb2ca046ba07dcd964b07a320b7d0ca88a5cf8e979da15cae68a2103ee/sha256:<USLEEP_IMAGE_DIGEST>/' ~/confidential-space/codelabs/health_data_analysis_codelab/src/uwear/opa_validation_values.json

6. สร้างภาระงาน UWear

เกี่ยวกับภาระงาน UWear

ภาระงาน UWear มี 4 ส่วนหลักๆ ดังนี้

  1. เข้าร่วมเซสชัน TLS เดียวกันที่สร้างขึ้นในเวิร์กโหลดของ USleep และดึงโทเค็นการรับรองจาก USleep ผ่านเซสชัน TLS ที่ปลอดภัย
func main() {
  fmt.Println("Initializing client...")

  tlsconfig := &tls.Config{
    // Skipping client verification of the server's certificate chain and host name since we are
    // doing custom verification using the attestation token.
    InsecureSkipVerify: true,
  }

  dialer := websocket.Dialer{
    TLSClientConfig:  tlsconfig,
    HandshakeTimeout: 5 * time.Second,
  }

  ipAddress := os.Getenv(ipAddrEnvVar)
  url := fmt.Sprintf("wss://%s:8081/connection", ipAddress)

  fmt.Printf("Attempting to dial to url %v...\n", url)
  conn, _, err := dialer.Dial(url, nil)
  if err != nil {
    fmt.Printf("Failed to dial to url %s, err %v\n", url, err)
    return
  }

  defer conn.Close()

  tokenString, ekm, err := retrieveTokenAndEKMFromConn(conn)
  if err != nil {
    fmt.Printf("Failed to retrieve token and EKM from connection: %v\n", err)
    return
  }

  fmt.Printf("token: %v\n", tokenString)

  ...
}
  1. การตรวจสอบโทเค็นแบบสแตนด์อโลนโดยทำดังนี้
  • การตรวจสอบการอ้างสิทธิ์ x5c มีชุดใบรับรองที่เชื่อมโยงอย่างถูกต้องจากใบรับรอง Leaf ไปยังใบรับรองระดับกลาง และสุดท้ายไปยังใบรับรองรูท
  • ตรวจสอบว่าโทเค็นได้รับการลงนามโดยใบรับรอง Leaf ที่อยู่ในอ้างสิทธิ์ x5c
  • การตรวจสอบใบรับรองรูทที่ดาวน์โหลด / จัดเก็บไว้เป็นรูทเดียวกันกับในคำกล่าวอ้าง x5c
func main() {
  ...

  token, err := validatePKIToken(tokenString)
  if err != nil {
    fmt.Printf("Failed to validate PKI token, err: %v\n.", err)
    return
  }
  fmt.Println("PKI token validated successfully")
 
  ...
}

// validatePKIToken validates the PKI token returned from the attestation service.
// It verifies the token the certificate chain and that the token is signed by Google
// Returns a jwt.Token or returns an error if invalid.
func validatePKIToken(attestationToken string) (jwt.Token, error) {
  // IMPORTANT: The attestation token should be considered untrusted until the certificate chain and
  // the signature is verified.
  rawRootCertificate, err := readFile(rootCertificateFile)
  if err != nil {
    return jwt.Token{}, fmt.Errorf("readFile(%v) - failed to read root certificate: %w", rootCertificateFile, err)
  }

  storedRootCert, err := decodeAndParsePEMCertificate(string(rawRootCertificate))
  if err != nil {
    return jwt.Token{}, fmt.Errorf("DecodeAndParsePEMCertificate(string) - failed to decode and parse root certificate: %w", err)
  }

  jwtHeaders, err := extractJWTHeaders(attestationToken)
  if err != nil {
    return jwt.Token{}, fmt.Errorf("ExtractJWTHeaders(token) - failed to extract JWT headers: %w", err)
  }

  if jwtHeaders["alg"] != "RS256" {
    return jwt.Token{}, fmt.Errorf("ValidatePKIToken(attestationToken, ekm) - got Alg: %v, want: %v", jwtHeaders["alg"], "RS256")
  }

  // Additional Check: Validate the ALG in the header matches the certificate SPKI.
  // https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.7
  // This is included in Golang's jwt.Parse function

  x5cHeaders := jwtHeaders["x5c"].([]any)
  certificates, err := extractCertificatesFromX5CHeader(x5cHeaders)
  if err != nil {
    return jwt.Token{}, fmt.Errorf("ExtractCertificatesFromX5CHeader(x5cHeaders) returned error: %w", err)
  }

  // Verify the leaf certificate signature algorithm is an RSA key
  if certificates.LeafCert.SignatureAlgorithm != x509.SHA256WithRSA {
    return jwt.Token{}, fmt.Errorf("leaf certificate signature algorithm is not SHA256WithRSA")
  }

  // Verify the leaf certificate public key algorithm is RSA
  if certificates.LeafCert.PublicKeyAlgorithm != x509.RSA {
    return jwt.Token{}, fmt.Errorf("leaf certificate public key algorithm is not RSA")
  }

  // Verify the storedRootCertificate is the same as the root certificate returned in the token
  // storedRootCertificate is downloaded from the confidential computing well known endpoint
  // https://confidentialcomputing.googleapis.com/.well-known/attestation-pki-root
  err = compareCertificates(*storedRootCert, *certificates.RootCert)
  if err != nil {
    return jwt.Token{}, fmt.Errorf("failed to verify certificate chain: %w", err)
  }

  err = verifyCertificateChain(certificates)
  if err != nil {
    return jwt.Token{}, fmt.Errorf("VerifyCertificateChain(CertificateChain) - error verifying x5c chain: %v", err)
  }

  keyFunc := func(token *jwt.Token) (any, error) {
    return certificates.LeafCert.PublicKey, nil
  }

  verifiedJWT, err := jwt.Parse(attestationToken, keyFunc)
  return *verifiedJWT, err
}



// verifyCertificateChain verifies the certificate chain from leaf to root.
// It also checks that all certificate lifetimes are valid.
func verifyCertificateChain(certificates CertificateChain) error {
    // Additional check: Verify that all certificates in the cert chain are valid.
    // Note: The *x509.Certificate Verify method in Golang already validates this but for other coding
    // languages it is important to make sure the certificate lifetimes are checked.
    if isCertificateLifetimeValid(certificates.LeafCert) {
        return fmt.Errorf("leaf certificate is not valid")
    }

    if isCertificateLifetimeValid(certificates.IntermediateCert) {
        return fmt.Errorf("intermediate certificate is not valid")
    }
    interPool := x509.NewCertPool()
    interPool.AddCert(certificates.IntermediateCert)

    if isCertificateLifetimeValid(certificates.RootCert) {
        return fmt.Errorf("root certificate is not valid")
    }
    rootPool := x509.NewCertPool()
    rootPool.AddCert(certificates.RootCert)

    _, err := certificates.LeafCert.Verify(x509.VerifyOptions{
        Intermediates: interPool,
        Roots:         rootPool,
        KeyUsages:     []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
    })

    if err != nil {
        return fmt.Errorf("failed to verify certificate chain: %v", err)
    }

    return nil
}
  1. จากนั้นภาระงาน UWear จะตรวจสอบว่าการอ้างสิทธิ์การวัดภาระงานในโทเค็นตรงกับเงื่อนไขแอตทริบิวต์ที่ระบุไว้ในนโยบาย OPA หรือไม่ OPA เป็นเครื่องมือด้านนโยบายแบบโอเพนซอร์สอเนกประสงค์ที่รวมการบังคับใช้นโยบายในทุกระดับ OPA ใช้เอกสารที่มีรูปแบบคล้ายกับ JSON เพื่อตั้งค่าพื้นฐานที่นโยบายจะได้รับการตรวจสอบ
func main() {
  ...

  err = validateClaimsAgainstOPAPolicy(token, ekm)
  if err != nil {
    fmt.Printf("Failed to validate claims against OPA policy: %v\n", err)
  return
  }

  fmt.Println("Validated token and claims. Sending sensitive data")

  ...
}

// validateClaimsAgainstOPAPolicy validates the claims in the JWT token against the OPA policy.
func validateClaimsAgainstOPAPolicy(token jwt.Token, ekm string) error {
        data, err := os.ReadFile("opa_validation_values.json")
        authorized, err := evaluateOPAPolicy(context.Background(), token, ekm, string(data))
        if err != nil {
                fmt.Println("Error evaluating OPA policy:", err)
                return fmt.Errorf("failed to evaluate OPA policy: %w", err)
        }
        if !authorized {
                fmt.Println("Remote TEE's JWT failed policy check.")
                return fmt.Errorf("remote TEE's JWT failed policy check")
        }
        fmt.Println("JWT is authorized.")
        return nil
}


// evaluateOPAPolicy returns boolean indicating if OPA policy is satisfied or not, or error if occurred
func evaluateOPAPolicy(ctx context.Context, token jwt.Token, ekm string, policyData string) (bool, error) {
        var claims jwt.MapClaims
        var ok bool
        if claims, ok = token.Claims.(jwt.MapClaims); !ok {
                return false, fmt.Errorf("failed to get the claims from the JWT")
        }

        module := fmt.Sprintf(opaPolicy, ekm)

        var json map[string]any
        err := util.UnmarshalJSON([]byte(policyData), &json)
        store := inmem.NewFromObject(json)

        // Bind 'allow' to the value of the policy decision
        // Bind 'hw_verified', 'image_verified', 'audience_verified, 'nonce_verified' to their respective policy evaluations
        query, err := rego.New(
                rego.Query(regoQuery),                          // Argument 1 (Query string)
                rego.Store(store),                              // Argument 2 (Data store)
                rego.Module("confidential_space.rego", module), // Argument 3 (Policy module)
        ).PrepareForEval(ctx)

        if err != nil {
                fmt.Printf("Error creating query: %v\n", err)
                return false, err
        }

        fmt.Println("Performing OPA query evaluation...")
        results, err := query.Eval(ctx, rego.EvalInput(claims))

        if err != nil {
                fmt.Printf("Error evaluating OPA policy: %v\n", err)
                return false, err
        } else if len(results) == 0 {
                fmt.Println("Undefined result from evaluating OPA policy")
                return false, err
        } else if result, ok := results[0].Bindings["allow"].(bool); !ok {
                fmt.Printf("Unexpected result type: %v\n", ok)
                fmt.Printf("Result: %+v\n", result)
                return false, err
        }

        fmt.Println("OPA policy evaluation completed.")

        fmt.Println("OPA policy result values:")
        for key, value := range results[0].Bindings {
                fmt.Printf("[ %s ]: %v\n", key, value)
        }
        result := results[0].Bindings["allow"]
        if result == true {
                fmt.Println("Policy check PASSED")
                return true, nil
        }
        fmt.Println("Policy check FAILED")
        return false, nil
}
{
  "allowed_submods_container_image_digest": [
    "sha256:<USLEEP_IMAGE_DIGEST>"
  ],
  "allowed_hwmodel": [
    "GCP_INTEL_TDX",
    "GCP_SHIELDED_VM",
    "GCP_AMD_SEV_ES",
    "GCP_AMD_SEV"
  ],
  "allowed_aud": [
    "uwear"
  ],
  "allowed_issuer": [
    "https://confidentialcomputing.googleapis.com"
  ],
  "allowed_secboot": [
    true
  ],
  "allowed_sw_name": [
    "CONFIDENTIAL_SPACE"
  ]
}
package confidential_space

import rego.v1

default allow := false
default hw_verified := false
default image_digest_verified := false
default audience_verified := false
default nonce_verified := false
default issuer_verified := false
default secboot_verified := false
default sw_name_verified := false

allow if {
  hw_verified
  image_digest_verified
  audience_verified
  nonce_verified
  issuer_verified
  secboot_verified
  sw_name_verified
}

hw_verified if input.hwmodel in data.allowed_hwmodel
image_digest_verified if input.submods.container.image_digest in data.allowed_submods_container_image_digest
audience_verified if input.aud in data.allowed_aud
issuer_verified if input.iss in data.allowed_issuer
secboot_verified if input.secboot in data.allowed_secboot
sw_name_verified if input.swname in data.allowed_sw_name
nonce_verified if {
  input.eat_nonce == "%s"
}
  • ตัวอย่างการค้นหา Rego
regoQuery = "
    allow = data.confidential_space.allow;
    hw_verified = data.confidential_space.hw_verified;
    image__digest_verified = data.confidential_space.image_digest_verified;
    audience_verified = data.confidential_space.audience_verified;
    nonce_verified = data.confidential_space.nonce_verified;
    issuer_verified = data.confidential_space.issuer_verified;
    secboot_verified = data.confidential_space.secboot_verified;
    sw_name_verified = data.confidential_space.sw_name_verified
"

ตัวอย่างโค้ดสำหรับการรับแฮช EKM

func getEKMHashFromConn(c *websocket.Conn) (string, error) {
  conn, ok := c.NetConn().(*tls.Conn)
  if !ok {
    return "", fmt.Errorf("failed to cast NetConn to *tls.Conn")
  }

  state := conn.ConnectionState()
  ekm, err := state.ExportKeyingMaterial("testing_nonce", nil, 32)
  if err != nil {
    return "", fmt.Errorf("failed to get EKM from TLS connection: %w", err)
  }

  sha := sha256.New()
  sha.Write(ekm)
  hash := base64.StdEncoding.EncodeToString(sha.Sum(nil))

  return hash, nil
}
  1. เมื่อการตรวจสอบทั้งหมดเสร็จสมบูรณ์และผ่านแล้ว UWear จะยืนยันได้ว่าระบบจะส่งและประมวลผลข้อมูลอย่างปลอดภัย จากนั้น UWear จะตอบกลับด้วย PHI ที่มีความละเอียดอ่อนผ่านเซสชัน TLS เดียวกัน และ USleep จะใช้ข้อมูลดังกล่าวเพื่อคำนวณคุณภาพการนอนหลับของลูกค้าได้
func main() {
  ...

  fmt.Println("Validated token and claims. Sending sensitive data")

  data, err := readFile(mySensitiveDataFile)
  if err != nil {
    fmt.Printf("Failed to read data from the file: %v\n", err)
  }

  conn.WriteMessage(websocket.BinaryMessage, data)
  fmt.Println("Sent payload. Closing the connection")
  conn.Close()
  
  ...
}

ขั้นตอนในการสร้างภาระงาน USleep

  1. ไปยังไดเรกทอรีสคริปต์
cd ~/confidential-space/codelabs/health_data_analysis_codelab/scripts
  1. เรียกใช้สคริปต์ create_uwear_workload.sh เพื่อสร้างภาระงาน UWear ดังนี้
  • สร้าง Artifact Registry ($UWEAR_ARTIFACT_REPOSITORY) ที่ UWear เป็นเจ้าของซึ่งจะมีการเผยแพร่เวิร์กโหลด
  • สร้างโค้ด uwear/workload.go และแพ็กเกจในอิมเมจ Docker ดูการกำหนดค่า Dockerfile สำหรับ USleep
  • เผยแพร่อิมเมจ Docker ไปยัง Artifact Registry ($UWEAR_ARTIFACT_REPOSITORY) ที่ UWear เป็นเจ้าของ
  • มอบ$UWEAR_WORKLOAD_SERVICE_ACCOUNTสิทธิ์อ่านสำหรับ Artifact Registry ($UWEAR_ARTIFACT_REPOSITORY) ให้กับบัญชีบริการ
./create_uwear_workload.sh

7. เรียกใช้ภาระงาน USleep และ UWear

เรียกใช้ภาระงาน USleep

gcloud config set project $USLEEP_PROJECT_ID


gcloud compute instances create \
 --confidential-compute-type=SEV \
 --shielded-secure-boot \
 --maintenance-policy=MIGRATE \
 --scopes=cloud-platform --zone=${USLEEP_PROJECT_ZONE} \
 --image-project=confidential-space-images \
 --image-family=confidential-space \
--service-account=${USLEEP_WORKLOAD_SERVICE_ACCOUNT}@${USLEEP_PROJECT_ID}.iam.gserviceaccount.com \
 --metadata ^~^tee-image-reference=${USLEEP_PROJECT_REPOSITORY_REGION}-docker.pkg.dev/${USLEEP_PROJECT_ID}/${USLEEP_ARTIFACT_REPOSITORY}/${USLEEP_WORKLOAD_IMAGE_NAME}:${USLEEP_WORKLOAD_IMAGE_TAG}~tee-restart-policy=Never~tee-container-log-redirect=true usleep

การตอบกลับควรแสดง STATUS: RUNNING และควรแสดง EXTERNAL_IP ในลักษณะเดียวกับตัวอย่างนี้

NAME: usleep
ZONE: us-west1-b
MACHINE_TYPE: n2d-standard-2
PREEMPTIBLE:
INTERNAL_IP: 10.138.0.6
EXTERNAL_IP: 34.168.56.10
STATUS: RUNNING

จัดเก็บ IP ภายนอกในตัวแปร

export USLEEP_EXTERNAL_IP=<add your external IP> 

ยืนยันว่าเวิร์กโหลด USleep ทำงานอย่างถูกต้อง

หากต้องการยืนยันว่าเวิร์กโหลด USleep ทำงานอย่างถูกต้อง ให้ไปที่หน้าอินสแตนซ์ VM ในโปรเจ็กต์ USleep คลิกอินสแตนซ์ "usleep" แล้วกด "Serial port 1(console)" ในส่วนบันทึก เมื่อเซิร์ฟเวอร์พร้อมใช้งานแล้ว บันทึกควรแสดงข้อความที่คล้ายกับข้อความต่อไปนี้ที่ด้านล่างของบันทึก

2024/09/13 17:00:00 workload task started
#####----- Local IP Address is <YOUR-LOCAL-IP> -----#####
Starting Server..

เรียกใช้ภาระงาน UWear

gcloud config set project $UWEAR_PROJECT_ID

gcloud compute instances create \
 --confidential-compute-type=SEV \
 --shielded-secure-boot \
 --maintenance-policy=MIGRATE \
 --scopes=cloud-platform --zone=${UWEAR_PROJECT_ZONE} \
 --image-project=confidential-space-images \
 --image-family=confidential-space \
--service-account=${UWEAR_WORKLOAD_SERVICE_ACCOUNT}@${UWEAR_PROJECT_ID}.iam.gserviceaccount.com \
 --metadata ^~^tee-image-reference=${UWEAR_PROJECT_REPOSITORY_REGION}-docker.pkg.dev/${UWEAR_PROJECT_ID}/${UWEAR_ARTIFACT_REPOSITORY}/${UWEAR_WORKLOAD_IMAGE_NAME}:${UWEAR_WORKLOAD_IMAGE_TAG}~tee-restart-policy=Never~tee-container-log-redirect=true~tee-env-remote_ip_addr=$USLEEP_EXTERNAL_IP uwear

ยืนยันว่าเวิร์กโหลด UWear ทำงานอย่างถูกต้อง

หากต้องการดูบันทึกของภาระงาน UWear ให้ไปที่หน้าอินสแตนซ์ VM ในโปรเจ็กต์ UWear คลิกอินสแตนซ์ "uwear" แล้วกด "Serial port 1(console)" ในส่วน Logs

เอาต์พุตบันทึกเมื่ออินสแตนซ์เริ่มต้นอย่างสมบูรณ์แล้วควรมีลักษณะดังนี้

ในโปรเจ็กต์ UWear บันทึกแบบอนุกรมควรแสดงข้อความที่คล้ายกับ

token: eyJ[...]MrXUg
PKI token validated successfully
Performing OPA query evaluation...
OPA policy evaluation completed.
OPA policy result values:
[ hw_verified ]: true
[ image__digest_verified ]: true
[ audience_verified ]: true
[ nonce_verified ]: true
[ issuer_verified ]: true
[ secboot_verified ]: true
[ sw_name_verified ]: true
[ allow ]: true
Policy check PASSED
JWT is authorized.
Validated token and claims. Sending sensitive data
Sent payload. Closing the connection

หากภาระงาน UWear ของคุณไม่เป็นเช่นนี้ โปรดดูหมายเหตุด้านล่างเพื่อดูวิธีการ

ดูผลลัพธ์ของ USleep

หากต้องการดูผลลัพธ์ ให้กลับไปที่หน้าอินสแตนซ์ VM ในโปรเจ็กต์ USleep คลิกอินสแตนซ์ "usleep" แล้วกด "Serial port 1(console)" ในส่วนบันทึก ดูผลลัพธ์ของเวิร์กโหลดที่ด้านล่างของบันทึก โดยควรมีลักษณะคล้ายกับตัวอย่างด้านล่าง

Token from the attestation service: eyJhbGci...Ii5A3CJBuDM2o5Q
Received content from other side, {
  "name": "Amy",
  "age": 29,
  "sleep": {
      "light": {
          "minutes": 270
      },
      "deep": {
          "minutes": 135
      },
      "rem": {
          "minutes": 105
      }
  }
}
Sleep quality result: total sleep time is less than 8 hours

ผลลัพธ์ควรเป็น "total sleep time is less than 8 hours".

ขอแสดงความยินดี คุณสร้างพื้นที่ลับระหว่าง UWear กับ USleep เพื่อแชร์ข้อมูลที่ละเอียดอ่อนได้สำเร็จแล้ว

8. (ไม่บังคับ) เรียกใช้เวิร์กโหลดที่ไม่ได้รับอนุญาต

ในสถานการณ์ถัดไป USleep จะอัปเดตโค้ดและเรียกใช้ภาระงานอื่นในข้อมูลการนอนหลับที่ UWear ให้ไว้ UWear ยังไม่ตกลงที่จะรับภาระงานใหม่นี้และยังไม่อัปเดตนโยบาย OPA เพื่ออนุญาตให้ใช้ข้อมูลสรุปรูปภาพใหม่ เราจะยืนยันว่า UWear จะไม่ส่งข้อมูลที่ละเอียดอ่อนไปยังเวิร์กโหลดที่ไม่ได้รับอนุญาต

USleep แก้ไขภาระงาน

  1. ตั้งค่าโปรเจ็กต์เป็น $USLEEP_PROJECT_ID
gcloud config set project $USLEEP_PROJECT_ID
  1. ลบอินสแตนซ์ VM ของ USleep
gcloud compute instances delete usleep --zone $USLEEP_PROJECT_ZONE
  1. ไปที่ไดเรกทอรี usleep/workload.go
cd ~/confidential-space/codelabs/health_data_analysis_codelab/src/usleep
  1. ในไฟล์ usleep/workload.go อัปเดตบรรทัด "audience": "uwear". ในตัวอย่างนี้ เราจะอัปเดตกลุ่มเป้าหมายเป็นค่าอื่นที่ UWear ไม่อนุมัติเพื่อเปลี่ยนค่าแฮชของรูปภาพ ดังนั้น UWear จึงควรปฏิเสธโฆษณานี้ด้วย 2 เหตุผล ได้แก่ ข้อมูลสรุปรูปภาพที่ไม่ได้รับอนุมัติและกลุ่มเป้าหมายที่ไม่ถูกต้อง
"audience": "anotherCompany.com",
  1. สร้างเวิร์กโหลด USleep ใหม่
cd ~/confidential-space/codelabs/health_data_analysis_codelab/scripts

./create_usleep_workload.sh
  1. สร้างอินสแตนซ์ VM ของ USleep ใหม่และเรียกใช้เวิร์กโหลด
gcloud compute instances create \
 --confidential-compute-type=SEV \
 --shielded-secure-boot \
 --maintenance-policy=MIGRATE \
 --scopes=cloud-platform --zone=${USLEEP_PROJECT_ZONE} \
 --image-project=confidential-space-images \
 --image-family=confidential-space \
--service-account=${USLEEP_WORKLOAD_SERVICE_ACCOUNT}@${USLEEP_PROJECT_ID}.iam.gserviceaccount.com \
 --metadata ^~^tee-image-reference=${USLEEP_PROJECT_REPOSITORY_REGION}-docker.pkg.dev/${USLEEP_PROJECT_ID}/${USLEEP_ARTIFACT_REPOSITORY}/${USLEEP_WORKLOAD_IMAGE_NAME}:${USLEEP_WORKLOAD_IMAGE_TAG}~tee-restart-policy=Never~tee-container-log-redirect=true usleep
  1. ดึง IP ภายนอกใหม่ของ USleep เพื่อใช้ในภายหลัง
export USLEEP_EXTERNAL_IP=<add your external IP>

เรียกใช้ภาระงานอีกครั้ง

  1. ลบอินสแตนซ์ VM ของ UWear
gcloud config set project $UWEAR_PROJECT_ID

gcloud compute instances delete uwear --zone $UWEAR_PROJECT_ZONE
  1. สร้างอินสแตนซ์ VM ของ UWear อีกครั้งโดยใช้ IP ภายนอกใหม่
gcloud compute instances create \
 --confidential-compute-type=SEV \
 --shielded-secure-boot \
 --maintenance-policy=MIGRATE \
 --scopes=cloud-platform --zone=${UWEAR_PROJECT_ZONE} \
 --image-project=confidential-space-images \
 --image-family=confidential-space \
--service-account=${UWEAR_WORKLOAD_SERVICE_ACCOUNT}@${UWEAR_PROJECT_ID}.iam.gserviceaccount.com \
 --metadata ^~^tee-image-reference=${UWEAR_PROJECT_REPOSITORY_REGION}-docker.pkg.dev/${UWEAR_PROJECT_ID}/${UWEAR_ARTIFACT_REPOSITORY}/${UWEAR_WORKLOAD_IMAGE_NAME}:${UWEAR_WORKLOAD_IMAGE_TAG}~tee-restart-policy=Never~tee-container-log-redirect=true~tee-env-remote_ip_addr=$USLEEP_EXTERNAL_IP uwear
  1. ในบันทึกแบบอนุกรมของ UWear ข้อความต่อไปนี้ควรปรากฏขึ้น และ VM ของ USleep ไม่ควรได้รับข้อมูลที่มีความละเอียดอ่อน
OPA policy result values:
[ nonce_verified ]: true
[ issuer_verified ]: true
[ secboot_verified ]: true
[ sw_name_verified ]: true
[ allow ]: false
[ hw_verified ]: true
[ image__digest_verified ]: false
[ audience_verified ]: false
Policy check FAILED
Remote TEE's JWT failed policy check.
Failed to validate claims against OPA policy: remote TEE's JWT failed policy check

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

คุณใช้สคริปต์ล้างข้อมูลเพื่อล้างข้อมูลทรัพยากรที่เราสร้างขึ้นเป็นส่วนหนึ่งของ Codelab นี้ได้ การล้างข้อมูลนี้จะลบทรัพยากรต่อไปนี้

  • บัญชีบริการ UWear ($UWEAR_SERVICE_ACCOUNT)
  • รีจิสทรีของอาร์ติแฟกต์ UWear ($UWEAR_ARTIFACT_REPOSITORY)
  • อินสแตนซ์การประมวลผล UWear
  • บัญชีบริการ USleep ($USLEEP_SERVICE_ACCOUNT)
  • รีจิสทรีของอาร์ติแฟกต์ USleep ($USLEEP_ARTIFACT_REPOSITORY)
  • อินสแตนซ์ Compute ของ USleep
./cleanup.sh

หากสำรวจเสร็จแล้ว โปรดพิจารณาลบโปรเจ็กต์โดยทำตามวิธีการเหล่านี้

ขอแสดงความยินดี

ยินดีด้วย คุณทำ Codelab เสร็จเรียบร้อยแล้ว

คุณได้เรียนรู้วิธีแชร์ข้อมูลอย่างปลอดภัยพร้อมทั้งรักษาความลับของข้อมูลไว้โดยใช้พื้นที่ทำงานลับ

สิ่งต่อไปที่ควรทำ

ลองดู Codelab ที่คล้ายกันเหล่านี้

อ่านเพิ่มเติม