1. Genel Bakış
Gizli Alan, çok taraflı güvenli veri paylaşımı ve ortak çalışma sağlarken kuruluşların verilerinin gizliliğini korumasına olanak tanır. Bu sayede kuruluşlar, verileri üzerinde kontrol sahibi olmaya ve yetkisiz erişime karşı korumaya devam ederken birbirleriyle ortak çalışabilir.
Gizli Alan, hassas ve genellikle yasal düzenlemelere tabi verileri toplayarak ve analiz ederek karşılıklı değer elde etmek istediğiniz senaryolarda, bu veriler üzerinde tam kontrol sahibi olmanızı sağlar. Gizli Alan sayesinde kuruluşlar, kimliği tanımlayabilecek bilgiler (PII), korunan sağlık bilgileri (PHI), fikri mülkiyet ve kriptografik sırlar gibi hassas verileri toplayıp analiz ederek bu veriler üzerinde tam kontrol sahibi olurken karşılıklı değer elde edebilir.
Gerekenler
- İki ayrı Google Cloud Platform projesi
- Chrome veya Firefox gibi bir tarayıcı
- Google Compute Engine, Gizli Sanal Makine, Container'lar ve uzak depolar, sertifikalar ve sertifika zincirleri hakkında temel bilgi sahibi olmanız gerekir.
- Hizmet Hesapları, Açık Politika Aracısı, Rego ve Ortak Anahtar Altyapısı hakkında temel düzeyde bilgi
Neler öğreneceksiniz?
- Gizli Alan'ı çalıştırmak için gerekli Cloud kaynaklarını yapılandırma
- Gizli Alan görüntüsünü çalıştıran bir Gizli Sanal Makine'de iş yükü çalıştırma
- Korunan kaynaklara erişimi, iş yükü kodunun (ne), Gizli Alan ortamının (nerede) ve iş yükünü çalıştıran hesabın (kim) özelliklerine göre yetkilendirme.
Bu kod laboratuvarı, Confidential Space'in Google Cloud dışında barındırılan korumalı kaynaklarla nasıl kullanılacağına odaklanır. Bir tek seferlik sayı, kitle ve PKI jeton türünü sağlayarak Google Attestation Service'ten nasıl özel, kendi kendine yeten bir jeton isteyeceğinizi öğreneceksiniz.
Bu kod laboratuvarında, uyku kalitenizi hesaplamak için USleep adlı kurgusal bir ürün (kapsayıcı uygulama) ile UWear adlı kurgusal bir ürün (bağlı giyilebilir cihaz) arasında Gizli Alan oluşturursunuz. UWear, korunan sağlık bilgilerini (PHI) USleep ile güvenli ve izole bir ortamda (Güvenilir Yürütme Ortamı veya TEE olarak da bilinir) paylaşır. Bu sayede, verilerin sahipleri tam gizlilik elde eder.
UWear hem iş yükü denetçisi hem de veri sahibidir. İş yükü denetleyicisi olarak,çalıştırılan iş yükündeki kodu inceler ve resim özetini not eder. Veri sahibi olarak UWear, jetonun ve imzasının geçerliliğini kontrol etmek için doğrulama mantığını yazar. Denetlenen iş yükleri görüntü özetini kullanarak, yalnızca belirli bir ortamdaki belirli görüntü özetinin hassas verilere erişmesine izin veren bir doğrulama politikası yazar.
Bu codelab'de USleep, kapsayıcıya alınmış uygulamayı dağıtmaktadır. USleep, hassas verilere erişemez ancak hassas verilere erişmesine izin verilen onaylanmış iş yükünü çalıştırır.
Kod laboratuvarı aşağıdaki adımları içerir:
- 1. adım: Codelab için gerekli bulut kaynaklarını ayarlayın. Projeleri, faturalandırmayı ve izinleri ayarlayın. Codelab kaynak kodunu indirin ve ortam değişkenlerini ayarlayın.
- 2. Adım: Kök sertifikayı indirin ve UWear kaynak kodunuzla birlikte depolayın.
- 3. Adım: USleep ve UWear için iş yükü sanal makinesi tarafından kullanılacak ayrı iş yükü hizmet hesapları oluşturun.
- 4. Adım: Onay jetonu sağlayan USleep iş yükünü oluşturun.
- 5. Adım: Onay jetonunu doğrulayan ve jeton onaylanırsa hassas verileri gönderen UWear iş yükünü oluşturun.
- 6. Adım: USleep ve UWear iş yüklerini çalıştırın. UWear hassas verileri sağlar ve USleep veriler üzerinde bir uyku algoritması çalıştırıp sonuç verir.
- 7. Adım: (İsteğe bağlı) Yetkisiz bir USleep iş yükü çalıştırın ve UWear'dan hassas veri alınmadığını onaylayın.
- 8. adım: Tüm kaynakları temizleyin.
İş akışını anlama
USleep, iş yükünü Gizli Alan'da çalıştırır. İş yükünü çalıştırabilmesi için UWear'ın PHI'sine erişmesi gerekir. USleep iş yükü, erişim elde etmek için önce güvenli bir TLS oturumu oluşturur. Ardından USleep, Google Attestation Service'ten yük içeren bir doğrulama jetonu da ister.
USleep, üç öğe içeren bir JSON yükü içeren bir doğrulama jetonu ister:
- TLS oturumuna bağlı bir doğrulama jetonu. Sertifika jetonunu TLS oturumuna bağlamak için tek seferlik değer, TLS dışa aktarılan anahtarlama malzemesinin karmasıdır. Jetonun TLS oturumuna bağlanması, tek seferlik değeri yalnızca TLS oturumunda yer alan iki tarafın oluşturabileceğinden, arada bir makinenin bulunduğu saldırıların gerçekleşmesini önler.
- "uwear" kitlesinin sağlanacağı UWear, doğrulama jetonunun amaçlanan kitle olduğunu doğrular.
- "PKI" jeton türü. "PKI" jeton türü, USleep'in kendi kendine yeten bir jeton istemek istediği anlamına gelir. Kendine ait jetonun Google tarafından imzalandığı, Confidential Space'in bilinen PKI uç noktasından indirilen kök kullanılarak doğrulanabilir. Bu, imzasının düzenli olarak değişen bir ortak anahtar kullanılarak doğrulandığı varsayılan OIDC jeton türünün aksinedir.
USleep iş yükü, doğrulama jetonunu alır. Ardından UWear, USleep ile TLS bağlantısına katılır ve USleep'in doğrulama jetonunu alır. UWear, x5c iddiasını kök sertifikayla karşılaştırarak jetonu doğrular.
UWear, USleep iş yükünü aşağıdaki durumlarda onaylar:
- Jeton, PKI doğrulama mantığını geçer.
- UWear, x5c iddiasını kök sertifikayla karşılaştırarak, jetonun alt sertifika tarafından imzalandığını ve son olarak indirilen kök sertifikanın x5c iddiasındaki kökle aynı olup olmadığını kontrol ederek jetonu doğrular.
- Jetondaki iş yükü ölçümü iddiaları, OPA politikasında belirtilen özellik koşullarıyla eşleşiyor. OPA, yığın genelinde politika yaptırımını birleştiren açık kaynaklı, genel amaçlı bir politika motorudur. OPA, politikanın doğrulandığı referans değerlerini ayarlamak için JSON'a benzer söz dizimine sahip dokümanlar kullanır. Politikanın hangi değerleri kontrol ettiğine dair bir örnek için OPA referans değerleri bölümüne bakın.
- Tek seferlik sayı, beklenen tek seferlik sayı (TLS Dışa Aktarım Yapılan Anahtarlama Materyali) ile eşleşiyor. Bu durum, yukarıdaki OPA politikasında doğrulanmıştır.
Bu kontrollerin tümü tamamlanıp geçtikten sonra UWear, verilerin güvenli bir şekilde gönderilip işleneceğini onaylayabilir. Ardından UWear, aynı TLS oturumunda hassas PHI ile yanıt verir ve USleep, müşterinin uyku kalitesini hesaplamak için bu verileri kullanabilir.
2. Cloud Kaynakları Oluşturma
Başlamadan önce
- Biri USleep, diğeri UWear için iki Google Cloud projesi oluşturun. Google Cloud projesi oluşturma hakkında daha fazla bilgi için lütfen "İlk Google projenizi oluşturma ve projenizde gezinme" codelab'ine bakın. Proje kimliğinin nasıl alınacağı ve proje adı ile proje numarasından nasıl farklı olduğu hakkında ayrıntılı bilgi edinmek için proje oluşturma ve yönetme başlıklı makaleyi inceleyebilirsiniz.
- Projeleriniz için Faturalandırmayı etkinleştirin.
- Google projenizin Cloud Shell'inde, gerekli proje ortam değişkenlerini aşağıda gösterildiği gibi ayarlayın.
export UWEAR_PROJECT_ID=<Google Cloud project id of UWear>
export USLEEP_PROJECT_ID=<Google Cloud project id of USleep>
- Gizli Bilgi İşleme API'yi ve aşağıdaki API'leri her iki proje için etkinleştirin.
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
- Aşağıdakileri kullanarak birincil tanımlayıcınızı alın:
gcloud auth list
# Output should contain
# ACCOUNT: <Principal Identifier>
# Set your member variable
export MEMBER='user:<Principal Identifier>'
- Bu iki proje için izinler ekleyin. İzinler, IAM rolü verme web sayfası'ndaki ayrıntıları uygulayarak eklenebilir.
$UWEAR_PROJECT_ID
için Artifact Registry Yöneticisi ve Hizmet Hesabı Yöneticisi rolüne sahip olmanız gerekir.
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'
$USLEEP_PROJECT_ID
için Compute Yöneticisi, Depolama Yöneticisi, Artifact Kayıt Defteri Yöneticisi ve Hizmet Hesabı Yöneticisi rolüne sahip olmanız gerekir.
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'
- Google Cloud projelerinizden birinde Cloud Shell'i kullanarak aşağıdaki komutu uygulayarak Confidential Space Codelab GitHub deposunu klonlayın. Bu, bu kod laboratuvarının bir parçası olarak kullanılan gerekli komut dosyalarını almanızı sağlar.
git clone https://github.com/GoogleCloudPlatform/confidential-space.git
- Dizini, sağlık verileri kod laboratuvarının komut dosyaları dizini olarak değiştirin.
cd confidential-space/codelabs/health_data_analysis_codelab/scripts
- codelabs/health_data_analysis_codelab/scripts dizininde bulunan config_env.sh komut dosyasında bu iki satırı güncelleyin. Proje kimliklerini USleep ve UWear için proje kimliklerinizle güncelleyin. Satırın başındaki yorum simgesini ("#") kaldırdığınızdan emin olun.
# TODO: Populate UWear and USleep Project IDs
export UWEAR_PROJECT_ID=your-uwear-project-id
export USLEEP_PROJECT_ID=your-usleep-project-id
- İsteğe bağlı: Mevcut değişkenleri ayarlayın. Bu değişkenleri kullanarak kaynak adlarını geçersiz kılabilirsiniz (ör.
export UWEAR_ARTIFACT_REPOSITORY='my-artifact-repository'
)
- Aşağıdaki değişkenleri mevcut bulut kaynağı adlarıyla ayarlayabilirsiniz. Değişken ayarlanmışsa projedeki ilgili mevcut Cloud kaynağı kullanılır. Değişken ayarlanmazsa bulut kaynağı adı, config_env.sh komut dosyasındaki değerlerden oluşturulur.
- Kalan değişken adlarını, kaynak adları için proje kimliğinize göre değerlere ayarlamak üzere config_env.sh komut dosyasını çalıştırı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. Kök sertifikayı indirme
- Onaylama hizmetinden döndürülen kendi kendine yeten jetonu doğrulamak için UWear'ın imzayı Confidential Space kök sertifikasıyla doğrulaması gerekir. UWear'ın kök sertifikayı indirmesi ve yerel olarak depolaması gerekir. Google Cloud projelerinizin konsolunda aşağıdaki komutları çalıştırın:
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
- İndirilen kök sertifikanın parmak izini oluşturun
openssl x509 -fingerprint -in confidential_space_root.pem -noout
- Parmak izinin aşağıdaki SHA-1 özetiyle eşleştiğini doğrulayın:
B9:51:20:74:2C:24:E3:AA:34:04:2E:1C:3B:A3:AA:D2:8B:21:23:21
4. İş yükü hizmet hesabı oluşturma
Ardından, biri USleep, diğeri UWear iş yükleri için olmak üzere iki hizmet hesabı oluşturacaksınız. USleep ve UWear projelerinde iş yükü hizmet hesapları oluşturmak için create_service_accounts.sh komut dosyasını çalıştırın. İş yüklerini çalıştıran sanal makineler bu hizmet hesaplarını kullanır.
# Navigate to the scripts folder
cd ~/confidential-space/codelabs/health_data_analysis_codelab/scripts
# Run the create_service_accounts script
./create_service_accounts.sh
Komut dosyası:
- Hizmet hesabını iş yüküne ekleyen
iam.serviceAccountUser
rolünü verir. - İş yükü hizmet hesabına
confidentialcomputing.workloadUser
rolü verir . Bu işlem, kullanıcı hesabının bir doğrulama jetonu oluşturmasına olanak tanır. - İş yükü hizmet hesabına
logging.logWriter
rolü izni verir. Bu sayede Gizli Alan ortamı, seri konsola ek olarak Cloud Logging'a günlük yazabilir. Böylece günlükler, sanal makine sonlandırıldıktan sonra kullanılabilir.
5. USleep iş yükü oluşturma
Bu adımda, bu codelab'de kullanılan iş yükleri için Docker görüntüleri oluşturacaksınız. USleep iş yükü, giyilebilir cihazdaki kişisel sağlık bilgilerini kullanarak müşterinin uyku kalitesini belirleyen basit bir Golang uygulamasıdır.
USleep iş yükü hakkında
USleep iş yükü, giyilebilir cihazdaki kişisel sağlık bilgilerini kullanarak müşterinin uyku kalitesini belirleyen basit bir Golang uygulamasıdır. USleep iş yükü üç ana bölümden oluşur:
- TLS oturumu oluşturma ve dışa aktarılan anahtarlama materyalini ayıklama
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
}
- Attestation Hizmeti'nden kitle, tek seferlik sayı ve PKI jeton türü içeren bir jeton isteme.
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
}
- Hassas verileri alma ve kullanıcının uyku kalitesini hesaplama
func handleConnectionRequest(w http.ResponseWriter, r *http.Request) {
...
// Read the sensitive data
_, content, err := conn.ReadMessage()
if err != nil {
fmt.Printf("failed to read message from the connection: %v\n", err)
}
fmt.Printf("Received content from other side, %v\n", string(content))
// TODO: Handle sensitive data
...
}
USleep iş yükünü oluşturma adımları
- USleep iş yükünü oluşturmak için create_usleep_workload.sh komut dosyasını çalıştırın. Bu komut dosyası:
- İş yükünün yayınlanacağı UWear'a ait Artifact Registry (
$USLEEP_ARTIFACT_REPOSITORY
) oluşturur. - usleep/workload.go kodunu oluşturur ve Docker görüntüsünde paketler. USleep için Dockerfile yapılandırmasına bakın.
- Docker görüntüsünü UWear'a ait Artifact Registry'de (
$USLEEP_ARTIFACT_REPOSITORY
) yayınlar. $USLEEP_WORKLOAD_SERVICE_ACCOUNT
hizmet hesabına Artifact Registry ($USLEEP_ARTIFACT_REPOSITORY
) için okuma izni verir.
./create_usleep_workload.sh
- Önemli: Çıkış günlüklerinde USleep için resim özetini çıkarın.
latest: digest: sha256:<USLEEP_IMAGE_DIGEST> size: 945
- UWear dizinine gidin
cd ~/confidential-space/codelabs/health_data_analysis_codelab/src/uwear
- opa_validation_values.json dosyasında "allowed_submods_container_image_digest" altındaki değeri USLEEP_IMAGE_DIGEST ile değiştirin.
# Replace the image digest
sed -i 's/sha256:bc4c32cb2ca046ba07dcd964b07a320b7d0ca88a5cf8e979da15cae68a2103ee/sha256:<USLEEP_IMAGE_DIGEST>/' ~/confidential-space/codelabs/health_data_analysis_codelab/src/uwear/opa_validation_values.json
6. UWear iş yükü oluşturma
UWear iş yükü hakkında
UWear iş yükü 4 ana parçadan oluşur:
- USleep iş yükü içinde oluşturulan TLS oturumuna katılma ve güvenli TLS oturumu üzerinden USleep'ten doğrulama jetonunu alma.
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)
...
}
- Aşağıdaki işlemleri yaparak kendi kendine yeten jetonu doğrulama:
- x5c iddiasını kontrol ederek son varlık sertifikasından ara sertifikaya ve son olarak kök sertifikaya doğru şekilde bağlanan bir sertifika zinciri olup olmadığını kontrol edin.
- Jetonun, x5c iddiasında bulunan son varlık sertifikası tarafından imzalandığını kontrol edin.
- İndirilen / depolatılan kök sertifikanın, x5c iddiasında belirtilen kökle aynı olup olmadığını kontrol edin.
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
}
- Ardından UWear iş yükü, jetondaki iş yükü ölçümü iddialarının OPA politikasında belirtilen özellik koşullarıyla eşleşip eşleşmediğini kontrol eder. OPA, yığın genelinde politika yaptırımını birleştiren açık kaynaklı, genel amaçlı bir politika motorudur. OPA, politikanın doğrulandığı referans değerlerini ayarlamak için JSON'a benzer söz dizimine sahip dokümanlar kullanır.
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
}
- Örnek OPA referans değerleri:
{
"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"
]
}
- Rego ile yazılmış örnek OPA politikası.
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"
}
- Örnek Rego Sorgusu.
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
"
- UWear iş yükü, OPA doğrulaması sırasında tek seferlik sayının beklenen tek seferlik sayıyla (TLS Dışa Aktarım Yapılan Anahtarlama Materyali - EKM) eşleşip eşleşmediğini de doğrular. Tek seferlik şifre, politika değerlendiriciye iletilen EKM kullanılarak OPA politikasında doğrulanır.
EKM karmasını almayla ilgili örnek kod:
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
}
- Bu kontrollerin tümü tamamlanıp geçtikten sonra UWear, verilerin güvenli bir şekilde gönderilip işleneceğini onaylayabilir. Ardından UWear, aynı TLS oturumunda hassas PHI ile yanıt verir ve USleep, müşterinin uyku kalitesini hesaplamak için bu verileri kullanabilir.
func main() {
...
fmt.Println("Validated token and claims. Sending sensitive data")
data, err := readFile(mySensitiveDataFile)
if err != nil {
fmt.Printf("Failed to read data from the file: %v\n", err)
}
conn.WriteMessage(websocket.BinaryMessage, data)
fmt.Println("Sent payload. Closing the connection")
conn.Close()
...
}
USleep iş yükünü oluşturma adımları
- Komut dosyaları dizine gidin
cd ~/confidential-space/codelabs/health_data_analysis_codelab/scripts
- UWear iş yükünü oluşturmak için create_uwear_workload.sh komut dosyasını çalıştırın:
- İş yükünün yayınlanacağı UWear'a ait Artifact Registry (
$UWEAR_ARTIFACT_REPOSITORY
) oluşturur. - uwear/workload.go kodunu oluşturur ve Docker görüntüsünde paketler. USleep için Dockerfile yapılandırmasına bakın.
- Docker görüntüsünü UWear'a ait Artifact Registry'de (
$UWEAR_ARTIFACT_REPOSITORY
) yayınlar. $UWEAR_WORKLOAD_SERVICE_ACCOUNT
hizmet hesabına Artifact Registry ($UWEAR_ARTIFACT_REPOSITORY
) için okuma izni verir.
./create_uwear_workload.sh
7. USleep ve UWear iş yüklerini çalıştırma
USleep iş yükünü çalıştırma
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
Yanıt, STATUS: RUNNING (DURUM: ÇALIŞIYOR) değerini döndürmeli ve EXTERNAL_IP de aşağıdakine benzer şekilde döndürülmelidir:
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
Harici IP'yi bir değişkende depolama
export USLEEP_EXTERNAL_IP=<add your external IP>
USleep iş yükünün doğru şekilde çalıştığını doğrulama
USleep iş yükünün doğru şekilde çalıştığını doğrulamak için USleep projesindeki Sanal Makine Örnekleri sayfasına gidin. "usleep" örneğini tıklayın ve Günlükler bölümünün altındaki "Seri bağlantı noktası 1(konsol)"a basın. Sunucu çalışmaya başladıktan sonra günlüklerin alt kısmında aşağıdakine benzer bir mesaj gösterilir.
2024/09/13 17:00:00 workload task started
#####----- Local IP Address is <YOUR-LOCAL-IP> -----#####
Starting Server..
UWear iş yükünü çalıştırma
gcloud config set project $UWEAR_PROJECT_ID
gcloud compute instances create \
--confidential-compute-type=SEV \
--shielded-secure-boot \
--maintenance-policy=MIGRATE \
--scopes=cloud-platform --zone=${UWEAR_PROJECT_ZONE} \
--image-project=confidential-space-images \
--image-family=confidential-space \
--service-account=${UWEAR_WORKLOAD_SERVICE_ACCOUNT}@${UWEAR_PROJECT_ID}.iam.gserviceaccount.com \
--metadata ^~^tee-image-reference=${UWEAR_PROJECT_REPOSITORY_REGION}-docker.pkg.dev/${UWEAR_PROJECT_ID}/${UWEAR_ARTIFACT_REPOSITORY}/${UWEAR_WORKLOAD_IMAGE_NAME}:${UWEAR_WORKLOAD_IMAGE_TAG}~tee-restart-policy=Never~tee-container-log-redirect=true~tee-env-remote_ip_addr=$USLEEP_EXTERNAL_IP uwear
UWear iş yükünün doğru şekilde çalıştığını doğrulama
UWear iş yükünün günlüklerini görüntülemek için UWear projesindeki Sanal Makine Örnekleri sayfasına gidin. "uwear" örneğini tıklayın ve Günlükler bölümünün altındaki "Seri bağlantı noktası 1(konsol)"a basın.
Örnek tamamen başlatıldıktan sonra günlük çıkışı şu şekilde görünmelidir:
UWear projesinde seri günlükleri aşağıdakine benzer bir şey göstermelidir:
token: eyJ[...]MrXUg
PKI token validated successfully
Performing OPA query evaluation...
OPA policy evaluation completed.
OPA policy result values:
[ hw_verified ]: true
[ image__digest_verified ]: true
[ audience_verified ]: true
[ nonce_verified ]: true
[ issuer_verified ]: true
[ secboot_verified ]: true
[ sw_name_verified ]: true
[ allow ]: true
Policy check PASSED
JWT is authorized.
Validated token and claims. Sending sensitive data
Sent payload. Closing the connection
UWear iş yükünüz bu şekilde görünmüyorsa talimatlar için aşağıdaki notlara bakın.
USleep sonuçlarını görüntüleme
Sonuçları görüntülemek için USleep projesindeki Sanal Makine Örnekleri sayfasına dönün. "usleep" örneğini tıklayın ve Günlükler bölümünün altındaki "Seri bağlantı noktası 1(konsol)"a basın. Günlüklerin alt kısmında iş yükünün sonuçlarını görüntüleyin. Bu veriler aşağıdaki örneğe benzer şekilde görünmelidir.
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
Sonuç "total sleep time is less than 8 hours".
olmalıdır.
Tebrikler. Hassas bilgileri paylaşmak için UWear ile USleep arasında Gizli Alan'ı başarıyla oluşturdunuz.
8. (İsteğe bağlı) Yetkisiz iş yükünü çalıştırma
Bir sonraki senaryoda USleep, kodu günceller ve UWear tarafından sağlanan uyku verilerinde farklı bir iş yükü çalıştırır. UWear bu yeni iş yükünü kabul etmedi ve OPA politikasını yeni resim özetine izin verecek şekilde güncellemedi. UWear'ın hassas verilerini yetkisiz iş yüküne göndermeyeceğini doğrularız.
USleep, iş yükünü değiştirir
- Projeyi $USLEEP_PROJECT_ID olarak ayarlayın.
gcloud config set project $USLEEP_PROJECT_ID
- USleep sanal makine örneğini silin.
gcloud compute instances delete usleep --zone $USLEEP_PROJECT_ZONE
- usleep/workload.go dizinine gidin.
cd ~/confidential-space/codelabs/health_data_analysis_codelab/src/usleep
- usleep/workload.go dosyasında. Satırı güncelleyin
"audience": "uwear".
Bu örnekte, resim özetini değiştirmek için kitleyi UWear'ın onaylamadığı farklı bir değerle güncelleyeceğiz. Bu nedenle UWear, onaylanmamış resim özeti ve yanlış kitle nedeniyle bunu reddetmelidir.
"audience": "anotherCompany.com",
- Yeni USleep iş yükü oluşturma
cd ~/confidential-space/codelabs/health_data_analysis_codelab/scripts
./create_usleep_workload.sh
- Yeni USleep sanal makine örneğini oluşturun ve iş yükünü çalıştırın
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
- Daha sonra kullanmak için yeni USleep harici IP'sini ayıklayın
export USLEEP_EXTERNAL_IP=<add your external IP>
İş yükünü yeniden çalıştırma
- UWear sanal makine örneğini silme
gcloud config set project $UWEAR_PROJECT_ID
gcloud compute instances delete uwear --zone $UWEAR_PROJECT_ZONE
- Yeni harici IP'yi kullanarak UWear sanal makine örneğini yeniden oluşturun
gcloud compute instances create \
--confidential-compute-type=SEV \
--shielded-secure-boot \
--maintenance-policy=MIGRATE \
--scopes=cloud-platform --zone=${UWEAR_PROJECT_ZONE} \
--image-project=confidential-space-images \
--image-family=confidential-space \
--service-account=${UWEAR_WORKLOAD_SERVICE_ACCOUNT}@${UWEAR_PROJECT_ID}.iam.gserviceaccount.com \
--metadata ^~^tee-image-reference=${UWEAR_PROJECT_REPOSITORY_REGION}-docker.pkg.dev/${UWEAR_PROJECT_ID}/${UWEAR_ARTIFACT_REPOSITORY}/${UWEAR_WORKLOAD_IMAGE_NAME}:${UWEAR_WORKLOAD_IMAGE_TAG}~tee-restart-policy=Never~tee-container-log-redirect=true~tee-env-remote_ip_addr=$USLEEP_EXTERNAL_IP uwear
- UWear seri günlüklerinde aşağıdaki mesaj gösterilir ve USleep sanal makinesi herhangi bir hassas veri almaz.
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. Temizleme
Temizleme komut dosyası, bu codelab kapsamında oluşturduğumuz kaynakları temizlemek için kullanılabilir. Bu temizleme kapsamında aşağıdaki kaynaklar silinecek:
- UWear hizmet hesabı (
$UWEAR_SERVICE_ACCOUNT
). - UWear artifact registry (
$UWEAR_ARTIFACT_REPOSITORY
). - UWear Compute örneği
- USleep hizmet hesabı (
$USLEEP_SERVICE_ACCOUNT
). - USleep yapı kayıt defteri (
$USLEEP_ARTIFACT_REPOSITORY
). - USleep Compute Örneği
./cleanup.sh
Keşiflerinizi tamamladıysanız lütfen bu talimatları uygulayarak projenizi silin.
Tebrikler
Tebrikler, kod laboratuvarını başarıyla tamamladınız.
Gizli Alan'ı kullanarak verilerin gizliliğini korurken nasıl güvenli bir şekilde paylaşacağınızı öğrendiniz.
Sırada ne var?
Benzer codelab'lerden bazılarına göz atın...
- Gizli Alan'ı kullanarak makine öğrenimi modellerini ve fikri mülkü koruma
- Çok taraflı hesaplama ve gizli alanlarla dijital öğelerle işlem yapma
- Gizli alanlarla gizli verileri analiz etme