1. Descripción general
Los nodos de Confidential GKE (CGKE) garantizan que los datos de las cargas de trabajo se encripten en uso. Exponer el dispositivo vTPM a las cargas de trabajo de CGKE permite que estas usen las funciones de vTPM. En este codelab, se muestran dos funciones del vTPM.
- La certificación remota del vTPM permite que una entidad remota verifique que los nodos de CGKE que alojan cargas de trabajo se ejecutan en VMs confidenciales (CVM).
- Autorización y sellado de vTPM

Como se muestra en la figura anterior, la primera parte de este codelab incluye los siguientes pasos:
- Los nodos de CGKE configuran y exponen el dispositivo vTPM a las cargas de trabajo seleccionadas.
- Implementa una carga de trabajo y realiza la certificación remota del nodo de CGKE que la aloja.
- Configuración del servidor web de Secret Release.

Como se muestra en la figura anterior, la segunda parte de este codelab incluye lo siguiente:
- Configuración de la autorización del vTPM y sellado del vTPM en los nodos de CGKE
Qué aprenderás
- Cómo exponer el dispositivo de vTPM a las cargas de trabajo de CGKE
- Cómo realizar la certificación remota a través de la API de Confidential Computing (servicio de verificador de certificación) en cargas de trabajo de CGKE
- Cómo configurar la autorización del vTPM y realizar el sellado del vTPM
Requisitos
- Un proyecto de Google Cloud Platform
- Un navegador, como Chrome o Firefox
- Conocimientos básicos de Google Compute Engine ( codelab), Confidential VMs, Confidential GKE nodes y Artifact Registry
2. Configuración y requisitos:
Para habilitar las APIs necesarias, ejecuta el siguiente comando en Cloud Console o en tu entorno de desarrollo 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. Configura nodos de CGKE y expón el dispositivo vTPM a cargas de trabajo seleccionadas
Este paso inicia la primera parte de este codelab. En este paso, iniciarás un clúster de CGKE y aplicarás un complemento de dispositivo para exponer el dispositivo vTPM de la CVM a las cargas de trabajo. Ve a Cloud Console o a tu entorno de desarrollo local para ejecutar los comandos.
1). Crea un clúster de CGKE y usa un grupo de identidades para cargas de trabajo para permitir que las cargas de trabajo de CGKE usen la API de Confidential Computing de GCP. El grupo de identidades para cargas de trabajo es necesario porque las cargas de trabajo de CGKE deben acceder a los recursos de GCP. Además, para acceder a los recursos de GCP, las cargas de trabajo de CGKE deben tener una identidad.
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
Reemplaza lo siguiente:
project-ides el identificador único del proyecto.
2). Inicia el complemento del dispositivo para permitir que el clúster de CGKE exponga el dispositivo vTPM a las cargas de trabajo. Usamos un complemento de dispositivo de Kubernetes para crear un nuevo recurso: google.com/cc. Cualquier carga de trabajo asociada con el nuevo recurso podrá ver el dispositivo vTPM en el nodo trabajador.
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
Reemplaza lo siguiente:
project-ides el identificador único del proyecto.
El siguiente comando te permite ver el cc-device-plugin implementado.
kubectl get pods -A | grep "cc-device-plugin"
Nota: En el caso de un clúster de GKE en modo mixto (con nodos trabajadores de GKE confidenciales y no confidenciales), se recomienda que el operador solo implemente cc-device-plugin en los nodos trabajadores de GKE confidenciales.
(Opcional) Aplica la supervisión de Prometheus del pod de CGKE. Activar la supervisión te permite observar el estado del complemento del dispositivo.
kubectl apply -f https://raw.githubusercontent.com/google/cc-device-plugin/main/manifests/cc-device-plugin-pod-monitoring.yaml
Ve a https://console.cloud.google.com/monitoring/metrics-explorer y busca las métricas de cc-device-plugin o usa PROMQL. Por ejemplo, el siguiente comando de PROMQL muestra los segundos de CPU para cada proceso de cc-device-plugin.
rate(process_cpu_seconds_total[${__interval}])
4. Implementar una carga de trabajo y realizar la certificación remota en ella
En este paso, crearás e implementarás una carga de trabajo en el clúster de CGKE que creaste en el paso anterior y realizarás una certificación remota del vTPM para recuperar un token de certificación (token de OIDC) en el nodo de trabajador.
1). Crea la imagen del contenedor de la aplicación y envíala a Artifact Registry. La imagen del contenedor de la aplicación contiene la herramienta go-tpm, que puede recopilar evidencia de certificación y enviarla al servicio de Attestation Verifier para obtener un token de certificación (un token de OIDC).
- Crea el Dockerfile para la imagen del contenedor de la aplicación.
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"]
- Crea un registro de artefactos.
gcloud artifacts repositories create codelab-repo \
--repository-format=docker \
--location=us
- Envía la imagen del contenedor de la aplicación a 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). Configura una cuenta de servicio de Kubernetes para heredar los permisos de una cuenta de servicio de GCP en los recursos de GCP.
- Crea una cuenta de servicio de Kubernetes
codelab-ksa.
kubectl create serviceaccount codelab-ksa \
--namespace default
- Crea un rol
Confidential_Computing_Workload_Usery le otorga permisos para acceder a las APIs de Confidential Computing.
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
Reemplaza lo siguiente:
project-ides el identificador único del proyecto.
- Crea una cuenta de servicio de GCP
codelab-csay vincúlala con el rolConfidential_Computing_Workload_User. So that codelab-csaque tiene permisos para acceder a las APIs de Confidential Computing.
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]"
Reemplaza lo siguiente:
project-ides el identificador único del proyecto.
- Vincula la cuenta de servicio de Kubernetes
codelab-ksacon la cuenta de servicio de GCPcodelab-csa. Para quecodelab-ksatenga permisos para acceder a las APIs de Confidential Computing
kubectl annotate serviceaccount codelab-ksa \
--namespace default \
iam.gke.io/gcp-service-account=codelab-csa@<project-id>.iam.gserviceaccount.com
Reemplaza lo siguiente:
project-ides el identificador único del proyecto.
3). Crea el archivo YAML de implementación de la aplicación de demostración. Asigna la cuenta de servicio de Kubernetes codelab-ksa a las cargas de trabajo seleccionadas.
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
Reemplaza lo siguiente:
project-ides el identificador único del proyecto.
4). Aplica la implementación al clúster de CGKE.
kubectl create -f deploy.yaml
5). Conéctate a la carga de trabajo y lanza la certificación remota para recuperar un token de certificación (un token de OIDC)
kubectl exec -it go-tpm-demo -- /bin/bash ./gotpm token --event-log=/run/cc-device-plugin/binary_bios_measurements > attestation_token
Puedes decodificar el token de certificación en jwt.io para ver los reclamos.
5. Cómo configurar el servidor web de Secret Release
En este paso, saldrás de la sesión SSH anterior y configurarás otra VM. En esta VM, configurarás un servidor web de lanzamiento secreto. El servidor web valida el token de certificación recibido y sus declaraciones. Si las validaciones se realizan correctamente, se pasa el secreto al solicitante.
1). Ve a Cloud Console o a tu entorno de desarrollo local. Crea una 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
Reemplaza lo siguiente:
project-ides el identificador único del proyecto.
2). Conéctate a tu nueva VM a través de SSH.
gcloud compute ssh --zone us-central1-c cgke-attestation-codelab-web-server
3). Configura el entorno de 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). Crea los siguientes dos archivos que almacenan el código fuente del servidor web de lanzamiento secreto (copia y pega con 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). Ejecuta los siguientes comandos para compilar el servidor web y ejecutarlo. Esto inicia el servidor web de lanzamiento de secretos en el puerto :8080.
go mod init google.com/codelab go mod tidy go get github.com/golang-jwt/jwt/v4 go build ./codelab
Solución de problemas: Es posible que veas la siguiente advertencia, que puedes ignorar cuando ejecutes 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). Inicia otra pestaña de Cloud Console o sesión del entorno de desarrollo local y ejecuta el siguiente comando. Esto te dará el 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). Conéctate a tu carga de trabajo de CGKE y lanza la certificación remota para recuperar un token de certificación (un token de OIDC). Luego, incorpora el contenido de attestation-token y cgke-attestation-codelab-web-server-internal-ip en el siguiente comando. Esto recuperará el secreto que contiene el servidor web de lanzamiento de secretos.
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)"
Reemplaza lo siguiente:
cgke-attestation-codelab-web-server-internal-ipes la IP interna de la instancia de VMcgke-attestation-codelab-web-server.
6. Sellado de vTPM en los nodos de CGKE
Con este paso, comienza la segunda parte de este codelab. En este paso, configurarás la autorización del propietario del vTPM en los nodos de CGKE y, luego, implementarás una carga de trabajo con la frase de contraseña del propietario del vTPM. Luego, crearás una clave principal del vTPM para sellar y desellar datos en la carga de trabajo con la capacidad de sellado del vTPM.
1). Configura la autorización del propietario del vTPM en los nodos de CGKE.
- Crea una imagen de contenedor de trabajo único. El trabajo único establece la contraseña del propietario para todos los vTPM. A continuación, se muestra el Dockerfile para crear la imagen del contenedor.
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"]
- Compila y envía la imagen del contenedor de trabajo único al registro de artefactos.
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
Reemplaza lo siguiente:
project-ides el identificador único del proyecto.
- Ejecuta el trabajo único a través de un trabajo de Kubernetes. (ADVERTENCIA: Este trabajo borra el vTPM en cada CVM. Si tu CVM usa el vTPM para encriptar el disco, este trabajo hará que tu CVM sea inutilizable después de reiniciar. Puedes verificar si tu disco tiene FSTYPE
crypto_LUKScon el 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
Reemplaza lo siguiente:
project-ides el identificador único del proyecto.
- Inicia el trabajo único. Este trabajo establece la frase de contraseña del propietario del vTPM en todos los nodos trabajadores.
kubectl create -f tpm-tools-task.yaml
2). Crea un secreto de Kubernetes para almacenar la frase de contraseña del propietario del vTPM.
kubectl create secret generic tpm-secret --from-literal=passphrase='this_is_passphrase'
3). Crea un contenedor de aplicación de demostración y pásale la frase de contraseña. El contenedor de la aplicación de demostración contiene herramientas de tpm2 para interactuar con el vTPM.
- Crea el archivo YAML de implementación para el contenedor de la aplicación de demostración.
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
Reemplaza lo siguiente:
project-ides el identificador único del proyecto.
- Implementa la aplicación de demostración.
kubectl create -f deploy_demo.yaml
4). Realiza el sellado del vTPM en el contenedor de la aplicación de demostración.
- Conéctate al contenedor de la aplicación de demostración y establece una clave principal con una frase de contraseña.
kubectl exec -it tpm-tools-demo -- /bin/bash tpm2_createprimary -C o -c primary.ctx -P $(cat /etc/tpmsecret/passphrase)
tpm2_createprimary interactúa con el vTPM para generar el objeto principal según la jerarquía y la plantilla especificadas.
- -C o: Indica que la clave principal se creará en la jerarquía del propietario del TPM.
- -c primary.ctx: Guarda el contexto (controlador y datos asociados) del objeto principal creado en el archivo primary.ctx. Este contexto es esencial para operaciones posteriores.
La carga de trabajo no puede usar la frase de contraseña del propietario incorrecta para crear una clave principal.
tpm2_createprimary -C o -P wrong_passphrase
El comando devuelve los siguientes errores:
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
- La clave principal creada se podría usar para sellar y abrir datos.
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 interactúa con el vTPM para generar el objeto criptográfico deseado.
- -C primary.ctx: Usa el contexto de clave principal que creamos anteriormente.
- -u sealed.pub: Almacena la parte pública de la clave de sellado (necesaria para el desellado) en sealed.pub.
- -r sealed.priv: Almacena la parte privada de la clave de sellado en sealed.priv.
- -i secret.txt: Es el archivo que contiene el secreto que se sellará.
tpm2_load: Carga la clave de sellado en el TPM con las partes pública y privada (sealed.pub, sealed.priv) y guarda su contexto en sealed.ctx.
tpm2_unseal: Desencripta (desella) los datos que se encriptaron (sellaron) anteriormente con un objeto de sellado de vTPM.
Ten en cuenta que los archivos primary.ctx y sealed.priv solo se pueden usar en un dispositivo vTPM. Además, cualquier persona que tenga acceso al dispositivo vTPM y a estos archivos podrá acceder a los datos sellados. También puedes usar la política sobre los valores del PCR para sellar los datos, pero está fuera del alcance de este codelab.
7. Limpieza
Ejecuta los siguientes comandos en la consola de Cloud o en tu entorno de desarrollo 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
Reemplaza lo siguiente:
project-ides el identificador único del proyecto.
8. ¿Qué sigue?
Obtén más información sobre los Confidential GKE Nodes.