1. Genel Bakış
Gizli GKE (CGKE) düğümleri, iş yüklerindeki verilerin kullanım sırasında şifrelenmesini sağlar. vTPM cihazının CGKE iş yüklerine sunulması, iş yüklerinin vTPM özelliklerini kullanmasına olanak tanır. Bu codelab'de vTPM'nin iki özelliği gösterilmektedir.
- vTPM uzaktan doğrulama, uzaktaki bir tarafın iş yüklerini barındıran CGKE düğümlerinin Gizli Sanal Makineler (CVM) üzerinde çalıştığını doğrulamasını sağlar.
- vTPM yetkilendirmesi ve vTPM mühürleme.

Yukarıdaki şekilde gösterildiği gibi, bu codelab'in ilk bölümünde aşağıdaki adımlar yer almaktadır:
- CGKE düğümleri, vTPM cihazını kurar ve seçili iş yüklerine sunar.
- Bir iş yükü dağıtın ve iş yükünü barındıran CGKE düğümünü uzaktan onaylayın.
- Secret Release Web Server kurulumu.

Yukarıdaki şekilde gösterildiği gibi, bu codelab'in ikinci bölümünde şunlar yer alır:
- CGKE düğümlerinde vTPM yetkilendirme kurulumu ve vTPM mühürleme.
Neler öğreneceksiniz?
- vTPM cihazını CGKE iş yüklerine nasıl sunacağınız.
- CGKE iş yüklerinde Gizli Bilişim API'si (Attestation Verifier hizmeti) aracılığıyla uzaktan doğrulama yapma
- vTPM yetkilendirmesi nasıl ayarlanır ve vTPM mühürleme nasıl yapılır?
Gerekenler
- Google Cloud Platform projesi
- Chrome veya Firefox gibi bir tarayıcı
- Google Compute Engine ( codelab), Gizli Sanal Makine, Gizli GKE düğümleri ve Artifact Registry hakkında temel bilgiler
2. Kurulum ve Şartlar:
Gerekli API'leri etkinleştirmek için bulut konsolunda veya yerel geliştirme ortamınızda aşağıdaki komutu çalıştırın:
gcloud auth login
gcloud services enable \
cloudapis.googleapis.com \
cloudshell.googleapis.com \
container.googleapis.com \
containerregistry.googleapis.com \
confidentialcomputing.googleapis.com \
iamcredentials.googleapis.com \
compute.googleapis.com
3. CGKE düğümlerini ayarlama ve vTPM cihazını seçili iş yüklerine sunma
Bu adım, bu codelab'in ilk bölümünü başlatır. Bu adımda bir CGKE kümesi başlatır ve CVM vTPM cihazını iş yüklerine sunmak için bir cihaz eklentisi uygularsınız. Komutları çalıştırmak için Cloud Console'a veya yerel geliştirme ortamınıza gidin.
1). CGKE iş yüklerinin GCP Confidential Computing API'sini kullanmasına izin vermek için CGKE kümesi oluşturun ve Workload Identity havuzunu kullanın. CGKE iş yüklerinin GCP kaynaklarına erişmesi gerektiğinden Workload Identity Pool gereklidir. CGKE iş yüklerinin GCP kaynaklarına erişebilmesi için kimliklerinin olması gerekir.
gcloud container clusters create cgke-attestation-codelab \
--machine-type=n2d-standard-2 \
--enable-confidential-nodes \
--zone us-central1-c \
--workload-pool=${PROJECT_ID}.svc.id.goog \
--workload-metadata=GKE_METADATA
Aşağıdakini değiştirin:
project-id, projenin benzersiz tanımlayıcısıdır.
2). CGKE kümesinin vTPM cihazını iş yüklerine sunmasına izin vermek için cihaz eklentisini başlatın. Yeni bir kaynak (google.com/cc) oluşturmak için kubernetes cihaz eklentisi kullanırız. Yeni kaynakla ilişkili tüm iş yükleri, çalışan düğümündeki vTPM cihazını görebilir.
gcloud container clusters get-credentials cgke-attestation-codelab --zone us-central1-c --project ${PROJECT_ID}
kubectl create -f https://raw.githubusercontent.com/google/cc-device-plugin/main/manifests/cc-device-plugin.yaml
Aşağıdakini değiştirin:
project-id, projenin benzersiz tanımlayıcısıdır.
Aşağıdaki komut, dağıtılan cc-device-plugin'i görmenizi sağlar.
kubectl get pods -A | grep "cc-device-plugin"
Not: Karma mod GKE kümesi (hem Confidential hem de Confidential olmayan GKE çalışma düğümleriyle) durumunda, operatörün cc-device-plugin'i yalnızca Confidential GKE çalışma düğümlerine dağıtması önerilir.
(İsteğe bağlı). CGKE kapsülünün Prometheus izlemesini uygulayın. İzlemeyi etkinleştirdiğinizde cihaz eklentisinin durumunu gözlemleyebilirsiniz.
kubectl apply -f https://raw.githubusercontent.com/google/cc-device-plugin/main/manifests/cc-device-plugin-pod-monitoring.yaml
https://console.cloud.google.com/monitoring/metrics-explorer adresine gidin ve cc-device-plugin metriklerini bulun veya PROMQL'yi kullanın. Örneğin, aşağıdaki PROMQL komutu her cc-device-plugin işlemi için CPU saniyelerini gösterir.
rate(process_cpu_seconds_total[${__interval}])
4. İş yükü dağıtma ve iş yükünde uzaktan onay gerçekleştirme
Bu adımda, önceki adımda oluşturduğunuz CGKE kümesinde bir iş yükü oluşturup dağıtacak ve çalışma düğümünde bir onay jetonu (OIDC jetonu) almak için vTPM uzaktan onaylama işlemi gerçekleştireceksiniz.
1). Uygulama container görüntüsünü oluşturun ve Artifact Registry'ye aktarın. Uygulama kapsayıcı görüntüsü, onay kanıtı toplayıp onay jetonu (OIDC jetonu) için Onay Doğrulayıcı hizmetine gönderebilen go-tpm aracını içerir.
- Uygulama container görüntüsü için Dockerfile'ı oluşturun.
Dockerfile
FROM golang:1.21.0 as builder
WORKDIR /
RUN git clone https://github.com/google/go-tpm-tools.git
WORKDIR /go-tpm-tools/cmd/gotpm
RUN CGO_ENABLED=0 GOOS=linux go build -o /gotpm
FROM debian:trixie
WORKDIR /
RUN apt-get update -y
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y ca-certificates
RUN rm -rf /etc/apt/sources.list.d
COPY --from=builder /gotpm /gotpm
CMD ["tail", "-f", "/dev/null"]
- Artifact Registry oluşturun.
gcloud artifacts repositories create codelab-repo \
--repository-format=docker \
--location=us
- Uygulama container görüntüsünü Artifact Registry'ye aktarın.
docker build -t us-docker.pkg.dev/${PROJECT_ID}/codelab-repo/go-tpm:latest .
docker push us-docker.pkg.dev/${PROJECT_ID}/codelab-repo/go-tpm:latest
2). GCP kaynaklarında bir GCP hizmet hesabının izinlerini devralmak için bir Kubernetes hizmet hesabı oluşturun.
- Kubernetes hizmet hesabı oluşturun
codelab-ksa.
kubectl create serviceaccount codelab-ksa \
--namespace default
- Bir rol oluşturun
Confidential_Computing_Workload_Userve role, Confidential Computing API'lerine erişim izni verin.
gcloud iam roles create Confidential_Computing_Workload_User --project=<project-id> \
--title="CGKE Workload User" --description="Grants the ability to generate an attestation token in a GKE workload." \
--permissions="confidentialcomputing.challenges.create,confidentialcomputing.challenges.verify,confidentialcomputing.locations.get,confidentialcomputing.locations.list" --stage=GA
Aşağıdakini değiştirin:
project-id, projenin benzersiz tanımlayıcısıdır.
- Bir GCP hizmet hesabı oluşturun
codelab-csave bunu, Gizli Bilişim API'lerine erişme izni olanConfidential_Computing_Workload_User. So that codelab-csarolüyle bağlayın.
gcloud iam service-accounts create codelab-csa \
--project=<project-id>
gcloud projects add-iam-policy-binding <project-id> \
--member "serviceAccount:codelab-csa@<project-id>.iam.gserviceaccount.com" \
--role "projects/<project-id>/roles/Confidential_Computing_Workload_User"
gcloud iam service-accounts add-iam-policy-binding codelab-csa@<project-id>.iam.gserviceaccount.com \
--role roles/iam.workloadIdentityUser \
--member "serviceAccount:<project-id>.svc.id.goog[default/codelab-ksa]"
Aşağıdakini değiştirin:
project-id, projenin benzersiz tanımlayıcısıdır.
- Kubernetes hizmet hesabını
codelab-ksaGCP hizmet hesabıylacodelab-csabağlayın.codelab-ksa, Gizli Bilgi İşlem API'lerine erişmek için gerekli izinlere sahip olmalıdır.
kubectl annotate serviceaccount codelab-ksa \
--namespace default \
iam.gke.io/gcp-service-account=codelab-csa@<project-id>.iam.gserviceaccount.com
Aşağıdakini değiştirin:
project-id, projenin benzersiz tanımlayıcısıdır.
3). Demo uygulaması için uygulama dağıtımı yaml'sini oluşturun. Kubernetes hizmet hesabını codelab-ksa seçili iş yüklerine atayın.
deploy.yaml
apiVersion: v1
kind: Pod
metadata:
name: go-tpm-demo
labels:
app.kubernetes.io/name: go-tpm-demo
spec:
serviceAccountName: codelab-ksa
nodeSelector:
iam.gke.io/gke-metadata-server-enabled: "true"
containers:
- name: go-tpm
image: us-docker.pkg.dev/<project-id>/codelab-repo/go-tpm:latest
resources:
limits:
google.com/cc: 1
Aşağıdakini değiştirin:
project-id, projenin benzersiz tanımlayıcısıdır.
4). Dağıtımı CGKE kümesine uygulayın.
kubectl create -f deploy.yaml
5). İş yüküne bağlanın ve doğrulama jetonu (OIDC jetonu) almak için uzaktan doğrulamayı başlatın.
kubectl exec -it go-tpm-demo -- /bin/bash ./gotpm token --event-log=/run/cc-device-plugin/binary_bios_measurements > attestation_token
Hak taleplerini görüntülemek için onay jetonunun kodunu jwt.io adresinde çözebilirsiniz.
5. Secret Release Web Sunucusu'nu ayarlama
Bu adımda, önceki SSH oturumundan çıkıp başka bir sanal makine oluşturacaksınız. Bu sanal makinede, gizli sürüm web sunucusu ayarlarsınız. Web sunucusu, alınan onay jetonunu ve iddialarını doğrular. Doğrulamalar başarılı olursa gizli bilgi, istekte bulunana iletilir.
1). Bulut konsoluna veya yerel geliştirme ortamınıza gidin. Sanal makine oluşturun.
gcloud config set project <project-id>
gcloud compute instances create cgke-attestation-codelab-web-server \
--machine-type=n2d-standard-2 \
--zone=us-central1-c \
--image=ubuntu-2204-jammy-v20240228 \
--image-project=ubuntu-os-cloud
Aşağıdakini değiştirin:
project-id, projenin benzersiz tanımlayıcısıdır.
2). Yeni sanal makinenize SSH ile bağlanın.
gcloud compute ssh --zone us-central1-c cgke-attestation-codelab-web-server
3). Go ortamını ayarlayın.
wget https://go.dev/dl/go1.22.0.linux-amd64.tar.gz sudo tar -C /usr/local -xzf go1.22.0.linux-amd64.tar.gz export PATH=$PATH:/usr/local/go/bin
4). Gizli sürüm web sunucusunun kaynak kodunu depolayan aşağıdaki iki dosyayı oluşturun (nano ile kopyalayıp yapıştırın).
main.go
package main
import (
"fmt"
"net/http"
"strings"
"time"
"log"
"github.com/golang-jwt/jwt/v4"
)
const (
theSecret = "This is the super secret information!"
)
func homePage(w http.ResponseWriter, r *http.Request) {
tokenString := r.Header.Get("Authorization")
if tokenString != "" {
tokenString, err := extractToken(tokenString)
if err != nil {
http.Error(w, err.Error(), http.StatusUnauthorized)
}
tokenBytes := []byte(tokenString)
// A method to return a public key from the well-known endpoint
keyFunc := getRSAPublicKeyFromJWKsFile
token, err := decodeAndValidateToken(tokenBytes, keyFunc)
if err != nil {
http.Error(w, "Invalid JWT Token", http.StatusUnauthorized)
}
if ok, err := isValid(token.Claims.(jwt.MapClaims)); ok {
fmt.Fprintln(w, theSecret)
} else {
if err != nil {
http.Error(w, "Error validating JWT claims: "+err.Error(), http.StatusUnauthorized)
} else {
http.Error(w, "Invalid JWT token Claims", http.StatusUnauthorized)
}
}
} else {
http.Error(w, "Authorization token required", http.StatusUnauthorized)
}
}
func extractToken(tokenString string) (string, error) {
if strings.HasPrefix(tokenString, "Bearer ") {
return strings.TrimPrefix(tokenString, "Bearer "), nil
}
return "", fmt.Errorf("invalid token format")
}
func isValid(claims jwt.MapClaims) (bool, error) {
// 1. Evaluating Standard Claims:
subject, ok := claims["sub"].(string)
if !ok {
return false, fmt.Errorf("missing or invalid 'sub' claim")
}
fmt.Println("Subject:", subject)
// e.g. "sub":"https://www.googleapis.com/compute/v1/projects/<project_id>/zones/<project_zone>/instances/<instance_name>"
issuedAt, ok := claims["iat"].(float64)
if !ok {
return false, fmt.Errorf("missing or invalid 'iat' claim")
}
fmt.Println("Issued At:", time.Unix(int64(issuedAt), 0))
// 2. Evaluating Remote Attestation Claims:
hwModel, ok := claims["hwmodel"].(string)
if !ok || hwModel != "GCP_AMD_SEV" {
return false, fmt.Errorf("missing or invalid 'hwModel'")
}
fmt.Println("hwmodel:", hwModel)
swName, ok := claims["swname"].(string)
if !ok || swName != "GCE" {
return false, fmt.Errorf("missing or invalid 'hwModel'")
}
fmt.Println("swname:", swName)
return true, nil
}
func main() {
http.HandleFunc("/", homePage)
fmt.Println("Server listening on :8080")
err := http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatalf("Server failed to start: %v", err)
}
}
helper.go
package main
import (
"crypto/rsa"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io"
"math/big"
"net/http"
"github.com/golang-jwt/jwt/v4"
)
const (
socketPath = "/run/container_launcher/teeserver.sock"
expectedIssuer = "https://confidentialcomputing.googleapis.com"
wellKnownPath = "/.well-known/openid-configuration"
)
type jwksFile struct {
Keys []jwk `json:"keys"`
}
type jwk struct {
N string `json:"n"` // "nMMTBwJ7H6Id8zUCZd-L7uoNyz9b7lvoyse9izD9l2rtOhWLWbiG-7pKeYJyHeEpilHP4KdQMfUo8JCwhd-OMW0be_XtEu3jXEFjuq2YnPSPFk326eTfENtUc6qJohyMnfKkcOcY_kTE11jM81-fsqtBKjO_KiSkcmAO4wJJb8pHOjue3JCP09ZANL1uN4TuxbM2ibcyf25ODt3WQn54SRQTV0wn098Y5VDU-dzyeKYBNfL14iP0LiXBRfHd4YtEaGV9SBUuVhXdhx1eF0efztCNNz0GSLS2AEPLQduVuFoUImP4s51YdO9TPeeQ3hI8aGpOdC0syxmZ7LsL0rHE1Q",
E string `json:"e"` // "AQAB" or 65537 as an int
Kid string `json:"kid"` // "1f12fa916c3a0ef585894b4b420ad17dc9d6cdf5",
// Unused fields:
// Alg string `json:"alg"` // "RS256",
// Kty string `json:"kty"` // "RSA",
// Use string `json:"use"` // "sig",
}
type wellKnown struct {
JwksURI string `json:"jwks_uri"` // "https://www.googleapis.com/service_accounts/v1/metadata/jwk/signer@confidentialspace-sign.iam.gserviceaccount.com"
// Unused fields:
// Iss string `json:"issuer"` // "https://confidentialcomputing.googleapis.com"
// Subject_types_supported string `json:"subject_types_supported"` // [ "public" ]
// Response_types_supported string `json:"response_types_supported"` // [ "id_token" ]
// Claims_supported string `json:"claims_supported"` // [ "sub", "aud", "exp", "iat", "iss", "jti", "nbf", "dbgstat", "eat_nonce", "google_service_accounts", "hwmodel", "oemid", "secboot", "submods", "swname", "swversion" ]
// Id_token_signing_alg_values_supported string `json:"id_token_signing_alg_values_supported"` // [ "RS256" ]
// Scopes_supported string `json:"scopes_supported"` // [ "openid" ]
}
func getWellKnownFile() (wellKnown, error) {
httpClient := http.Client{}
resp, err := httpClient.Get(expectedIssuer + wellKnownPath)
if err != nil {
return wellKnown{}, fmt.Errorf("failed to get raw .well-known response: %w", err)
}
wellKnownJSON, err := io.ReadAll(resp.Body)
if err != nil {
return wellKnown{}, fmt.Errorf("failed to read .well-known response: %w", err)
}
wk := wellKnown{}
json.Unmarshal(wellKnownJSON, &wk)
return wk, nil
}
func getJWKFile() (jwksFile, error) {
wk, err := getWellKnownFile()
if err != nil {
return jwksFile{}, fmt.Errorf("failed to get .well-known json: %w", err)
}
// Get JWK URI from .wellknown
uri := wk.JwksURI
fmt.Printf("jwks URI: %v\n", uri)
httpClient := http.Client{}
resp, err := httpClient.Get(uri)
if err != nil {
return jwksFile{}, fmt.Errorf("failed to get raw JWK response: %w", err)
}
jwkbytes, err := io.ReadAll(resp.Body)
if err != nil {
return jwksFile{}, fmt.Errorf("failed to read JWK body: %w", err)
}
file := jwksFile{}
err = json.Unmarshal(jwkbytes, &file)
if err != nil {
return jwksFile{}, fmt.Errorf("failed to unmarshall JWK content: %w", err)
}
return file, nil
}
// N and E are 'base64urlUInt' encoded: https://www.rfc-editor.org/rfc/rfc7518#section-6.3
func base64urlUIntDecode(s string) (*big.Int, error) {
b, err := base64.RawURLEncoding.DecodeString(s)
if err != nil {
return nil, err
}
z := new(big.Int)
z.SetBytes(b)
return z, nil
}
func getRSAPublicKeyFromJWKsFile(t *jwt.Token) (any, error) {
keysfile, err := getJWKFile()
if err != nil {
return nil, fmt.Errorf("failed to fetch the JWK file: %w", err)
}
// Multiple keys are present in this endpoint to allow for key rotation.
// This method finds the key that was used for signing to pass to the validator.
kid := t.Header["kid"]
for _, key := range keysfile.Keys {
if key.Kid != kid {
continue // Select the key used for signing
}
n, err := base64urlUIntDecode(key.N)
if err != nil {
return nil, fmt.Errorf("failed to decode key.N %w", err)
}
e, err := base64urlUIntDecode(key.E)
if err != nil {
return nil, fmt.Errorf("failed to decode key.E %w", err)
}
// The parser expects an rsa.PublicKey: https://github.com/golang-jwt/jwt/blob/main/rsa.go#L53
// or an array of keys. We chose to show passing a single key in this example as its possible
// not all validators accept multiple keys for validation.
return &rsa.PublicKey{
N: n,
E: int(e.Int64()),
}, nil
}
return nil, fmt.Errorf("failed to find key with kid '%v' from well-known endpoint", kid)
}
func decodeAndValidateToken(tokenBytes []byte, keyFunc func(t *jwt.Token) (any, error)) (*jwt.Token, error) {
var err error
fmt.Println("Unmarshalling token and checking its validity...")
token, err := jwt.NewParser().Parse(string(tokenBytes), keyFunc)
fmt.Printf("Token valid: %v\n", token.Valid)
if token.Valid {
return token, nil
}
if ve, ok := err.(*jwt.ValidationError); ok {
if ve.Errors&jwt.ValidationErrorMalformed != 0 {
return nil, fmt.Errorf("token format invalid. Please contact the Confidential Space team for assistance")
}
if ve.Errors&(jwt.ValidationErrorNotValidYet) != 0 {
// If device time is not synchronized with the Attestation Service you may need to account for that here.
return nil, errors.New("token is not active yet")
}
if ve.Errors&(jwt.ValidationErrorExpired) != 0 {
return nil, fmt.Errorf("token is expired")
}
return nil, fmt.Errorf("unknown validation error: %v", err)
}
return nil, fmt.Errorf("couldn't handle this token or couldn't read a validation error: %v", err)
}
5). Web sunucusunu oluşturup çalıştırmak için aşağıdaki komutları çalıştırın. Bu işlem, :8080 bağlantı noktasında gizli anahtar yayınlama web sunucusunu başlatır.
go mod init google.com/codelab go mod tidy go get github.com/golang-jwt/jwt/v4 go build ./codelab
Sorun giderme: go mod tidy: çalıştırıldığında göz ardı edilebilecek aşağıdaki uyarıyı görebilirsiniz.
go: finding module for package github.com/golang-jwt/jwt/v4 go: downloading github.com/golang-jwt/jwt v3.2.2+incompatible go: downloading github.com/golang-jwt/jwt/v4 v4.5.0 go: found github.com/golang-jwt/jwt/v4 in github.com/golang-jwt/jwt/v4 v4.5.0 go: google.com/codelab/go/pkg/mod/github.com/golang-jwt/jwt@v3.2.2+incompatible: import path "google.com/codelab/go/pkg/mod/github.com/golang-jwt/jwt@v3.2.2+incompatible" should not have @version go: google.com/codelab/go/pkg/mod/github.com/golang-jwt/jwt@v3.2.2+incompatible/cmd/jwt: import path "google.com/codelab/go/pkg/mod/github.com/golang-jwt/jwt@v3.2.2+incompatible/cmd/jwt" should not have @version go: google.com/codelab/go/pkg/mod/github.com/golang-jwt/jwt@v3.2.2+incompatible/request: import path "google.com/codelab/go/pkg/mod/github.com/golang-jwt/jwt@v3.2.2+incompatible/request" should not have @version go: google.com/codelab/go/pkg/mod/github.com/golang-jwt/jwt@v3.2.2+incompatible/test: import path "google.com/codelab/go/pkg/mod/github.com/golang-jwt/jwt@v3.2.2+incompatible/test" should not have @version
6). Başka bir Cloud Console sekmesi veya yerel geliştirme ortamı oturumu başlatıp aşağıdaki komutu çalıştırın. Bu işlem, cgke-attestation-codelab-web-server-internal-ip almanızı sağlar.
gcloud compute instances describe cgke-attestation-codelab-web-server --format='get(networkInterfaces[0].networkIP)' --zone=us-central1-c
7). CGKE iş yükünüze bağlanın ve bir doğrulama jetonu (OIDC jetonu) getirmek için uzaktan doğrulamayı başlatın. Ardından, attestation-token ve cgke-attestation-codelab-web-server-internal-ip içeriğini aşağıdaki komuta yerleştirin. Bu işlem, gizli sürüm web sunucusunda tutulan gizli anahtarı getirir.
kubectl exec -it go-tpm-demo -- /bin/bash ./gotpm token --event-log=/run/cc-device-plugin/binary_bios_measurements > attestation_token curl http://<cgke-attestation-codelab-web-server-internal-ip>:8080 -H "Authorization: Bearer $(cat ./attestation_token)"
Aşağıdakini değiştirin:
cgke-attestation-codelab-web-server-internal-ip,cgke-attestation-codelab-web-serversanal makine örneğinin dahili IP'sidir.
6. CGKE düğümlerinde vTPM mühürleme
Bu adım, bu codelab'in ikinci bölümünü başlatır. Bu adımda, CGKE düğümlerinde vTPM sahibi yetkilendirmesini ayarlayacak ve vTPM sahibi parola ifadesiyle bir iş yükü dağıtacaksınız. Ardından, iş yükündeki verileri vTPM mühürleme özelliğiyle mühürlemek ve mühürlerini kaldırmak için bir vTPM birincil anahtarı oluşturursunuz.
1). CGKE düğümlerinde vTPM sahibi yetkilendirmesini ayarlayın.
- Tek seferlik iş kapsayıcı görüntüsü oluşturun. Tek seferlik iş, tüm vTPM'ler için sahip şifresini ayarlar. Aşağıda, kapsayıcı görüntüsünü oluşturmak için kullanılan Dockerfile verilmiştir.
Dockerfile
FROM debian:latest
RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections
RUN apt-get update
RUN apt -y install \
autoconf-archive \
libcmocka0 \
libcmocka-dev \
net-tools \
build-essential \
git \
pkg-config \
gcc \
g++ \
m4 \
libtool \
automake \
libgcrypt20-dev \
libssl-dev \
uthash-dev \
autoconf \
uuid-dev \
libcurl4-openssl-dev \
libjson-c-dev
RUN mkdir /src
WORKDIR /src
RUN git clone https://github.com/tpm2-software/tpm2-tss
WORKDIR /src/tpm2-tss
RUN ./bootstrap
RUN ./configure --prefix=/usr/local
RUN make all install
WORKDIR /src
RUN git clone https://github.com/tpm2-software/tpm2-tools
WORKDIR /src/tpm2-tools
RUN apt-get -y install libcurl4 libcurl4-openssl-dev pandoc man-db
RUN ./bootstrap
RUN ./configure --prefix=/usr/local
RUN make all install
RUN apt-get -y install vim
ENTRYPOINT ["/bin/bash"]
- Tek seferlik iş container görüntüsünü oluşturup Artifact Registry'ye aktarın.
docker build -t us-docker.pkg.dev/<project-id>/codelab-repo/tpm-tools:latest . docker push us-docker.pkg.dev/<project-id>/codelab-repo/tpm-tools:latest
Aşağıdakini değiştirin:
project-id, projenin benzersiz tanımlayıcısıdır.
- Tek seferlik işi bir Kubernetes işi aracılığıyla yürütün. (UYARI: Bu iş, her CVM'de vTPM'yi temizler. CVM'niz diski şifrelemek için vTPM kullanıyorsa bu iş, yeniden başlatma işleminden sonra CVM'nizi kullanılamaz hale getirir. Diskinizde FSTYPE
crypto_LUKSolup olmadığınılsblk -fkomutuyla kontrol edebilirsiniz.
tpm-tools-task.yaml
apiVersion: batch/v1
kind: Job
metadata:
name: tpm-tools-task
spec:
template:
spec:
containers:
- name: tpm-tools
image: us-docker.pkg.dev/<project-id>/codelab-repo/tpm-tools:latest
command: ["/bin/sh", "-c"]
args: ["tpm2_clear; tpm2_changeauth -c owner this_is_passphrase"]
resources:
limits:
google.com/cc: 1
restartPolicy: Never
Aşağıdakini değiştirin:
project-id, projenin benzersiz tanımlayıcısıdır.
- Tek seferlik işi başlatın. Bu iş, tüm çalışan düğümlerinde vTPM sahibi geçiş ifadesini ayarlar.
kubectl create -f tpm-tools-task.yaml
2). vTPM sahibi parolasını tutmak için bir Kubernetes gizlisi oluşturun.
kubectl create secret generic tpm-secret --from-literal=passphrase='this_is_passphrase'
3). Bir demo uygulama kapsayıcısı oluşturun ve parolayı bu kapsayıcıya iletin. Demo uygulama kapsayıcısı, vTPM ile etkileşim kurmak için tpm2 araçlarını içerir.
- Demo uygulama container'ı için dağıtım yaml dosyasını oluşturun.
deploy_demo.yaml
apiVersion: v1
kind: Pod
metadata:
name: tpm-tools-demo
labels:
app.kubernetes.io/name: tpm-tools-demo
spec:
containers:
- name: tpm-tools
image: us-docker.pkg.dev/<project-id>/codelab-repo/tpm-tools:latest
command: ["tail", "-f", "/dev/null"]
resources:
limits:
google.com/cc: 1
volumeMounts:
- name: secret-volume
mountPath: "/etc/tpmsecret"
readOnly: true
volumes:
- name: secret-volume
secret:
secretName: tpm-secret
Aşağıdakini değiştirin:
project-id, projenin benzersiz tanımlayıcısıdır.
- Demo uygulamasını dağıtın.
kubectl create -f deploy_demo.yaml
4). Demo uygulama container'ında vTPM mühürleme işlemini gerçekleştirin.
- Demo uygulama kapsayıcısına bağlanın ve parola ile birincil anahtar ayarlayın.
kubectl exec -it tpm-tools-demo -- /bin/bash tpm2_createprimary -C o -c primary.ctx -P $(cat /etc/tpmsecret/passphrase)
tpm2_createprimary, belirtilen hiyerarşi ve şablona göre birincil nesneyi oluşturmak için vTPM ile etkileşime girer.
- -C o: Birincil anahtarın TPM'nin sahibi hiyerarşisi altında oluşturulacağını gösterir.
- -c primary.ctx: Oluşturulan birincil nesnenin bağlamını (tanıtıcı ve ilişkili veriler) primary.ctx dosyasına kaydeder. Bu bağlam, sonraki işlemler için gereklidir.
İş yükü, birincil anahtar oluşturmak için yanlış sahip parolasını kullanamaz.
tpm2_createprimary -C o -P wrong_passphrase
Komut aşağıdaki hataları döndürüyor:
WARNING:esys:src/tss2-esys/api/Esys_CreatePrimary.c:401:Esys_CreatePrimary_Finish() Received TPM Error ERROR:esys:src/tss2-esys/api/Esys_CreatePrimary.c:135:Esys_CreatePrimary() Esys Finish ErrorCode (0x000009a2) ERROR: Esys_CreatePrimary(0x9A2) - tpm:session(1):authorization failure without DA implications ERROR: Unable to run tpm2_createprimary
- Oluşturulan birincil anahtar, verileri mühürlemek ve mühürlerini açmak için kullanılabilir.
echo "This is my secret message" > secret.txt tpm2_create -C primary.ctx -u sealed.pub -r sealed.priv -i secret.txt tpm2_load -C primary.ctx -u sealed.pub -r sealed.priv -c sealed.ctx tpm2_unseal -c sealed.ctx -o unsealed.txt
tpm2_create, istenen şifreleme nesnesini oluşturmak için vTPM ile etkileşime girer.
- -C primary.ctx: Daha önce oluşturduğumuz birincil anahtar bağlamını kullanır.
- -u sealed.pub: sealed.pub dosyasında, mühürleme anahtarının ortak kısmını (mühürün açılması için gereklidir) saklar.
- -r sealed.priv: Sızdırmazlık anahtarının özel bölümünü sealed.priv dosyasına kaydeder.
- -i secret.txt: Mühürlenecek sırrı içeren dosya.
tpm2_load: Mühürleme anahtarını genel ve özel bölümleri (sealed.pub, sealed.priv) kullanarak TPM'ye yükler ve bağlamını sealed.ctx dosyasına kaydeder.
tpm2_unseal: Daha önce vTPM mühürleme nesnesi kullanılarak şifrelenmiş (mühürlenmiş) verilerin şifresini çözme (mühürünü açma)
primary.ctx ve sealed.priv dosyalarının yalnızca tek bir vTPM cihazda kullanılabileceğini unutmayın. vTPM cihazına ve bu dosyalara erişimi olan herkes de mühürlenmiş verilere erişebilir. Verileri mühürlemek için PCR değerleriyle ilgili politikayı da kullanabilirsiniz ancak bu codelab'in kapsamı dışındadır.
7. Temizleme
Cloud Console'da veya yerel geliştirme ortamınızda aşağıdaki komutları çalıştırın:
gcloud config set project <project-id> # Delete the CGKE cluster gcloud container clusters delete cgke-attestation-codelab --zone us-central1-c # Delete the Artifact Registry gcloud artifacts repositories delete codelab-repo --location=us # Delete the web server VM instance gcloud compute instances delete cgke-attestation-codelab-web-server --zone=us-central1-c # Delete the GCP service account gcloud iam service-accounts delete codelab-csa@<project-id>.iam.gserviceaccount.com # Delete the role gcloud iam roles delete Confidential_Computing_Workload_User
Aşağıdakini değiştirin:
project-id, projenin benzersiz tanımlayıcısıdır.
8. Sırada ne var?
Gizli GKE düğümleri hakkında daha fazla bilgi edinin.