1. Ringkasan
Confidential Space menawarkan berbagi dan kolaborasi data multi-pihak yang aman, sekaligus memungkinkan organisasi menjaga kerahasiaan data mereka. Artinya, organisasi dapat berkolaborasi satu sama lain sekaligus tetap mengontrol data mereka dan melindunginya dari akses yang tidak sah.
Confidential Space memungkinkan skenario saat Anda ingin memperoleh manfaat bersama dari menggabungkan dan menganalisis data sensitif, yang sering kali diatur, sekaligus mempertahankan kontrol penuh atas data tersebut. Dengan Confidential Space, organisasi dapat memperoleh manfaat bersama dari menggabungkan dan menganalisis data sensitif seperti informasi identitas pribadi (PII), informasi kesehatan terlindungi (PHI), kekayaan intelektual, dan rahasia kriptografi — sekaligus mempertahankan kontrol penuh atas data tersebut.
Yang Anda butuhkan
- Dua project Google Cloud Platform terpisah
- Browser, seperti Chrome atau Firefox
- Pengetahuan dasar tentang Google Compute Engine, Confidential VM, Containers, dan repositori jarak jauh, sertifikat, serta rantai sertifikat.
- Pengetahuan dasar tentang Akun Layanan, Open Policy Agent, Rego, dan Public Key Infrastructure
Yang akan Anda pelajari
- Cara mengonfigurasi resource Cloud yang diperlukan untuk menjalankan Confidential Space
- Cara menjalankan workload di Confidential VM yang menjalankan image Confidential Space
- Cara mengizinkan akses ke resource yang dilindungi berdasarkan atribut kode workload (apa), lingkungan Confidential Space (di mana), dan akun yang menjalankan workload (siapa).
Codelab ini berfokus pada cara menggunakan Confidential Space dengan resource yang dilindungi yang dihosting di tempat selain Google Cloud. Anda akan mempelajari cara meminta token kustom yang mandiri dari Layanan Pengesahan Google dengan memberikan nonce, audiens, dan jenis token PKI.
Dalam codelab ini, Anda akan menyiapkan Ruang Rahasia antara produk fiktif - USleep, aplikasi yang di-container, dan produk fiktif - UWear, perangkat wearable yang terhubung, untuk menghitung kualitas tidur Anda. UWear akan membagikan informasi kesehatan terlindungi (PHI) kepada USleep di lingkungan yang aman, terlindungi, dan terisolasi (alias Trusted Execution Environment atau TEE) sehingga pemilik data tetap memiliki kerahasiaan yang lengkap.
UWear adalah auditor workload dan pemilik data. Sebagai auditor workload,, ia meninjau kode dalam workload yang sedang dijalankan dan mencatat ringkasan image. Sebagai pemilik data, UWear menulis logika verifikasi untuk memeriksa validitas token dan tanda tangannya. Kebijakan validasi ditulis menggunakan ringkasan image workload yang diaudit, yang hanya mengizinkan ringkasan image tertentu, di lingkungan tertentu, untuk mendapatkan akses ke data sensitif.
USleep, dalam codelab ini, men-deploy aplikasi dalam container. USleep tidak memiliki akses ke data sensitif, tetapi menjalankan beban kerja yang disetujui yang diizinkan mengakses data sensitif.
Codelab ini mencakup langkah-langkah berikut:
- Langkah 1: Siapkan resource cloud yang diperlukan untuk codelab. Siapkan project, penagihan, dan izin. Download kode sumber codelab dan tetapkan variabel lingkungan.
- Langkah 2: Download sertifikat root dan simpan dengan kode sumber UWear Anda.
- Langkah 3: Buat akun layanan workload terpisah yang akan digunakan oleh VM workload untuk USleep dan UWear.
- Langkah 4: Buat workload USleep yang menyediakan token pengesahan.
- Langkah 5: Buat beban kerja UWear yang memvalidasi token pengesahan dan mengirim data sensitif jika token disetujui.
- Langkah 6: Jalankan beban kerja USleep dan UWear. UWear akan memberikan data sensitif, dan USleep akan menjalankan algoritma tidur pada data tersebut dan menghasilkan output.
- Langkah 7: (Opsional) Jalankan beban kerja USleep yang tidak sah dan konfirmasi bahwa data sensitif belum diterima dari UWear.
- Langkah 8: Bersihkan semua resource.
Memahami alur kerja
USleep akan menjalankan workload di Confidential Space. Untuk menjalankan workload, workload memerlukan akses ke PHI UWear. Untuk mendapatkan akses, beban kerja USleep terlebih dahulu membuat sesi TLS yang aman. USleep kemudian juga akan meminta token pengesahan dari Layanan Pengesahan Google dengan payload.
USleep akan meminta token pengesahan dengan payload JSON yang akan berisi tiga hal:
- Token pengesahan yang terikat ke sesi TLS. Untuk mengikat token pengesahan ke sesi TLS, nilai nonce akan menjadi hash TLS Exported Keying Material. Mengikat token ke sesi TLS memastikan bahwa tidak ada serangan man-in-the-middle yang terjadi karena hanya dua pihak yang terlibat dalam sesi TLS yang dapat membuat nilai nonce.
- Audiens "uwear" akan disediakan. UWear akan memverifikasi bahwa UWear adalah audiens yang dituju untuk token pengesahan.
- Jenis token "PKI". Jenis token "PKI" berarti USleep ingin meminta token mandiri. Token mandiri dapat diverifikasi bahwa token tersebut ditandatangani oleh Google menggunakan root yang didownload dari endpoint PKI Confidential Space yang sudah dikenal. Hal ini berbeda dengan jenis token OIDC default, yang tanda tangannya diverifikasi menggunakan kunci publik yang dirotasi secara rutin.

Workload USleep menerima token pengesahan. Kemudian, UWear bergabung dengan koneksi TLS dengan USleep dan mengambil token pengesahan USleep. UWear akan memvalidasi token dengan memeriksa klaim x5c terhadap root certificate.
UWear akan menyetujui workload USleep jika:
- Token lulus logika validasi PKI.
- UWear akan memvalidasi token dengan memeriksa klaim x5c terhadap sertifikat root, memeriksa apakah token ditandatangani oleh sertifikat leaf, dan terakhir memastikan bahwa sertifikat root yang didownload adalah root yang sama seperti dalam klaim x5c.
- Klaim pengukuran workload dalam token cocok dengan kondisi atribut yang ditentukan dalam kebijakan OPA. OPA adalah mesin kebijakan serbaguna open source yang menyatukan penegakan kebijakan di seluruh stack. OPA menggunakan dokumen, dengan sintaksis yang mirip dengan JSON, untuk menetapkan nilai dasar yang divalidasi oleh kebijakan. Lihat Nilai dasar OPA untuk mengetahui contoh nilai yang diperiksa oleh kebijakan.
- Nonce cocok dengan nonce yang diharapkan (Materi Kunci yang Diekspor TLS). Hal ini diverifikasi dalam kebijakan OPA di atas.
Setelah semua pemeriksaan tersebut selesai dan berhasil, UWear dapat mengonfirmasi bahwa data akan dikirim dan diproses dengan aman. Kemudian, UWear akan merespons kembali dengan PHI sensitif melalui sesi TLS yang sama dan USleep akan dapat menggunakan data tersebut untuk menghitung kualitas tidur pelanggan.
2. Menyiapkan Resource Cloud
Sebelum memulai
- Siapkan dua project Google Cloud, satu untuk USleep dan satu untuk UWear. Untuk mengetahui informasi selengkapnya tentang cara membuat project Google Cloud, lihat codelab"Menyiapkan dan menjelajahi project Google pertama Anda". Anda dapat melihat membuat dan mengelola project untuk mendapatkan detail tentang cara mengambil project ID dan perbedaannya dengan nama project dan nomor project.
- Aktifkan Penagihan untuk project Anda.
- Di salah satu Cloud Shell project Google, tetapkan variabel lingkungan project yang diperlukan seperti yang ditunjukkan di bawah.
export UWEAR_PROJECT_ID=<Google Cloud project id of UWear>
export USLEEP_PROJECT_ID=<Google Cloud project id of USleep>
- Aktifkan Confidential Computing API dan API berikut untuk kedua project.
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
- Mengambil ID Principal Anda menggunakan
gcloud auth list
# Output should contain
# ACCOUNT: <Principal Identifier>
# Set your member variable
export MEMBER='user:<Principal Identifier>'
- Tambahkan izin untuk kedua project ini. Izin dapat ditambahkan dengan mengikuti detail di halaman web pemberian peran IAM.
- Untuk
$UWEAR_PROJECT_ID, Anda memerlukan Administrator Artifact Registry dan Admin Akun Layanan.
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'
- Untuk
$USLEEP_PROJECT_ID, Anda memerlukan Compute Admin, Storage Admin, Artifact Registry Administrator, dan Service Account 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'
- Di salah satu Cloud Shell project Google Cloud Anda, clone Confidential Space Codelab Github Repository menggunakan perintah di bawah untuk mendapatkan skrip yang diperlukan yang digunakan sebagai bagian dari codelab ini.
git clone https://github.com/GoogleCloudPlatform/confidential-space.git
- Ubah direktori ke direktori skrip untuk codelab data kesehatan.
cd confidential-space/codelabs/health_data_analysis_codelab/scripts
- Perbarui dua baris dalam skrip config_env.sh ini, yang ada di direktori codelabs/health_data_analysis_codelab/scripts. Perbarui project ID dengan project ID Anda untuk USleep dan UWear. Pastikan untuk menghapus simbol komentar "#" di awal baris.
# TODO: Populate UWear and USleep Project IDs
export UWEAR_PROJECT_ID=your-uwear-project-id
export USLEEP_PROJECT_ID=your-usleep-project-id
- Opsional: Tetapkan variabel yang sudah ada. Anda dapat mengganti nama resource menggunakan variabel ini (misalnya,
export UWEAR_ARTIFACT_REPOSITORY='my-artifact-repository')
- Anda dapat menetapkan variabel berikut dengan nama resource cloud yang ada. Jika variabel ditetapkan, resource cloud yang ada dan sesuai dari project akan digunakan. Jika variabel tidak ditetapkan, nama resource cloud akan dibuat dari nilai dalam skrip config_env.sh.
- Jalankan skrip config_env.sh untuk menyetel nama variabel yang tersisa ke nilai berdasarkan ID project Anda untuk nama resource.
# 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. Download root certificate
- Untuk memvalidasi token mandiri yang ditampilkan dari layanan pengesahan, UWear harus memvalidasi tanda tangan terhadap sertifikat root Confidential Space. UWear perlu mendownload sertifikat root dan menyimpannya secara lokal. Di salah satu konsol project Google Cloud Anda, jalankan perintah berikut:
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
- Buat sidik jari sertifikat root yang didownload
openssl x509 -fingerprint -in confidential_space_root.pem -noout
- Pastikan sidik jari cocok dengan ringkasan SHA-1 berikut:
B9:51:20:74:2C:24:E3:AA:34:04:2E:1C:3B:A3:AA:D2:8B:21:23:21
4. Buat akun layanan workload
Sekarang, Anda akan membuat dua akun layanan; satu untuk workload USleep dan satu untuk workload UWear. Jalankan skrip create_service_accounts.sh untuk membuat akun layanan beban kerja di project USleep dan UWear. VM yang menjalankan workload akan menggunakan akun layanan ini.
# Navigate to the scripts folder
cd ~/confidential-space/codelabs/health_data_analysis_codelab/scripts
# Run the create_service_accounts script
./create_service_accounts.sh
Skrip:
- Memberikan peran
iam.serviceAccountUseryang melampirkan akun layanan ke workload. - Memberikan peran
confidentialcomputing.workloadUserke akun layanan workload . Tindakan ini akan memungkinkan akun pengguna membuat token pengesahan. - Memberikan izin peran
logging.logWriterke akun layanan workload. Hal ini memungkinkan lingkungan Confidential Space menulis log ke Cloud Logging selain Konsol Serial, sehingga log tersedia setelah VM dihentikan.Buat beban kerja
5. Membuat Beban Kerja USleep
Sebagai bagian dari langkah ini, Anda akan membuat image Docker untuk beban kerja yang digunakan dalam codelab ini. Beban kerja USleep adalah aplikasi Golang sederhana yang menentukan kualitas tidur pelanggan menggunakan informasi kesehatan pribadi di perangkat wearable.
Tentang Workload USleep
Beban kerja USleep adalah aplikasi Golang sederhana yang menentukan kualitas tidur pelanggan, menggunakan informasi kesehatan pribadi di perangkat wearable. Beban kerja USleep memiliki tiga bagian utama:
- Menyiapkan Sesi TLS dan mengekstrak Materi Kunci yang Diekspor
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
}
- Meminta token dari Layanan Pengesahan dengan audiens, nonce, dan jenis token 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
}
- Menerima data sensitif dan menghitung kualitas tidur pengguna
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
...
}
Langkah-langkah untuk membuat Beban Kerja USleep
- Jalankan skrip create_usleep_workload.sh untuk membuat workload USleep. Skrip ini:
- Membuat Artifact Registry (
$USLEEP_ARTIFACT_REPOSITORY) yang dimiliki oleh UWear tempat workload akan dipublikasikan. - Membangun kode usleep/workload.go dan mengemasnya dalam image Docker. Lihat konfigurasi Dockerfile untuk USleep.
- Memublikasikan image Docker ke Artifact Registry (
$USLEEP_ARTIFACT_REPOSITORY) yang dimiliki oleh UWear. - Memberikan izin baca
$USLEEP_WORKLOAD_SERVICE_ACCOUNTuntuk akun layanan bagi Artifact Registry ($USLEEP_ARTIFACT_REPOSITORY).
./create_usleep_workload.sh
- Penting: Dalam log output, ekstrak ringkasan gambar untuk USleep.
latest: digest: sha256:<USLEEP_IMAGE_DIGEST> size: 945
- Buka direktori UWear
cd ~/confidential-space/codelabs/health_data_analysis_codelab/src/uwear
- Ganti nilai di bagian "allowed_submods_container_image_digest" dalam opa_validation_values.json dengan 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. Membuat Beban Kerja UWear
Tentang Beban Kerja UWear
Workload UWear memiliki 4 bagian utama:
- Bergabung dengan sesi TLS yang sama yang dibuat di workload USleep dan mengambil token pengesahan dari USleep melalui Sesi TLS yang aman.
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)
...
}
- Memvalidasi token mandiri dengan:
- Memeriksa klaim x5c berisi rantai sertifikat yang dirantai dengan benar dari sertifikat leaf ke sertifikat perantara dan akhirnya ke sertifikat root.
- Memeriksa apakah token ditandatangani oleh sertifikat leaf yang terdapat dalam klaim x5c.
- Memeriksa root certificate yang didownload / disimpan adalah root yang sama seperti dalam klaim 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
}
- Kemudian, workload UWear akan memeriksa apakah klaim pengukuran workload dalam token cocok dengan kondisi atribut yang ditentukan dalam kebijakan OPA. OPA adalah mesin kebijakan serbaguna open source yang menyatukan penegakan kebijakan di seluruh stack. OPA menggunakan dokumen, dengan sintaksis yang mirip dengan JSON, untuk menetapkan nilai dasar yang divalidasi oleh kebijakan.
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
}
- Contoh nilai dasar 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"
]
}
- Contoh kebijakan OPA yang ditulis dalam 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"
}
- Contoh Kueri 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
"
- Selama validasi OPA, beban kerja UWear juga memvalidasi bahwa nonce cocok dengan nonce yang diharapkan (Materi Keying yang Diekspor - EKM TLS). Nonce diverifikasi dalam kebijakan OPA menggunakan EKM yang diteruskan ke evaluator kebijakan.
Contoh kode untuk mendapatkan Hash 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
}
- Setelah semua pemeriksaan tersebut selesai dan berhasil, UWear dapat mengonfirmasi bahwa data akan dikirim dan diproses dengan aman. Kemudian, UWear akan merespons kembali dengan PHI sensitif melalui sesi TLS yang sama dan USleep akan dapat menggunakan data tersebut untuk menghitung kualitas tidur pelanggan.
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()
...
}
Langkah-langkah untuk membuat Workload USleep
- Buka direktori skrip
cd ~/confidential-space/codelabs/health_data_analysis_codelab/scripts
- Jalankan skrip create_uwear_workload.sh untuk membuat workload UWear:
- Membuat Artifact Registry (
$UWEAR_ARTIFACT_REPOSITORY) yang dimiliki oleh UWear tempat workload akan dipublikasikan. - Membangun kode uwear/workload.go dan mengemasnya dalam image Docker. Lihat konfigurasi Dockerfile untuk USleep.
- Memublikasikan image Docker ke Artifact Registry (
$UWEAR_ARTIFACT_REPOSITORY) yang dimiliki oleh UWear. - Memberikan izin baca
$UWEAR_WORKLOAD_SERVICE_ACCOUNTuntuk akun layanan bagi Artifact Registry ($UWEAR_ARTIFACT_REPOSITORY).
./create_uwear_workload.sh
7. Menjalankan Workload USleep dan UWear
Menjalankan workload 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
Respons akan menampilkan STATUS: RUNNING dan EXTERNAL_IP juga akan ditampilkan seperti ini:
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
Simpan IP eksternal dalam variabel
export USLEEP_EXTERNAL_IP=<add your external IP>
Memverifikasi bahwa Beban Kerja USleep berjalan dengan benar
Untuk memverifikasi bahwa beban kerja USleep berjalan dengan benar, buka halaman VM Instances di project USleep. Klik instance "usleep" dan tekan "Serial port 1(console)" di bagian Logs. Setelah server aktif dan berjalan, di bagian bawah log, log akan menampilkan sesuatu yang mirip dengan berikut.
2024/09/13 17:00:00 workload task started
#####----- Local IP Address is <YOUR-LOCAL-IP> -----#####
Starting Server..
Menjalankan workload 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
Memastikan workload UWear berjalan dengan benar
Untuk melihat log workload UWear, buka halaman VM Instances di project UWear. Klik instance "uwear" dan tekan "Serial port 1(console)" di bagian Logs.
Output log setelah instance dimulai sepenuhnya akan terlihat seperti ini
Di project UWear, log serial akan menampilkan sesuatu yang mirip dengan
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
Jika beban kerja UWear Anda tidak terlihat seperti ini, lihat catatan di bawah untuk mengetahui petunjuknya.
Melihat hasil USleep
Untuk melihat hasilnya, kembali ke halaman VM Instances di project USleep. Klik instance "usleep" dan tekan "Serial port 1(console)" di bagian Logs. Lihat hasil beban kerja di bagian bawah log. Tampilannya akan terlihat seperti contoh di bawah.
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
Hasilnya harus "total sleep time is less than 8 hours".
Selamat, Anda telah berhasil membuat Ruang Rahasia antara UWear dan USleep untuk membagikan informasi sensitif.
8. (Opsional) Menjalankan Workload yang Tidak Sah
Dalam skenario berikutnya, USleep memperbarui kode dan menjalankan beban kerja yang berbeda pada data tidur yang disediakan oleh UWear. UWear belum menyetujui workload baru ini dan belum memperbarui kebijakan OPA-nya untuk mengizinkan image digest baru. Kami akan memverifikasi bahwa UWear tidak akan mengirimkan data sensitifnya ke workload yang tidak sah.
USleep mengubah beban kerjanya
- Tetapkan project ke $USLEEP_PROJECT_ID.
gcloud config set project $USLEEP_PROJECT_ID
- Hapus instance VM USleep.
gcloud compute instances delete usleep --zone $USLEEP_PROJECT_ZONE
- Buka direktori usleep/workload.go.
cd ~/confidential-space/codelabs/health_data_analysis_codelab/src/usleep
- Di file usleep/workload.go. Perbarui baris
"audience": "uwear".Dalam contoh ini, untuk mengubah ringkasan gambar, kita akan memperbarui audiens ke nilai lain yang belum disetujui UWear. Jadi, UWear harus menolaknya karena dua alasan - ringkasan gambar yang tidak disetujui dan audiens yang salah.
"audience": "anotherCompany.com",
- Buat beban kerja USleep baru
cd ~/confidential-space/codelabs/health_data_analysis_codelab/scripts
./create_usleep_workload.sh
- Buat Instance VM USleep baru dan jalankan workload
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
- Ekstrak IP eksternal USleep baru untuk digunakan nanti
export USLEEP_EXTERNAL_IP=<add your external IP>
Menjalankan ulang workload
- Hapus instance VM UWear
gcloud config set project $UWEAR_PROJECT_ID
gcloud compute instances delete uwear --zone $UWEAR_PROJECT_ZONE
- Buat ulang instance VM UWear menggunakan IP Eksternal baru
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
- Di log serial UWear, pesan berikut akan muncul dan VM USleep tidak akan menerima data sensitif apa pun
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. Pembersihan
Skrip pembersihan dapat digunakan untuk membersihkan resource yang telah kita buat sebagai bagian dari codelab ini. Sebagai bagian dari pembersihan ini, resource berikut akan dihapus:
- Akun layanan UWear (
$UWEAR_SERVICE_ACCOUNT). - Registry artefak UWear (
$UWEAR_ARTIFACT_REPOSITORY). - Instance Compute UWear
- Akun layanan USleep (
$USLEEP_SERVICE_ACCOUNT). - Registry artefak USleep (
$USLEEP_ARTIFACT_REPOSITORY). - Instance Compute USleep
./cleanup.sh
Jika Anda sudah selesai menjelajahi, pertimbangkan untuk menghapus project Anda dengan mengikuti petunjuk ini.
Selamat
Selamat, Anda berhasil menyelesaikan codelab ini.
Anda telah mempelajari cara membagikan data dengan aman sambil mempertahankan kerahasiaannya menggunakan Confidential Space.
Apa selanjutnya?
Lihat beberapa codelab serupa ini...
- Mengamankan model ML dan Kekayaan Intelektual menggunakan Confidential Space
- Cara melakukan transaksi aset digital dengan komputasi banyak pihak dan ruang rahasia
- Menganalisis data rahasia dengan Ruang rahasia
Bacaan lebih lanjut
- Merasa terisolasi? Confidential computing hadir untuk membantu
- Confidential Computing di GCP
- Confidential Space: Masa depan kolaborasi yang menjaga privasi
- Cara Google dan Intel membuat Confidential Computing lebih aman
- Privasi vs. Progres - Meningkatkan Keamanan dengan Confidential Computing Google Cloud