1. Visão geral
Os nós do GKE confidencial (CGKE) garantem que os dados nas cargas de trabalho sejam criptografados em uso. Ao expor o dispositivo vTPM às cargas de trabalho do CGKE, elas podem usar os recursos do vTPM. Neste codelab, você vai conhecer dois recursos do vTPM.
- O atestado remoto do vTPM permite que uma parte remota verifique se os nós do CGKE que hospedam cargas de trabalho estão sendo executados em VMs confidenciais (CVMs).
- Autorização e bloqueio do vTPM.

Conforme mostrado na figura acima, a primeira parte deste codelab inclui as seguintes etapas:
- Os nós do CGKE configuram e expõem o dispositivo vTPM a cargas de trabalho selecionadas.
- Implante uma carga de trabalho e faça o atestado remoto do nó do CGKE que a hospeda.
- Configuração do servidor da Web de lançamento secreto.

Conforme mostrado na figura acima, a segunda parte deste codelab inclui:
- Configuração de autorização e lacre do vTPM nos nós do CGKE.
O que você vai aprender
- Como expor o dispositivo vTPM a cargas de trabalho do CGKE.
- Como fazer a atestação remota usando a API Computação confidencial (serviço de verificação de atestado) em cargas de trabalho do CGKE.
- Como configurar a autorização do vTPM e realizar o lacre do vTPM.
O que é necessário
- Um projeto do Google Cloud Platform
- Um navegador, como o Chrome ou o Firefox
- Conhecimento básico do Google Compute Engine ( codelab), VM confidencial, nós confidenciais do GKE e Artifact Registry
2. Configuração e requisitos:
Para ativar as APIs necessárias, execute o seguinte comando no console do Cloud ou no ambiente de desenvolvimento local:
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. Configurar nós do CGKE e expor o dispositivo vTPM a cargas de trabalho selecionadas
Esta etapa inicia a primeira parte deste codelab. Nesta etapa, você inicia um cluster do CGKE e aplica um plug-in de dispositivo para expor o dispositivo vTPM da CVM às cargas de trabalho. Acesse o console do Cloud ou seu ambiente de desenvolvimento local para executar os comandos.
1) Crie um cluster do CGKE e use um pool de identidade da carga de trabalho para permitir que as cargas de trabalho do CGKE usem a API de computação confidencial do GCP. O pool de identidade da carga de trabalho é necessário porque as cargas de trabalho do CGKE precisam acessar recursos do GCP. Para acessar os recursos do GCP, as cargas de trabalho do CGKE precisam ter uma identidade.
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
Substitua:
project-idé o identificador exclusivo do projeto.
2) Inicie o plug-in de dispositivo para permitir que o cluster do CGKE exponha o dispositivo vTPM às cargas de trabalho. Usamos um plug-in de dispositivo do Kubernetes para criar um novo recurso, o google.com/cc. Qualquer carga de trabalho associada ao novo recurso poderá ver o dispositivo vTPM no nó de trabalho.
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
Substitua:
project-idé o identificador exclusivo do projeto.
O comando a seguir permite ver o cc-device-plugin implantado.
kubectl get pods -A | grep "cc-device-plugin"
Observação: em caso de um cluster do GKE de modo misto (com nós de trabalho confidenciais e não confidenciais do GKE), é recomendável que o operador implante o cc-device-plugin apenas nos nós de trabalho do GKE Confidencial.
(Opcional). Aplique o monitoramento do Prometheus do pod do CGKE. Ao ativar o monitoramento, você pode observar o status do plug-in do dispositivo.
kubectl apply -f https://raw.githubusercontent.com/google/cc-device-plugin/main/manifests/cc-device-plugin-pod-monitoring.yaml
Acesse https://console.cloud.google.com/monitoring/metrics-explorer e encontre as métricas cc-device-plugin ou use o PROMQL. Por exemplo, o comando PROMQL a seguir mostra os segundos de CPU para cada processo cc-device-plugin.
rate(process_cpu_seconds_total[${__interval}])
4. Implantar uma carga de trabalho e realizar um atestado remoto nela
Nesta etapa, você vai criar e implantar uma carga de trabalho no cluster do CGKE criado na etapa anterior e realizar uma atestação remota do vTPM para recuperar um token de atestação (token OIDC) no nó de trabalho.
1) Crie a imagem do contêiner do aplicativo e envie-a para o Artifact Registry. A imagem do contêiner do aplicativo contém a ferramenta go-tpm, que pode coletar evidências de atestado e enviá-las ao serviço Attestation Verifier para um token de atestado (um token OIDC).
- Crie o Dockerfile para a imagem de contêiner do aplicativo.
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"]
- Crie um Artifact Registry.
gcloud artifacts repositories create codelab-repo \
--repository-format=docker \
--location=us
- Envie a imagem do contêiner do aplicativo para o Artifact Registry.
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) Configure uma conta de serviço do Kubernetes para herdar as permissões de uma conta de serviço do GCP nos recursos do GCP.
- Crie uma conta de serviço do Kubernetes
codelab-ksa.
kubectl create serviceaccount codelab-ksa \
--namespace default
- Crie um papel
Confidential_Computing_Workload_Usere conceda as permissões dele para acessar as APIs da Computação confidencial.
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
Substitua:
project-idé o identificador exclusivo do projeto.
- Crie uma conta de serviço do GCP
codelab-csae vincule-a ao papelConfidential_Computing_Workload_User. So that codelab-csaque tem permissões para acessar as APIs de Computação confidencial.
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]"
Substitua:
project-idé o identificador exclusivo do projeto.
- Vincule a conta de serviço do Kubernetes
codelab-ksaà conta de serviço do GCPcodelab-csa. Para quecodelab-ksatenha permissões de acesso às APIs de Computação confidencial.
kubectl annotate serviceaccount codelab-ksa \
--namespace default \
iam.gke.io/gcp-service-account=codelab-csa@<project-id>.iam.gserviceaccount.com
Substitua:
project-idé o identificador exclusivo do projeto.
3) Crie o arquivo YAML de implantação do aplicativo de demonstração. Atribua a conta de serviço do Kubernetes codelab-ksa às cargas de trabalho selecionadas.
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
Substitua:
project-idé o identificador exclusivo do projeto.
4). Aplique a implantação ao cluster do CGKE.
kubectl create -f deploy.yaml
5). Conecte-se à carga de trabalho e inicie o atestado remoto para buscar um token de atestado (um token OIDC).
kubectl exec -it go-tpm-demo -- /bin/bash ./gotpm token --event-log=/run/cc-device-plugin/binary_bios_measurements > attestation_token
Você pode decodificar o token de comprovação em jwt.io para ver as declarações.
5. Como configurar o servidor da Web de lançamento de secrets
Nesta etapa, você vai sair da sessão SSH anterior e configurar outra VM. Nessa VM, você vai configurar um servidor da Web de lançamento secreto. O servidor da Web valida o token de comprovação recebido e as declarações dele. Se as validações forem bem-sucedidas, o Secret será transmitido ao requerente.
1) Acesse o console do Cloud ou seu ambiente de desenvolvimento local. Crie uma máquina virtual.
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
Substitua:
project-idé o identificador exclusivo do projeto.
2) Conecte-se por SSH à nova VM.
gcloud compute ssh --zone us-central1-c cgke-attestation-codelab-web-server
3) Configure o ambiente do Go.
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). Crie os dois arquivos a seguir para armazenar o código-fonte do servidor da Web de lançamento secreto (copie e cole com o nano).
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). Execute os comandos a seguir para criar e executar o servidor da Web. Isso inicia o servidor da Web de lançamento de secrets na porta :8080.
go mod init google.com/codelab go mod tidy go get github.com/golang-jwt/jwt/v4 go build ./codelab
Solução de problemas: talvez você veja o seguinte aviso, que pode ser ignorado ao executar go mod tidy:
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). Inicie outra guia do console do Cloud ou sessão do ambiente de desenvolvimento local e execute o seguinte comando. Isso vai dar a você o cgke-attestation-codelab-web-server-internal-ip.
gcloud compute instances describe cgke-attestation-codelab-web-server --format='get(networkInterfaces[0].networkIP)' --zone=us-central1-c
7). Conecte-se à sua carga de trabalho do CGKE e inicie a atestação remota para buscar um token de atestação (um token OIDC). Em seguida, incorpore o conteúdo de attestation-token e cgke-attestation-codelab-web-server-internal-ip no seguinte comando. Isso vai buscar o secret mantido pelo servidor da Web de lançamento de secrets.
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)"
Substitua:
cgke-attestation-codelab-web-server-internal-ipé o IP interno da instância de VMcgke-attestation-codelab-web-server.
6. Vedação de vTPM nos nós do CGKE
Esta etapa inicia a segunda parte deste codelab. Nesta etapa, você configura a autorização do proprietário do vTPM nos nós do CGKE e implanta uma carga de trabalho com a senha longa do proprietário do vTPM. Depois, você cria uma chave primária do vTPM para lacrar e abrir dados na carga de trabalho com a capacidade de lacragem do vTPM.
1) Configure a autorização do proprietário do vTPM nos nós do CGKE.
- Crie uma imagem do contêiner de job único. O job único define a senha do proprietário para todas as vTPMs. Confira abaixo o Dockerfile para criar a imagem do contêiner.
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"]
- Crie e envie a imagem do contêiner de job único para o Artifact Registry.
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
Substitua:
project-idé o identificador exclusivo do projeto.
- Execute o job único usando um job do Kubernetes. AVISO: esse job limpa o vTPM em cada CVM. Se a CVM usar o vTPM para criptografar o disco, esse job vai tornar a CVM inutilizável após a reinicialização. Verifique se o disco tem FSTYPE
crypto_LUKScom o comandolsblk -f.
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
Substitua:
project-idé o identificador exclusivo do projeto.
- Inicie o job único. Esse job define a senha do proprietário do vTPM em todos os nós de worker.
kubectl create -f tpm-tools-task.yaml
2) Crie um secret do Kubernetes para armazenar a senha do proprietário do vTPM.
kubectl create secret generic tpm-secret --from-literal=passphrase='this_is_passphrase'
3) Crie um contêiner de aplicativo de demonstração e transmita a senha a ele. O contêiner do aplicativo de demonstração contém ferramentas tpm2 para interagir com o vTPM.
- Crie o arquivo YAML de implantação para o contêiner do aplicativo de demonstração.
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
Substitua:
project-idé o identificador exclusivo do projeto.
- Implante o aplicativo de demonstração.
kubectl create -f deploy_demo.yaml
4). Realize o isolamento do vTPM no contêiner do aplicativo de demonstração.
- Conecte-se ao contêiner do aplicativo de demonstração e defina uma chave primária com senha.
kubectl exec -it tpm-tools-demo -- /bin/bash tpm2_createprimary -C o -c primary.ctx -P $(cat /etc/tpmsecret/passphrase)
O tpm2_createprimary interage com o vTPM para gerar o objeto principal com base na hierarquia e no modelo especificados.
- -C o: indica que a chave primária será criada na hierarquia do proprietário do TPM.
- -c primary.ctx: salva o contexto (handle e dados associados) do objeto principal criado no arquivo primary.ctx. Esse contexto é essencial para operações posteriores.
A carga de trabalho não pode usar a senha longa do proprietário errada para criar uma chave primária.
tpm2_createprimary -C o -P wrong_passphrase
O comando retorna os seguintes erros:
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
- A chave principal criada pode ser usada para lacrar e abrir dados.
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
O tpm2_create interage com o vTPM para gerar o objeto criptográfico desejado.
- -C primary.ctx: usa o contexto de chave primária que criamos antes.
- -u sealed.pub: armazena a parte pública da chave de lacre (necessária para remover o lacre) em sealed.pub.
- -r sealed.priv: armazena a parte privada da chave de lacre em sealed.priv.
- -i secret.txt: o arquivo que contém o secret a ser lacrado.
tpm2_load: carrega a chave de lacre no TPM usando as partes pública e privada (sealed.pub, sealed.priv) e salva o contexto em sealed.ctx.
tpm2_unseal: descriptografa (abre) dados que foram criptografados (lacrados) anteriormente usando um objeto de lacre vTPM.
Observação: os arquivos primary.ctx e sealed.priv só podem ser usados em um dispositivo vTPM. Qualquer pessoa com acesso ao dispositivo vTPM e a esses arquivos pode acessar os dados criptografados. Você também pode usar a política em valores de PCR para proteger os dados, mas isso está fora do escopo deste codelab.
7. Limpeza
Execute os comandos a seguir no console do Cloud ou no seu ambiente de desenvolvimento local:
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
Substitua:
project-idé o identificador exclusivo do projeto.
8. A seguir
Saiba mais sobre os Confidential GKE Nodes.