1. Tổng quan
Không gian bảo mật cung cấp tính năng cộng tác và chia sẻ dữ liệu an toàn cho nhiều bên, đồng thời cho phép các tổ chức duy trì tính bảo mật cho dữ liệu của họ. Điều này có nghĩa là các tổ chức có thể cộng tác với nhau mà vẫn duy trì quyền kiểm soát đối với dữ liệu của mình và bảo vệ dữ liệu đó khỏi bị truy cập trái phép.
Confidential Space mở ra những trường hợp mà bạn muốn thu được giá trị tương hỗ từ việc tổng hợp và phân tích dữ liệu nhạy cảm (thường được kiểm soát), đồng thời vẫn giữ toàn quyền kiểm soát dữ liệu đó. Với Confidential Space, các tổ chức có thể nhận được giá trị tương hỗ từ việc tổng hợp và phân tích dữ liệu nhạy cảm như thông tin nhận dạng cá nhân (PII), thông tin sức khoẻ được bảo vệ (PHI), tài sản trí tuệ và các bí mật mật mã học – đồng thời vẫn giữ toàn quyền kiểm soát dữ liệu đó.
Bạn cần có
- Hai dự án riêng biệt trên Google Cloud Platform
- Một trình duyệt, chẳng hạn như Chrome hoặc Firefox
- Kiến thức cơ bản về Google Compute Engine, VM bảo mật, Vùng chứa và kho lưu trữ từ xa, chứng chỉ và chuỗi chứng chỉ.
- Kiến thức cơ bản về Tài khoản dịch vụ, Open Policy Agent, Rego và Cơ sở hạ tầng khoá công khai
Kiến thức bạn sẽ học được
- Cách định cấu hình các tài nguyên cần thiết trên Đám mây để chạy Confidential Space
- Cách chạy một tải trọng trong một Máy ảo bảo mật đang chạy hình ảnh Không gian bảo mật
- Cách cho phép truy cập vào các tài nguyên được bảo vệ dựa trên thuộc tính của mã đơn vị công việc (what), môi trường Confidential Space (where) và tài khoản đang chạy đơn vị công việc (who).
Lớp học lập trình này tập trung vào cách sử dụng Confidential Space với các tài nguyên được bảo vệ được lưu trữ ở một nơi khác ngoài Google Cloud. Bạn sẽ tìm hiểu cách yêu cầu một mã thông báo tuỳ chỉnh, độc lập từ Dịch vụ chứng thực của Google bằng cách cung cấp một số chỉ dùng một lần, đối tượng và loại mã thông báo PKI.
Trong lớp học lập trình này, bạn sẽ thiết lập một Không gian riêng tư giữa một sản phẩm hư cấu (USleep), một ứng dụng được chứa trong vùng chứa và một sản phẩm hư cấu (UWear), một thiết bị đeo được kết nối, để tính toán chất lượng giấc ngủ của bạn. UWear sẽ chia sẻ thông tin sức khoẻ được bảo vệ (PHI) với USleep trong một môi trường an toàn, bảo mật và biệt lập (còn gọi là Môi trường thực thi đáng tin cậy hoặc TEE) để chủ sở hữu dữ liệu vẫn giữ được tính bảo mật hoàn toàn.
UWear vừa là đơn vị kiểm toán khối lượng công việc vừa là chủ sở hữu dữ liệu. Là trình kiểm tra tải,nó sẽ xem xét mã trong tải đang chạy và ghi lại thông tin tóm tắt về hình ảnh. Là chủ sở hữu dữ liệu, UWear sẽ viết logic xác minh để kiểm tra tính hợp lệ của mã thông báo và chữ ký của mã thông báo đó. Nó ghi chính sách xác thực bằng cách sử dụng bản tóm tắt hình ảnh của khối lượng công việc được kiểm tra, chỉ cho phép bản tóm tắt hình ảnh cụ thể trong một môi trường cụ thể truy cập vào dữ liệu nhạy cảm.
USleep đang triển khai ứng dụng trong vùng chứa trong lớp học lập trình này. USleep không có quyền truy cập vào dữ liệu nhạy cảm nhưng chạy khối lượng công việc đã được phê duyệt và được phép truy cập vào dữ liệu nhạy cảm.
Lớp học lập trình này bao gồm các bước sau:
- Bước 1: Thiết lập các tài nguyên đám mây cần thiết cho lớp học lập trình. Thiết lập dự án, thông tin thanh toán và quyền. Tải mã nguồn của lớp học lập trình xuống và thiết lập các biến môi trường.
- Bước 2: Tải chứng chỉ gốc xuống và lưu trữ cùng với mã nguồn UWear.
- Bước 3: Tạo các tài khoản dịch vụ riêng biệt cho khối lượng công việc mà VM khối lượng công việc sẽ dùng cho USleep và UWear.
- Bước 4: Tạo tải USleep cung cấp mã thông báo chứng thực.
- Bước 5: Tạo tải UWear để xác thực mã chứng thực và gửi dữ liệu nhạy cảm nếu mã này được phê duyệt.
- Bước 6: Chạy các tải USleep và UWear. UWear sẽ cung cấp dữ liệu nhạy cảm, còn USleep sẽ chạy một thuật toán giấc ngủ trên dữ liệu đó và đưa ra kết quả.
- Bước 7: (Không bắt buộc) Chạy một tải không được uỷ quyền của USleep và xác nhận rằng bạn chưa nhận được dữ liệu nhạy cảm từ UWear.
- Bước 8: Dọn dẹp tất cả tài nguyên.
Tìm hiểu quy trình làm việc
USleep sẽ chạy khối lượng công việc trong Confidential Space. Để chạy khối lượng công việc, cần có quyền truy cập vào thông tin sức khoẻ được bảo vệ (PHI) của UWear. Để có quyền truy cập, khối lượng công việc USleep trước tiên sẽ tạo một phiên TLS an toàn. Sau đó, USleep cũng sẽ yêu cầu mã thông báo chứng thực từ Dịch vụ chứng thực của Google bằng một tải trọng.
USleep sẽ yêu cầu một mã thông báo chứng thực có tải trọng JSON chứa 3 nội dung:
- Một mã thông báo chứng thực được liên kết với phiên TLS. Để liên kết mã thông báo chứng thực với phiên TLS, giá trị số chỉ dùng một lần sẽ là hàm băm của TLS Exported Keying Material. Việc liên kết mã thông báo với phiên TLS đảm bảo rằng không có cuộc tấn công trung gian nào xảy ra vì chỉ có hai bên liên quan đến phiên TLS mới có thể tạo ra giá trị số chỉ dùng một lần.
- Đối tượng "uwear" sẽ được cung cấp. UWear sẽ xác minh rằng đó là đối tượng mục tiêu của mã thông báo chứng thực.
- Loại mã thông báo là "PKI". Loại mã thông báo "PKI" có nghĩa là USleep muốn yêu cầu một mã thông báo độc lập. Bạn có thể xác minh rằng mã thông báo độc lập này được Google ký bằng cách sử dụng gốc được tải xuống từ điểm cuối PKI đã biết của Confidential Space. Điều này trái ngược với loại mã thông báo OIDC mặc định, có chữ ký được xác minh bằng khoá công khai xoay vòng thường xuyên.

Tải USleep nhận được mã thông báo chứng thực. Sau đó, UWear sẽ tham gia kết nối TLS với USleep và truy xuất mã thông báo chứng thực của USleep. UWear sẽ xác thực mã thông báo bằng cách kiểm tra x5c claim dựa trên chứng chỉ gốc.
UWear sẽ phê duyệt khối lượng công việc USleep nếu:
- Mã thông báo vượt qua logic xác thực PKI.
- UWear sẽ xác thực mã thông báo bằng cách kiểm tra khai báo x5c dựa trên chứng chỉ gốc, kiểm tra xem mã thông báo có được ký bằng chứng chỉ lá hay không và cuối cùng là chứng chỉ gốc đã tải xuống có phải là chứng chỉ gốc trong khai báo x5c hay không.
- Các yêu cầu đo lường khối lượng công việc trong mã thông báo khớp với các điều kiện thuộc tính được chỉ định trong chính sách OPA. OPA là một công cụ chính sách nguồn mở, đa năng, hợp nhất việc thực thi chính sách trên toàn bộ ngăn xếp. OPA sử dụng các tài liệu có cú pháp tương tự như JSON để đặt các giá trị cơ sở mà chính sách được xác thực dựa trên đó. Hãy xem các giá trị cơ sở của OPA để biết ví dụ về những giá trị mà chính sách kiểm tra.
- Số chỉ dùng một lần khớp với số chỉ dùng một lần dự kiến (Tài liệu khoá đã xuất TLS). Điều này được xác minh trong chính sách OPA ở trên.
Sau khi hoàn tất và vượt qua tất cả các bước kiểm tra đó, UWear có thể xác nhận rằng dữ liệu sẽ được gửi và xử lý một cách an toàn. Sau đó, UWear sẽ phản hồi bằng PHI nhạy cảm qua cùng một phiên TLS và USleep sẽ có thể sử dụng dữ liệu đó để tính toán chất lượng giấc ngủ của khách hàng.
2. Thiết lập tài nguyên trên đám mây
Trước khi bắt đầu
- Thiết lập hai dự án trên Google Cloud, một cho USleep và một cho UWear. Để biết thêm thông tin về cách tạo dự án trên Google Cloud, vui lòng tham khảo lớp học lập trình"Thiết lập và khám phá dự án đầu tiên của bạn trên Google". Bạn có thể tham khảo bài viết tạo và quản lý dự án để biết thông tin chi tiết về cách truy xuất mã dự án và mã dự án khác với tên dự án và số dự án như thế nào.
- Bật tính năng Thanh toán cho các dự án của bạn.
- Trong Cloud Shell của một trong các dự án trên Google, hãy đặt các biến môi trường dự án bắt buộc như minh hoạ bên dưới.
export UWEAR_PROJECT_ID=<Google Cloud project id of UWear>
export USLEEP_PROJECT_ID=<Google Cloud project id of USleep>
- Bật Confidential Computing API và các API sau cho cả hai dự án.
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
- Truy xuất giá trị nhận dạng Principal bằng cách sử dụng
gcloud auth list
# Output should contain
# ACCOUNT: <Principal Identifier>
# Set your member variable
export MEMBER='user:<Principal Identifier>'
- Thêm quyền cho 2 dự án này. Bạn có thể thêm quyền bằng cách làm theo thông tin chi tiết trên trang web cấp vai trò IAM.
- Đối với
$UWEAR_PROJECT_ID, bạn sẽ cần có vai trò Quản trị viên Artifact Registry và Quản trị viên tài khoản dịch vụ.
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'
- Đối với
$USLEEP_PROJECT_ID, bạn sẽ cần có quyền Quản trị viên điện toán, Quản trị viên bộ nhớ, Quản trị viên Artifact Registry và Quản trị viên tài khoản dịch vụ.
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'
- Trong một dự án Google Cloud Cloud Shell, hãy sao chép Kho lưu trữ Github của Lớp học lập trình Confidential Space bằng lệnh bên dưới để lấy các tập lệnh bắt buộc được dùng trong lớp học lập trình này.
git clone https://github.com/GoogleCloudPlatform/confidential-space.git
- Thay đổi thư mục thành thư mục tập lệnh cho lớp học lập trình dữ liệu sức khoẻ.
cd confidential-space/codelabs/health_data_analysis_codelab/scripts
- Cập nhật 2 dòng này trong tập lệnh config_env.sh, nằm trong thư mục codelabs/health_data_analysis_codelab/scripts. Cập nhật mã dự án bằng mã dự án của bạn cho USleep và UWear. Nhớ xoá ký hiệu nhận xét "#" ở đầu dòng.
# TODO: Populate UWear and USleep Project IDs
export UWEAR_PROJECT_ID=your-uwear-project-id
export USLEEP_PROJECT_ID=your-usleep-project-id
- Không bắt buộc: Đặt mọi biến đã có từ trước. Bạn có thể ghi đè tên tài nguyên bằng các biến này (ví dụ:
export UWEAR_ARTIFACT_REPOSITORY='my-artifact-repository')
- Bạn có thể đặt các biến sau bằng tên tài nguyên đám mây hiện có. Nếu bạn đặt biến này, thì tài nguyên đám mây hiện có tương ứng trong dự án sẽ được dùng. Nếu bạn không đặt biến này, tên tài nguyên đám mây sẽ được tạo từ các giá trị trong tập lệnh config_env.sh.
- Chạy tập lệnh config_env.sh để đặt các tên biến còn lại thành các giá trị dựa trên mã dự án cho tên tài nguyên.
# 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. Tải chứng chỉ gốc xuống
- Để xác thực mã thông báo độc lập do dịch vụ chứng thực trả về, UWear sẽ cần xác thực chữ ký dựa trên chứng chỉ gốc của Confidential Space. UWear sẽ cần tải chứng chỉ gốc xuống và lưu trữ cục bộ. Trong một bảng điều khiển của dự án trên Google Cloud, hãy chạy các lệnh sau:
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
- Tạo vân tay số của chứng chỉ gốc đã tải xuống
openssl x509 -fingerprint -in confidential_space_root.pem -noout
- Xác minh rằng dấu vân tay khớp với thông báo SHA-1 sau đây:
B9:51:20:74:2C:24:E3:AA:34:04:2E:1C:3B:A3:AA:D2:8B:21:23:21
4. Tạo tài khoản dịch vụ cho tải
Bây giờ, bạn sẽ tạo hai tài khoản dịch vụ; một cho khối lượng công việc USleep và một cho khối lượng công việc UWear. Chạy tập lệnh create_service_accounts.sh để tạo tài khoản dịch vụ cho khối lượng công việc trong các dự án USleep và UWear. Các VM chạy khối lượng công việc sẽ sử dụng những tài khoản dịch vụ này.
# Navigate to the scripts folder
cd ~/confidential-space/codelabs/health_data_analysis_codelab/scripts
# Run the create_service_accounts script
./create_service_accounts.sh
Tập lệnh:
- Cấp vai trò
iam.serviceAccountUserđể đính kèm tài khoản dịch vụ vào khối lượng công việc. - Cấp vai trò
confidentialcomputing.workloadUsercho tài khoản dịch vụ của tải . Thao tác này sẽ cho phép tài khoản người dùng tạo mã thông báo chứng thực. - Cấp vai trò
logging.logWritercho quyền của tài khoản dịch vụ tải. Điều này cho phép môi trường Không gian bảo mật ghi nhật ký vào Cloud Logging ngoài Bảng điều khiển nối tiếp, do đó, nhật ký vẫn có sẵn sau khi VM bị chấm dứt.Tạo khối lượng công việc
5. Tạo đơn vị công việc USleep
Trong bước này, bạn sẽ tạo các hình ảnh Docker cho những khối lượng công việc được dùng trong lớp học lập trình này. Tải USleep là một ứng dụng Golang đơn giản, xác định chất lượng giấc ngủ của khách hàng bằng cách sử dụng thông tin sức khoẻ cá nhân trên thiết bị đeo.
Giới thiệu về khối lượng công việc USleep
Tải USleep là một ứng dụng Golang đơn giản, giúp xác định chất lượng giấc ngủ của khách hàng bằng cách sử dụng thông tin sức khoẻ cá nhân trên thiết bị đeo. Tải USleep có 3 phần chính:
- Thiết lập một phiên TLS và trích xuất Exported Keying Material (Tài liệu khoá đã xuất)
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
}
- Yêu cầu mã thông báo từ Dịch vụ chứng thực bằng đối tượng, số chỉ dùng một lần và loại mã thông báo 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
}
- Nhận dữ liệu nhạy cảm và tính toán chất lượng giấc ngủ của người dùng
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
...
}
Các bước tạo khối lượng công việc USleep
- Chạy tập lệnh create_usleep_workload.sh để tạo khối lượng công việc USleep. Tập lệnh này:
- Tạo Artifact Registry (
$USLEEP_ARTIFACT_REPOSITORY) do UWear sở hữu, nơi khối lượng công việc sẽ được xuất bản. - Tạo mã usleep/workload.go và đóng gói mã đó trong một hình ảnh Docker. Xem cấu hình Dockerfile cho USleep.
- Phát hành hình ảnh Docker lên Artifact Registry (
$USLEEP_ARTIFACT_REPOSITORY) do UWear sở hữu. - Cấp cho tài khoản dịch vụ
$USLEEP_WORKLOAD_SERVICE_ACCOUNTquyền đọc đối với Artifact Registry ($USLEEP_ARTIFACT_REPOSITORY).
./create_usleep_workload.sh
- Quan trọng: Trong nhật ký đầu ra, hãy trích xuất mã nhận dạng hình ảnh cho USleep.
latest: digest: sha256:<USLEEP_IMAGE_DIGEST> size: 945
- Chuyển đến thư mục UWear
cd ~/confidential-space/codelabs/health_data_analysis_codelab/src/uwear
- Thay thế giá trị trong "allowed_submods_container_image_digest" trong opa_validation_values.json bằng 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. Tạo khối lượng công việc UWear
Giới thiệu về khối lượng công việc UWear
Tải UWear có 4 phần chính:
- Tham gia cùng một phiên TLS được tạo trong khối lượng công việc USleep và truy xuất mã thông báo chứng thực từ USleep qua phiên TLS an toàn.
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)
...
}
- Xác thực mã thông báo độc lập bằng cách:
- Kiểm tra xem khai báo x5c có chứa một chuỗi chứng chỉ được liên kết chính xác từ chứng chỉ thực thể cuối đến chứng chỉ trung gian và cuối cùng là chứng chỉ gốc hay không.
- Kiểm tra xem mã thông báo có được ký bằng chứng chỉ thực thể cuối có trong khai báo x5c hay không.
- Kiểm tra xem chứng chỉ gốc đã tải xuống / lưu trữ có phải là chứng chỉ gốc trong khai báo x5c hay không.
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
}
- Sau đó, khối lượng công việc UWear sẽ kiểm tra xem các yêu cầu đo lường khối lượng công việc trong mã thông báo có khớp với các điều kiện thuộc tính được chỉ định trong chính sách OPA hay không. OPA là một công cụ chính sách nguồn mở, đa năng, hợp nhất việc thực thi chính sách trên toàn bộ ngăn xếp. OPA sử dụng các tài liệu có cú pháp tương tự như JSON để đặt các giá trị cơ sở mà chính sách được xác thực dựa trên đó.
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
}
- Ví dụ về các giá trị cơ sở của OPA:
{
"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"
]
}
- Ví dụ về chính sách OPA được viết bằng Rego.
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"
}
- Ví dụ về truy vấn 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
"
- Trong quá trình xác thực OPA, tải UWear cũng xác thực rằng số chỉ dùng một lần khớp với số chỉ dùng một lần dự kiến (Tài liệu khoá đã xuất TLS – EKM). Số chỉ dùng một lần được xác minh trong chính sách OPA bằng cách sử dụng EKM được truyền vào trình đánh giá chính sách.
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
}
- Sau khi hoàn tất và vượt qua tất cả các bước kiểm tra đó, UWear có thể xác nhận rằng dữ liệu sẽ được gửi và xử lý một cách an toàn. Sau đó, UWear sẽ phản hồi bằng PHI nhạy cảm qua cùng một phiên TLS và USleep sẽ có thể sử dụng dữ liệu đó để tính toán chất lượng giấc ngủ của khách hàng.
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()
...
}
Các bước tạo khối lượng công việc USleep
- Chuyển đến thư mục tập lệnh
cd ~/confidential-space/codelabs/health_data_analysis_codelab/scripts
- Chạy tập lệnh create_uwear_workload.sh để tạo tải UWear:
- Tạo Artifact Registry (
$UWEAR_ARTIFACT_REPOSITORY) do UWear sở hữu, nơi khối lượng công việc sẽ được xuất bản. - Tạo mã uwear/workload.go và đóng gói mã đó trong một hình ảnh Docker. Xem cấu hình Dockerfile cho USleep.
- Phát hành hình ảnh Docker lên Artifact Registry (
$UWEAR_ARTIFACT_REPOSITORY) do UWear sở hữu. - Cấp cho tài khoản dịch vụ
$UWEAR_WORKLOAD_SERVICE_ACCOUNTquyền đọc đối với Artifact Registry ($UWEAR_ARTIFACT_REPOSITORY).
./create_uwear_workload.sh
7. Chạy các khối lượng công việc USleep và UWear
Chạy khối lượng công việc 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
Phản hồi phải trả về STATUS: RUNNING và EXTERNAL_IP cũng phải được trả về tương tự như phản hồi này:
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
Lưu trữ IP bên ngoài trong một biến
export USLEEP_EXTERNAL_IP=<add your external IP>
Xác minh rằng Workload USleep đã chạy đúng cách
Để xác minh rằng tải USleep đang chạy đúng cách, hãy chuyển đến trang Phiên bản máy ảo trong dự án USleep. Nhấp vào phiên bản "usleep" rồi nhấn vào "Serial port 1(console)" (Cổng nối tiếp 1 (bảng điều khiển)) trong phần Nhật ký. Sau khi máy chủ hoạt động, ở cuối nhật ký, nhật ký sẽ hiển thị nội dung tương tự như sau.
2024/09/13 17:00:00 workload task started
#####----- Local IP Address is <YOUR-LOCAL-IP> -----#####
Starting Server..
Chạy khối lượng công việc 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
Xác minh rằng tải UWear đã chạy đúng cách
Để xem nhật ký của tải công việc UWear, hãy chuyển đến trang Phiên bản máy ảo trong dự án UWear. Nhấp vào phiên bản "uwear" rồi nhấn vào "Serial port 1(console)" (Cổng nối tiếp 1 (bảng điều khiển)) trong phần Nhật ký.
Đầu ra nhật ký sau khi phiên bản khởi động hoàn tất sẽ có dạng như sau
Trong dự án UWear, nhật ký nối tiếp sẽ cho thấy nội dung tương tự như
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
Nếu khối lượng công việc UWear của bạn không giống như thế này, hãy xem các ghi chú bên dưới để biết hướng dẫn.
Xem kết quả của USleep
Để xem kết quả, hãy quay lại trang Phiên bản máy ảo trong dự án USleep. Nhấp vào phiên bản "usleep" rồi nhấn vào "Serial port 1(console)" (Cổng nối tiếp 1 (bảng điều khiển)) trong phần Nhật ký. Xem kết quả của khối lượng công việc ở cuối nhật ký. Các tệp này sẽ có dạng tương tự như mẫu bên dưới.
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
Kết quả phải là "total sleep time is less than 8 hours".
Xin chúc mừng, bạn đã tạo thành công một Không gian riêng tư giữa UWear và USleep để chia sẻ thông tin nhạy cảm!
8. (Không bắt buộc) Chạy khối lượng công việc trái phép
Trong trường hợp tiếp theo, USleep sẽ cập nhật mã và chạy một khối lượng công việc khác trên dữ liệu về giấc ngủ do UWear cung cấp. UWear chưa đồng ý với khối lượng công việc mới này và chưa cập nhật chính sách OPA để cho phép sử dụng mã nhận dạng hình ảnh mới. Chúng tôi sẽ xác minh rằng UWear sẽ không gửi dữ liệu nhạy cảm của mình đến tải công việc trái phép.
USleep điều chỉnh khối lượng công việc
- Đặt dự án thành $USLEEP_PROJECT_ID.
gcloud config set project $USLEEP_PROJECT_ID
- Xoá phiên bản máy ảo USleep.
gcloud compute instances delete usleep --zone $USLEEP_PROJECT_ZONE
- Chuyển đến thư mục usleep/workload.go.
cd ~/confidential-space/codelabs/health_data_analysis_codelab/src/usleep
- Trong tệp usleep/workload.go. Cập nhật dòng
"audience": "uwear".Trong ví dụ này, để thay đổi mã nhận dạng hình ảnh, chúng ta sẽ cập nhật đối tượng thành một giá trị khác mà UWear chưa phê duyệt. Vì vậy, UWear nên từ chối yêu cầu này vì 2 lý do: bản tóm tắt hình ảnh chưa được phê duyệt và đối tượng không chính xác.
"audience": "anotherCompany.com",
- Tạo khối lượng công việc USleep mới
cd ~/confidential-space/codelabs/health_data_analysis_codelab/scripts
./create_usleep_workload.sh
- Tạo phiên bản máy ảo USleep mới và chạy khối lượng công việc
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
- Trích xuất IP bên ngoài mới của USleep để sử dụng sau này
export USLEEP_EXTERNAL_IP=<add your external IP>
Chạy lại khối lượng công việc
- Xoá phiên bản máy ảo UWear
gcloud config set project $UWEAR_PROJECT_ID
gcloud compute instances delete uwear --zone $UWEAR_PROJECT_ZONE
- Tạo lại phiên bản máy ảo UWear bằng IP bên ngoài mới
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
- Trong nhật ký nối tiếp UWear, thông báo sau sẽ xuất hiện và VM USleep sẽ không nhận được bất kỳ dữ liệu nhạy cảm nào
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. Dọn dẹp
Bạn có thể dùng tập lệnh dọn dẹp để dọn dẹp các tài nguyên mà chúng ta đã tạo trong lớp học lập trình này. Trong quá trình dọn dẹp này, các tài nguyên sau đây sẽ bị xoá:
- Tài khoản dịch vụ UWear (
$UWEAR_SERVICE_ACCOUNT). - Sổ đăng ký cấu phần phần mềm UWear (
$UWEAR_ARTIFACT_REPOSITORY). - Đối tượng trong Compute Engine của UWear
- Tài khoản dịch vụ USleep (
$USLEEP_SERVICE_ACCOUNT). - Sổ đăng ký cấu phần phần mềm USleep (
$USLEEP_ARTIFACT_REPOSITORY). - Đối tượng điện toán USleep
./cleanup.sh
Nếu bạn đã khám phá xong, vui lòng cân nhắc xoá dự án của mình bằng cách làm theo các hướng dẫn này.
Xin chúc mừng
Xin chúc mừng, bạn đã hoàn tất thành công lớp học lập trình này!
Bạn đã tìm hiểu cách chia sẻ dữ liệu một cách an toàn trong khi vẫn giữ được tính bảo mật của dữ liệu bằng Confidential Space.
Tiếp theo là gì?
Hãy xem một số lớp học lập trình tương tự này...
- Bảo mật các mô hình học máy và Tài sản trí tuệ bằng Confidential Space
- Cách giao dịch tài sản kỹ thuật số bằng tính toán nhiều bên và không gian bảo mật
- Phân tích dữ liệu mật bằng Không gian bảo mật
Tài liệu đọc thêm
- Bạn cảm thấy bị cô lập? Điện toán bảo mật sẽ giúp bạn
- Điện toán bảo mật tại GCP
- Không gian bảo mật: Tương lai của hoạt động cộng tác bảo đảm quyền riêng tư
- Cách Google và Intel tăng cường bảo mật cho Điện toán bảo mật
- Quyền riêng tư so với tiến trình – Nâng cao tính bảo mật bằng Điện toán bảo mật của Google Cloud