1. সংক্ষিপ্ত বিবরণ
গোপনীয় GKE (CGKE) নোডগুলি নিশ্চিত করে যে ওয়ার্কলোডের ডেটা এনক্রিপ্ট করা-ব্যবহারযোগ্য। VTPM ডিভাইসটিকে CGKE ওয়ার্কলোডের কাছে এক্সপোজ করার ফলে ওয়ার্কলোডগুলি vTPM বৈশিষ্ট্যগুলি ব্যবহার করতে সক্ষম হয়। এই কোডল্যাবে, আপনাকে vTPM এর দুটি বৈশিষ্ট্য দেখানো হয়েছে।
- vTPM রিমোট অ্যাটেস্টেশন একটি রিমোট পার্টিকে যাচাই করতে দেয় যে ওয়ার্কলোড হোস্টিং করা CGKE নোডগুলি কনফিডেনশিয়াল VM (CVM) তে চলছে।
- vTPM অনুমোদন এবং vTPM সিলিং।

উপরের চিত্রে যেমন দেখানো হয়েছে, এই কোডল্যাবের প্রথম অংশে নিম্নলিখিত ধাপগুলি অন্তর্ভুক্ত রয়েছে:
- CGKE নোডগুলি vTPM ডিভাইসটিকে নির্বাচিত ওয়ার্কলোডের সাথে সেটআপ করে এবং এক্সপোজ করে।
- একটি ওয়ার্কলোড স্থাপন করুন এবং ওয়ার্কলোড হোস্ট করা CGKE নোডটি দূরবর্তীভাবে যাচাই করুন।
- সিক্রেট রিলিজ ওয়েব সার্ভার সেটআপ।

উপরের চিত্রে যেমন দেখানো হয়েছে, এই কোডল্যাবের দ্বিতীয় অংশে রয়েছে:
- CGKE নোডগুলিতে vTPM অনুমোদন সেটআপ এবং vTPM সিলিং।
তুমি কি শিখবে
- কিভাবে vTPM ডিভাইসটিকে CGKE ওয়ার্কলোডে এক্সপোজ করবেন।
- CGKE ওয়ার্কলোডে কনফিডেনশিয়াল কম্পিউটিং API (প্রত্যয়ন যাচাইকারী পরিষেবা) এর মাধ্যমে কীভাবে দূরবর্তীভাবে সত্যায়িত করবেন।
- কিভাবে vTPM অনুমোদন সেট আপ করবেন এবং vTPM সিলিং করবেন।
তোমার যা লাগবে
- একটি গুগল ক্লাউড প্ল্যাটফর্ম প্রকল্প
- একটি ব্রাউজার, যেমন ক্রোম বা ফায়ারফক্স
- গুগল কম্পিউট ইঞ্জিন ( কোডল্যাব ), কনফিডেনশিয়াল ভিএম , কনফিডেনশিয়াল জিকেই নোড এবং আর্টিফ্যাক্ট রেজিস্ট্রি সম্পর্কে প্রাথমিক জ্ঞান।
2. সেটআপ এবং প্রয়োজনীয়তা:
প্রয়োজনীয় API গুলি সক্রিয় করতে, ক্লাউড কনসোল অথবা আপনার স্থানীয় ডেভেলপমেন্ট পরিবেশে নিম্নলিখিত কমান্ডটি চালান:
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
৩. CGKE নোড সেট আপ করা এবং নির্বাচিত ওয়ার্কলোডের সাথে vTPM ডিভাইসটি এক্সপোজ করা
এই ধাপটি এই কোডল্যাবের প্রথম অংশটি শুরু করে। এই ধাপে, আপনি একটি CGKE ক্লাস্টার শুরু করবেন এবং CVM vTPM ডিভাইসটিকে ওয়ার্কলোডে এক্সপোজ করার জন্য একটি ডিভাইস প্লাগইন প্রয়োগ করবেন। কমান্ডগুলি চালানোর জন্য ক্লাউড কনসোল বা আপনার স্থানীয় ডেভেলপমেন্ট পরিবেশে যান।
১) একটি CGKE ক্লাস্টার তৈরি করুন, CGKE ওয়ার্কলোডগুলিকে GCP গোপনীয় কম্পিউটিং API ব্যবহার করার অনুমতি দেওয়ার জন্য ওয়ার্কলোড আইডেন্টিটি পুল ব্যবহার করুন। ওয়ার্কলোড আইডেন্টিটি পুল প্রয়োজনীয় কারণ CGKE ওয়ার্কলোডগুলিকে GCP রিসোর্স অ্যাক্সেস করতে হবে। এবং GCP রিসোর্স অ্যাক্সেস করতে, CGKE ওয়ার্কলোডগুলির একটি পরিচয় থাকা প্রয়োজন।
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
নিম্নলিখিতগুলি প্রতিস্থাপন করুন:
-
project-idহল প্রকল্পের অনন্য শনাক্তকারী।
২) CGKE ক্লাস্টারকে vTPM ডিভাইসটিকে ওয়ার্কলোডের মধ্যে প্রকাশ করার অনুমতি দেওয়ার জন্য ডিভাইস প্লাগইনটি শুরু করুন। আমরা একটি নতুন রিসোর্স তৈরি করতে একটি kubernetes ডিভাইস প্লাগইন ব্যবহার করি - google.com/cc । নতুন রিসোর্সের সাথে যুক্ত যেকোনো ওয়ার্কলোড ওয়ার্কার নোডে vTPM ডিভাইসটি দেখতে সক্ষম হবে।
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
নিম্নলিখিতগুলি প্রতিস্থাপন করুন:
-
project-idহল প্রকল্পের অনন্য শনাক্তকারী।
নিম্নলিখিত কমান্ডটি আপনাকে স্থাপন করা cc-device-plugin দেখতে দেয়।
kubectl get pods -A | grep "cc-device-plugin"
দ্রষ্টব্য: মিশ্র মোড GKE ক্লাস্টারের ক্ষেত্রে (গোপনীয় এবং অ-গোপনীয় উভয় GKE কর্মী নোড সহ), অপারেটরকে শুধুমাত্র গোপনীয় GKE কর্মী নোডগুলিতে cc-device-plugin স্থাপন করার পরামর্শ দেওয়া হচ্ছে।
(ঐচ্ছিক)। CGKE পড প্রোমিথিউস মনিটরিং প্রয়োগ করুন। মনিটরিং চালু করলে আপনি ডিভাইস প্লাগইনের অবস্থা পর্যবেক্ষণ করতে পারবেন।
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 এ যান এবং cc-device-plugin মেট্রিক্স খুঁজুন অথবা PROMQL ব্যবহার করুন। উদাহরণস্বরূপ, নিম্নলিখিত PROMQL কমান্ডটি প্রতিটি cc-device-plugin প্রক্রিয়ার জন্য cpu সেকেন্ড দেখায়।
rate(process_cpu_seconds_total[${__interval}])
৪. একটি কাজের চাপ স্থাপন করা এবং কাজের চাপের উপর দূরবর্তী প্রমাণীকরণ করা
এই ধাপে, আপনি পূর্ববর্তী ধাপে তৈরি করা CGKE ক্লাস্টারে একটি ওয়ার্কলোড তৈরি এবং স্থাপন করবেন এবং ওয়ার্কার নোডে একটি অ্যাটেস্টেশন টোকেন (OIDC টোকেন) পুনরুদ্ধার করার জন্য একটি vTPM রিমোট অ্যাটেস্টেশন করবেন।
১) অ্যাপ্লিকেশন কন্টেইনার ইমেজ তৈরি করুন এবং এটিকে আর্টিফ্যাক্ট রেজিস্ট্রিতে পুশ করুন। অ্যাপ্লিকেশন কন্টেইনার ইমেজটিতে go-tpm টুল রয়েছে, যা প্রত্যয়ন প্রমাণ সংগ্রহ করতে পারে এবং একটি প্রত্যয়ন টোকেনের (একটি OIDC টোকেন) জন্য প্রত্যয়ন যাচাইকারী পরিষেবাতে পাঠাতে পারে।
- অ্যাপ্লিকেশন কন্টেইনার ছবির জন্য ডকারফাইল তৈরি করুন।
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"]
- একটি আর্টিফ্যাক্ট রেজিস্ট্রি তৈরি করুন।
gcloud artifacts repositories create codelab-repo \
--repository-format=docker \
--location=us
- অ্যাপ্লিকেশন কন্টেইনারের ছবিটি আর্টিফ্যাক্ট রেজিস্ট্রিতে ঠেলে দিন।
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
২). GCP রিসোর্সে GCP পরিষেবা অ্যাকাউন্টের অনুমতি পেতে একটি Kubernetes পরিষেবা অ্যাকাউন্ট সেট আপ করুন।
- একটি Kubernetes পরিষেবা অ্যাকাউন্ট
codelab-ksaতৈরি করুন।
kubectl create serviceaccount codelab-ksa \
--namespace default
-
Confidential_Computing_Workload_Userনামে একটি ভূমিকা তৈরি করে এবং ভূমিকাটিকে Confidential Computing API গুলিতে অ্যাক্সেস করার অনুমতি দেয়।
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
নিম্নলিখিতগুলি প্রতিস্থাপন করুন:
-
project-idহল প্রকল্পের অনন্য শনাক্তকারী।
- একটি GCP পরিষেবা অ্যাকাউন্ট
codelab-csaতৈরি করুন এবং এটিকেConfidential_Computing_Workload_User. So that codelab-csaকাছে Confidential Computing API গুলি অ্যাক্সেস করার অনুমতি থাকে।
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]"
নিম্নলিখিতগুলি প্রতিস্থাপন করুন:
-
project-idহল প্রকল্পের অনন্য শনাক্তকারী।
- Kubernetes পরিষেবা অ্যাকাউন্ট
codelab-ksaGCP পরিষেবা অ্যাকাউন্টcodelab-csaএর সাথে আবদ্ধ করুন। যাতেcodelab-ksaকাছে গোপনীয় কম্পিউটিং API গুলি অ্যাক্সেস করার অনুমতি থাকে।
kubectl annotate serviceaccount codelab-ksa \
--namespace default \
iam.gke.io/gcp-service-account=codelab-csa@<project-id>.iam.gserviceaccount.com
নিম্নলিখিতগুলি প্রতিস্থাপন করুন:
-
project-idহল প্রকল্পের অনন্য শনাক্তকারী।
৩) ডেমো অ্যাপ্লিকেশনের জন্য অ্যাপ্লিকেশন ডিপ্লয়মেন্ট yaml তৈরি করুন। নির্বাচিত ওয়ার্কলোডে Kubernetes পরিষেবা অ্যাকাউন্ট codelab-ksa বরাদ্দ করুন।
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
নিম্নলিখিতগুলি প্রতিস্থাপন করুন:
-
project-idহল প্রকল্পের অনন্য শনাক্তকারী।
৪) CGKE ক্লাস্টারে স্থাপনা প্রয়োগ করুন।
kubectl create -f deploy.yaml
৫)। কাজের চাপের সাথে সংযোগ স্থাপন করুন এবং একটি প্রত্যয়ন টোকেন (একটি OIDC টোকেন) আনতে দূরবর্তী প্রত্যয়ন চালু করুন।
kubectl exec -it go-tpm-demo -- /bin/bash ./gotpm token --event-log=/run/cc-device-plugin/binary_bios_measurements > attestation_token
দাবিগুলি দেখতে আপনি jwt.io- তে প্রত্যয়ন টোকেনটি ডিকোড করতে পারেন!
৫. সিক্রেট রিলিজ ওয়েব সার্ভার সেট আপ করা
এই ধাপে, আপনি পূর্ববর্তী SSH সেশন থেকে বেরিয়ে এসে আরেকটি VM সেট আপ করবেন। এই VM-এ, আপনি একটি গোপন রিলিজ ওয়েব সার্ভার সেট আপ করবেন। ওয়েব সার্ভারটি প্রাপ্ত অ্যাটেস্টেশন টোকেন এবং এর দাবিগুলি যাচাই করে। যদি যাচাইকরণ সফল হয়, তাহলে এটি অনুরোধকারীর কাছে গোপন তথ্য প্রেরণ করে।
১). ক্লাউড কনসোল অথবা আপনার স্থানীয় ডেভেলপমেন্ট পরিবেশে যান। একটি ভার্চুয়াল মেশিন তৈরি করুন।
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
নিম্নলিখিতগুলি প্রতিস্থাপন করুন:
-
project-idহল প্রকল্পের অনন্য শনাক্তকারী।
২)। আপনার নতুন VM-এ SSH।
gcloud compute ssh --zone us-central1-c cgke-attestation-codelab-web-server
৩). 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
৪)। গোপন রিলিজ ওয়েব সার্ভারের সোর্স কোড সংরক্ষণ করে নিম্নলিখিত দুটি ফাইল তৈরি করুন (ন্যানো দিয়ে কপি পেস্ট করুন)।
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)
}
৫) ওয়েব সার্ভার তৈরি করতে এবং এটি চালানোর জন্য নিম্নলিখিত কমান্ডগুলি চালান। এটি :8080 পোর্টে গোপন রিলিজ ওয়েব সার্ভার শুরু করে।
go mod init google.com/codelab go mod tidy go get github.com/golang-jwt/jwt/v4 go build ./codelab
সমস্যা সমাধান: আপনি নিম্নলিখিত সতর্কতা দেখতে পাবেন যা 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
৬) আরেকটি ক্লাউড কনসোল ট্যাব অথবা লোকাল ডেভেলপমেন্ট এনভায়রনমেন্ট সেশন শুরু করুন এবং নিম্নলিখিত কমান্ডটি চালান। এটি আপনাকে 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
৭)। আপনার CGKE ওয়ার্কলোডের সাথে সংযোগ করুন এবং একটি অ্যাটেস্টেশন টোকেন (একটি OIDC টোকেন) আনতে রিমোট অ্যাটেস্টেশন চালু করুন। তারপর নিম্নলিখিত কমান্ডে attestation-token এবং cgke-attestation-codelab-web-server-internal-ip এর কন্টেন্ট এম্বেড করুন। এটি আপনাকে সিক্রেট রিলিজ ওয়েব সার্ভারের কাছে থাকা গোপন তথ্যটি এনে দেবে!
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)"
নিম্নলিখিতগুলি প্রতিস্থাপন করুন:
-
cgke-attestation-codelab-web-server-internal-ipহলcgke-attestation-codelab-web-serverVM ইনস্ট্যান্সের অভ্যন্তরীণ আইপি।
৬. CGKE নোডগুলিতে vTPM সিলিং
এই ধাপটি এই কোডল্যাবের দ্বিতীয় অংশ শুরু করে। এই ধাপে, আপনি CGKE নোডগুলিতে vTPM মালিক অনুমোদন সেট আপ করেন এবং vTPM মালিক পাসফ্রেজ সহ একটি ওয়ার্কলোড স্থাপন করেন। এরপর, আপনি vTPM সিলিং ক্ষমতা সহ ওয়ার্কলোডে ডেটা সিল এবং আনসিল করার জন্য একটি vTPM প্রাথমিক কী তৈরি করেন।
১). CGKE নোডগুলিতে vTPM মালিক অনুমোদন সেট আপ করুন।
- একটি এককালীন কাজের কন্টেইনার ইমেজ তৈরি করুন। এককালীন কাজের জন্য সমস্ত vTPM-এর জন্য মালিকের পাসওয়ার্ড সেট করা হয়। কন্টেইনার ইমেজ তৈরি করার জন্য ডকারফাইলটি নিচে দেওয়া হল।
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"]
- এককালীন কাজের কন্টেইনার চিত্রটি তৈরি করুন এবং আর্টিফ্যাক্ট রেজিস্ট্রিতে পুশ করুন।
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
নিম্নলিখিতগুলি প্রতিস্থাপন করুন:
-
project-idহল প্রকল্পের অনন্য শনাক্তকারী।
- Kubernetes জবের মাধ্যমে এককালীন কাজটি সম্পাদন করুন। (সতর্কতা: এই কাজটি প্রতিটি CVM-এ vTPM সাফ করে, যদি আপনার CVM ডিস্ক এনক্রিপ্ট করার জন্য vTPM ব্যবহার করে, তাহলে এই কাজটি রিবুট করার পরে আপনার CVM কে অব্যবহারযোগ্য করে তুলবে। আপনি
lsblk -fকমান্ড দিয়ে আপনার ডিস্কে FSTYPEcrypto_LUKSআছে কিনা তা পরীক্ষা করতে পারেন)
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
নিম্নলিখিতগুলি প্রতিস্থাপন করুন:
-
project-idহল প্রকল্পের অনন্য শনাক্তকারী।
- এককালীন কাজটি চালু করুন। এই কাজটি সমস্ত কর্মী নোডে vTPM মালিকের পাসফ্রেজ সেট করে।
kubectl create -f tpm-tools-task.yaml
২) vTPM মালিকের পাসফ্রেজ ধরে রাখার জন্য একটি Kubernetes গোপনীয়তা তৈরি করুন।
kubectl create secret generic tpm-secret --from-literal=passphrase='this_is_passphrase'
৩). একটি ডেমো অ্যাপ্লিকেশন কন্টেইনার তৈরি করুন এবং এতে পাসফ্রেজটি দিন। ডেমো অ্যাপ্লিকেশন কন্টেইনারে vTPM এর সাথে ইন্টারঅ্যাক্ট করার জন্য tpm2 টুল রয়েছে।
- ডেমো অ্যাপ্লিকেশন কন্টেইনারের জন্য ডিপ্লয়মেন্ট yaml ফাইল তৈরি করুন।
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
নিম্নলিখিতগুলি প্রতিস্থাপন করুন:
-
project-idহল প্রকল্পের অনন্য শনাক্তকারী।
- ডেমো অ্যাপ্লিকেশনটি স্থাপন করুন।
kubectl create -f deploy_demo.yaml
৪) ডেমো অ্যাপ্লিকেশন কন্টেইনারে vTPM সিলিং সম্পাদন করুন।
- ডেমো অ্যাপ্লিকেশন কন্টেইনারের সাথে সংযোগ করুন এবং পাসফ্রেজ সহ একটি প্রাথমিক কী সেট করুন।
kubectl exec -it tpm-tools-demo -- /bin/bash tpm2_createprimary -C o -c primary.ctx -P $(cat /etc/tpmsecret/passphrase)
tpm2_createprimary নির্দিষ্ট অনুক্রম এবং টেমপ্লেটের উপর ভিত্তি করে প্রাথমিক অবজেক্ট তৈরি করতে vTPM এর সাথে ইন্টারঅ্যাক্ট করে।
- -C o: নির্দেশ করে যে প্রাথমিক কীটি TPM এর মালিকের শ্রেণিবিন্যাসের অধীনে তৈরি করা হবে।
- -c primary.ctx: তৈরি করা প্রাথমিক বস্তুর প্রসঙ্গ (হ্যান্ডেল এবং সংশ্লিষ্ট ডেটা) primary.ctx ফাইলে সংরক্ষণ করে। পরবর্তী ক্রিয়াকলাপের জন্য এই প্রসঙ্গটি অপরিহার্য।
ওয়ার্কলোড একটি প্রাথমিক কী তৈরি করতে ভুল মালিকের পাসফ্রেজ ব্যবহার করতে পারে না।
tpm2_createprimary -C o -P ভুল_পাসফ্রেজ
কমান্ডটি নিম্নলিখিত ত্রুটিগুলি ফেরত দেয়:
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
- তৈরি করা প্রাথমিক কীটি তখন ডেটা সিল এবং আনসিল করতে ব্যবহার করা যেতে পারে।
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 কাঙ্ক্ষিত ক্রিপ্টোগ্রাফিক অবজেক্ট তৈরি করতে vTPM এর সাথে ইন্টারঅ্যাক্ট করে।
- -C primary.ctx: আমরা আগে তৈরি করা প্রাথমিক কী প্রসঙ্গ ব্যবহার করে।
- -u sealed.pub: সিলিং কী-এর সর্বজনীন অংশ (সিলিং মুক্ত করার জন্য প্রয়োজনীয়) sealed.pub-তে সংরক্ষণ করে।
- -r sealed.priv: সিলিং কী-এর গোপনাঙ্গ sealed.priv-এ সংরক্ষণ করে।
- -i secret.txt: সিল করা গোপন ফাইল।
tpm2_load : পাবলিক এবং প্রাইভেট অংশ (sealed.pub, sealed.priv) ব্যবহার করে TPM-এ সিলিং কী লোড করে এবং এর প্রসঙ্গ sealed.ctx-এ সংরক্ষণ করে।
tpm2_unseal : vTPM সিলিং অবজেক্ট ব্যবহার করে পূর্বে এনক্রিপ্ট করা (সিল করা) ডেটা ডিক্রিপ্ট (আনসিল) করা।
মনে রাখবেন: primary.ctx , sealed.priv ফাইলগুলি শুধুমাত্র একটি vTPM ডিভাইসে ব্যবহারযোগ্য। এবং vTPM ডিভাইস এবং এই ফাইলগুলিতে অ্যাক্সেস থাকা যে কেউ সিল করা ডেটা অ্যাক্সেস করতে পারে। আপনি ডেটা সিল করার জন্য PCR মানগুলির নীতি ব্যবহার করতে পারেন, তবে এই কোডল্যাবের জন্য এটি সুযোগের বাইরে।
৭. পরিষ্কার-পরিচ্ছন্নতা
ক্লাউড কনসোল অথবা আপনার স্থানীয় ডেভেলপমেন্ট পরিবেশে নিম্নলিখিত কমান্ডগুলি চালান:
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
নিম্নলিখিতগুলি প্রতিস্থাপন করুন:
-
project-idহল প্রকল্পের অনন্য শনাক্তকারী।
৮. এরপর কী?
গোপনীয় GKE নোড সম্পর্কে আরও জানুন।