1. Ringkasan
Confidential Space menawarkan kolaborasi dan berbagi data multipihak yang aman, sekaligus memungkinkan organisasi menjaga kerahasiaan data mereka. Artinya, organisasi dapat berkolaborasi satu sama lain sambil tetap mempertahankan kontrol atas data mereka dan melindunginya dari akses yang tidak sah.
Confidential Space membuka skenario saat Anda ingin memperoleh manfaat bersama dari menggabungkan dan menganalisis data sensitif yang sering 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 kriptografis — 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, Container, dan repositori jarak jauh, sertifikat, serta rantai sertifikat.
- Pengetahuan dasar tentang Akun Layanan, Open Policy Agent, Rego, dan Infrastruktur Kunci Publik
Yang akan Anda pelajari
- Cara mengonfigurasi resource Cloud yang diperlukan untuk menjalankan Confidential Space
- Cara menjalankan beban kerja di Confidential VM yang menjalankan image Confidential Space
- Cara memberikan otorisasi 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 Ruang Rahasia dengan resource yang dilindungi dan dihosting di tempat selain Google Cloud. Anda akan mempelajari cara meminta token kustom 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 dalam penampung, dan produk fiktif - UWear, perangkat wearable 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 mempertahankan kerahasiaan sepenuhnya.
UWear adalah auditor beban kerja dan pemilik data. Sebagai auditor beban kerja, alat ini meninjau kode dalam beban kerja yang sedang berjalan dan mencatat ringkasan image. Sebagai pemilik data, UWear menulis logika verifikasi untuk memeriksa validitas token dan tanda tangannya. Fitur ini menulis kebijakan validasi, 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 untuk mengakses data sensitif.
Codelab ini melibatkan langkah-langkah berikut:
- Langkah 1: Siapkan resource cloud yang diperlukan untuk codelab. Menyiapkan 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 beban kerja terpisah yang akan digunakan oleh VM beban kerja 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 dan menghasilkan output.
- Langkah 7: (Opsional) Jalankan beban kerja USleep yang tidak sah dan pastikan data sensitif belum diterima dari UWear.
- Langkah 8: Bersihkan semua resource.
Memahami alur kerja
USleep akan menjalankan workload di Confidential Space. Untuk menjalankan beban kerja, aplikasi memerlukan akses ke PHI UWear. Untuk mendapatkan akses, beban kerja USleep pertama-tama membuat sesi TLS yang aman. USleep 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 dengan sesi TLS. Untuk mengikat token pengesahan ke sesi TLS, nilai nonce akan berupa hash TLS Exported Keying Material. Mengikat token ke sesi TLS memastikan bahwa tidak ada serangan machine-in-the-middle yang terjadi karena hanya dua pihak yang terlibat dalam sesi TLS yang dapat menghasilkan nilai nonce.
- Audiens "uwear" akan disediakan. UWear akan memverifikasi bahwa perangkat tersebut adalah audiens yang dimaksud untuk token pengesahan.
- Jenis token "PKI". Jenis token "PKI" berarti USleep ingin meminta token mandiri. Token mandiri dapat diverifikasi bahwa token ditandatangani oleh Google menggunakan root yang didownload dari endpoint PKI terkenal Confidential Space. Hal ini berbeda dengan jenis token OIDC default, yang tanda tangannya diverifikasi menggunakan kunci publik yang dirotasi secara berkala.
Workload USleep menerima token pengesahan. UWear kemudian 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 root certificate, memeriksa token ditandatangani oleh leaf certificate, dan terakhir bahwa root certificate yang didownload adalah root yang sama seperti dalam klaim x5c.
- Klaim pengukuran beban kerja dalam token cocok dengan kondisi atribut yang ditentukan dalam kebijakan OPA. OPA adalah mesin kebijakan open source serbaguna yang menyatukan penerapan kebijakan di seluruh stack. OPA menggunakan dokumen, dengan sintaksis yang mirip dengan JSON, untuk menetapkan nilai dasar pengukuran yang digunakan untuk memvalidasi kebijakan. Lihat Nilai dasar pengukuran OPA untuk mengetahui contoh nilai yang diperiksa 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 lulus, UWear dapat mengonfirmasi bahwa data akan dikirim dan diproses dengan aman. UWear kemudian 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 informasi selengkapnya tentang cara membuat project Google Cloud, lihat codelab"Menyiapkan dan menjelajahi project Google pertama Anda". Anda dapat membaca 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
- Ambil ID Entitas utama 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 memberikan 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 Repositori GitHub Codelab Ruang Rahasia 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 terletak 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 menetapkan nama variabel yang tersisa ke nilai berdasarkan project ID 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. Mendownload root certificate
- Untuk memvalidasi token mandiri yang ditampilkan dari layanan pengesahan, UWear harus memvalidasi tanda tangan terhadap root certificate Confidential Space. UWear harus mendownload root certificate 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
- Membuat sidik jari root certificate 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. Membuat 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 beban kerja 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.serviceAccountUser
yang melampirkan akun layanan ke beban kerja. - Memberikan peran
confidentialcomputing.workloadUser
ke akun layanan beban kerja . Tindakan ini akan memungkinkan akun pengguna membuat token pengesahan. - Memberikan peran
logging.logWriter
ke izin akun layanan beban kerja. Hal ini memungkinkan lingkungan Ruang Rahasia menulis log ke Cloud Logging selain ke Konsol Serial, sehingga log tersedia setelah VM dihentikan.Buat beban kerja
5. Membuat Workload 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 Workload USleep
- Jalankan skrip create_usleep_workload.sh untuk membuat beban kerja USleep. Skrip ini:
- Membuat Artifact Registry (
$USLEEP_ARTIFACT_REPOSITORY
) yang dimiliki oleh UWear tempat beban kerja 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_ACCOUNT
akun layanan untuk 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" di 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 Workload UWear
Tentang Beban Kerja UWear
Workload UWear memiliki 4 bagian utama:
- Bergabung dengan sesi TLS yang sama yang dibuat dalam beban kerja 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 perantara dan akhirnya ke sertifikat root.
- Memeriksa apakah token ditandatangani oleh sertifikat akhir yang terdapat dalam klaim x5c.
- Memeriksa sertifikat root 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
}
- Workload UWear kemudian akan memeriksa apakah klaim pengukuran workload dalam token cocok dengan kondisi atribut yang ditentukan dalam kebijakan OPA. OPA adalah mesin kebijakan open source serbaguna yang menyatukan penerapan kebijakan di seluruh stack. OPA menggunakan dokumen, dengan sintaksis yang mirip dengan JSON, untuk menetapkan nilai dasar pengukuran yang digunakan untuk memvalidasi 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 pengukuran 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, workload UWear juga memvalidasi bahwa nonce cocok dengan nonce yang diharapkan (Exported Keying Material TLS - EKM). 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 lulus, UWear dapat mengonfirmasi bahwa data akan dikirim dan diproses dengan aman. UWear kemudian 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 beban kerja akan dipublikasikan. - Mem-build 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_ACCOUNT
akun layanan untuk 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
Responsnya akan menampilkan STATUS: RUNNING dan EXTERNAL_IP juga akan ditampilkan mirip dengan 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
Menyimpan IP eksternal dalam variabel
export USLEEP_EXTERNAL_IP=<add your external IP>
Memverifikasi bahwa Workload USleep berjalan dengan benar
Untuk memverifikasi bahwa beban kerja USleep berjalan dengan benar, buka halaman Instance VM di project USleep. Klik instance "usleep" dan tekan "Port serial 1(konsol)" di bagian Log. 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
Memverifikasi bahwa beban kerja UWear berjalan dengan benar
Untuk melihat log workload UWear, buka halaman VM Instances di project UWear. Klik instance "uwear" dan tekan "Port serial 1(konsol)" di bagian Log.
Output log setelah instance dimulai sepenuhnya akan terlihat seperti ini
Dalam 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 Instance VM di project USleep. Klik instance "usleep" dan tekan "Port serial 1(konsol)" di bagian Log. 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 berbagi informasi sensitif.
8. (Opsional) Menjalankan Workload yang Tidak Diizinkan
Dalam skenario berikutnya, USleep akan mengupdate kode dan menjalankan beban kerja yang berbeda pada data tidur yang disediakan oleh UWear. UWear belum menyetujui beban kerja baru ini dan belum memperbarui kebijakan OPA mereka untuk mengizinkan ringkasan gambar baru. Kami akan memverifikasi bahwa UWear tidak akan mengirim data sensitifnya ke beban kerja 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
- Dalam 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",
- Membuat workload USleep baru
cd ~/confidential-space/codelabs/health_data_analysis_codelab/scripts
./create_usleep_workload.sh
- Membuat Instance VM USleep baru dan menjalankan 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
- Mengekstrak IP eksternal USleep baru untuk digunakan nanti
export USLEEP_EXTERNAL_IP=<add your external IP>
Menjalankan ulang workload
- Menghapus 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
- Dalam 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 dengan mengikuti petunjuk ini.
Selamat
Selamat, Anda berhasil menyelesaikan codelab.
Anda telah mempelajari cara membagikan data dengan aman sekaligus 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 confidential space
- Menganalisis data rahasia dengan Confidential Space