Created
July 11, 2026 00:42
-
-
Save mohashari/952a36072d5b874063840f95f5974a37 to your computer and use it in GitHub Desktop.
Designing an Automated Secret Zero Bootstrapping Pipeline in Kubernetes using TPM 2.0 and SPIFFE/SPIRE — code snippets
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # SPIRE Server configuration snippet for TPM 2.0 node attestation | |
| server { | |
| bind_address = "0.0.0.0" | |
| bind_port = "8081" | |
| trust_domain = "prod.muklis.internal" | |
| data_dir = "/run/spire/data" | |
| log_level = "INFO" | |
| ca_key_type = "rsa-2048" | |
| ca_ttl = "24h" | |
| default_svid_ttl = "1h" | |
| } | |
| plugins { | |
| NodeAttestor "tpm" { | |
| plugin_data { | |
| ca_path = "/run/spire/config/tpm-vendor-cas.pem" | |
| # Whitelist database mapped to physical machine inventories | |
| endorsement_key_db_path = "/run/spire/config/ek_whitelist.json" | |
| } | |
| } | |
| KeyManager "disk" { | |
| plugin_data { | |
| keys_path = "/run/spire/data/keys.json" | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # SPIRE Agent configuration snippet for TPM 2.0 node attestation | |
| agent { | |
| data_dir = "/run/spire-agent/data" | |
| log_level = "INFO" | |
| server_address = "spire-server.spire.svc.cluster.local" | |
| server_port = "8081" | |
| socket_path = "/run/spire/sockets/agent.sock" | |
| trust_domain = "prod.muklis.internal" | |
| } | |
| plugins { | |
| NodeAttestor "tpm" { | |
| plugin_data { | |
| # Path to the TPM 2.0 resource manager device | |
| tpm_device_path = "/dev/tpmrm0" | |
| # Owner hierarchy password (empty by default on clean systems) | |
| owner_auth = "" | |
| } | |
| } | |
| KeyManager "memory" { | |
| plugin_data {} | |
| } | |
| WorkloadAttestor "k8s" { | |
| plugin_data { | |
| kubelet_read_only_port = 10255 | |
| # Enforce secure connection to kubelet API for verification | |
| use_https = true | |
| kubelet_ca_path = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" | |
| token_path = "/var/run/secrets/kubernetes.io/serviceaccount/token" | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Pod spec mounting the SPIFFE workload API socket via CSI driver | |
| apiVersion: apps/v1 | |
| kind: Deployment | |
| metadata: | |
| name: billing-service | |
| namespace: payment-apps | |
| labels: | |
| app: billing-service | |
| spec: | |
| replicas: 3 | |
| selector: | |
| matchLabels: | |
| app: billing-service | |
| template: | |
| metadata: | |
| labels: | |
| app: billing-service | |
| spec: | |
| serviceAccountName: billing-sa | |
| containers: | |
| - name: application | |
| image: billing-service:v2.1.0 | |
| env: | |
| - name: SPIFFE_ENDPOINT_SOCKET | |
| value: "unix:///run/spiffe/workload.sock" | |
| - name: VAULT_ADDR | |
| value: "https://vault.prod.muklis.internal:8200" | |
| volumeMounts: | |
| - name: spiffe-workload-api | |
| mountPath: /run/spiffe | |
| readOnly: true | |
| resources: | |
| limits: | |
| cpu: "1" | |
| memory: "1Gi" | |
| requests: | |
| cpu: "250m" | |
| memory: "256Mi" | |
| volumes: | |
| - name: spiffe-workload-api | |
| csi: | |
| driver: "spiffe.csi.spiffe.io" | |
| readOnly: true |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package main | |
| import ( | |
| "context" | |
| "fmt" | |
| "log" | |
| "net/http" | |
| "time" | |
| "github.com/spiffe/go-spiffe/v2/spiffeid" | |
| "github.com/spiffe/go-spiffe/v2/spiffetls" | |
| "github.com/spiffe/go-spiffe/v2/workload" | |
| ) | |
| const socketPath = "unix:///run/spiffe/workload.sock" | |
| func main() { | |
| ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) | |
| defer cancel() | |
| // Connect to the SPIFFE Workload API via the Unix Domain Socket | |
| client, err := workload.NewX509Source(ctx, workload.WithClientOptions(workload.WithAddr(socketPath))) | |
| if err != nil { | |
| log.Fatalf("Unable to create X.509 source from socket %s: %v", socketPath, err) | |
| } | |
| defer client.Close() | |
| // Verify the SVID was retrieved successfully | |
| svid, err := client.GetX509SVID() | |
| if err != nil { | |
| log.Fatalf("Failed to fetch SVID from source: %v", err) | |
| } | |
| fmt.Printf("Successfully retrieved SVID: %s\n", svid.ID.String()) | |
| // Set up an mTLS client configuration that trusts only specific workloads in our domain | |
| targetAudience, err := spiffeid.FromString("spiffe://prod.muklis.internal/ns/payment-apps/sa/vault-sa") | |
| if err != nil { | |
| log.Fatalf("Invalid target SPIFFE ID: %v", err) | |
| } | |
| tlsConfig := spiffetls.NewTLSConfig(client, spiffetls.AuthorizeID(targetAudience)) | |
| httpClient := &http.Client{ | |
| Transport: &http.Transport{ | |
| TLSClientConfig: tlsConfig, | |
| }, | |
| Timeout: 5 * time.Second, | |
| } | |
| // This client is now ready to query protected services using hardware-backed TLS | |
| _ = httpClient | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Configure HashiCorp Vault SPIFFE JWT Auth Method | |
| # Enable the JWT auth engine at a dedicated path | |
| vault auth enable -path=spiffe jwt | |
| # Write the configuration containing the SPIRE OIDC discovery URL | |
| vault write auth/spiffe/config \ | |
| oidc_discovery_url_query_parameter_supported=false \ | |
| oidc_discovery_url="https://spire-server-oidc.spire.svc.cluster.local:443" \ | |
| default_role="billing-role" \ | |
| bound_issuer="https://spire-server-oidc.spire.svc.cluster.local:443" | |
| # Create a role that binds a specific SPIFFE ID subject to a Vault policy | |
| vault write auth/spiffe/role/billing-role - <<EOF | |
| { | |
| "role_type": "jwt", | |
| "policies": ["billing-secrets-policy"], | |
| "token_ttl": "15m", | |
| "token_max_ttl": "1h", | |
| "bound_subject": "spiffe://prod.muklis.internal/ns/payment-apps/sa/billing-sa", | |
| "user_claim": "sub" | |
| } | |
| EOF |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| package main | |
| import ( | |
| "bytes" | |
| "context" | |
| "encoding/json" | |
| "fmt" | |
| "io" | |
| "log" | |
| "net/http" | |
| "time" | |
| "github.com/spiffe/go-spiffe/v2/spiffeid" | |
| "github.com/spiffe/go-spiffe/v2/workload" | |
| ) | |
| const ( | |
| spiffeSocket = "unix:///run/spiffe/workload.sock" | |
| vaultAddress = "https://vault.prod.muklis.internal:8200" | |
| vaultRole = "billing-role" | |
| ) | |
| type VaultLoginResponse struct { | |
| Auth struct { | |
| ClientToken string `json:"client_token"` | |
| Accessor string `json:"accessor"` | |
| LeaseDuration int `json:"lease_duration"` | |
| } `json:"auth"` | |
| } | |
| func fetchSecretZero() (string, error) { | |
| ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) | |
| defer cancel() | |
| // Initialize the JWT Source | |
| jwtSource, err := workload.NewJWTSource(ctx, workload.WithClientOptions(workload.WithAddr(spiffeSocket))) | |
| if err != nil { | |
| return "", fmt.Errorf("failed to connect to SPIFFE workload API: %w", err) | |
| } | |
| defer jwtSource.Close() | |
| // Request a signed JWT SVID with the 'vault' audience | |
| aud := spiffeid.RequireTrustDomainFromString("prod.muklis.internal") | |
| params := workload.JWTParameters{ | |
| Audience: "vault", | |
| Subject: spiffeid.RequireFromString("spiffe://prod.muklis.internal/ns/payment-apps/sa/billing-sa"), | |
| } | |
| svid, err := jwtSource.GetJWTSVID(ctx, params) | |
| if err != nil { | |
| return "", fmt.Errorf("failed to fetch JWT SVID: %w", err) | |
| } | |
| // Payload for Vault login | |
| payload := map[string]string{ | |
| "role": vaultRole, | |
| "jwt": svid.Marshal(), | |
| } | |
| jsonPayload, err := json.Marshal(payload) | |
| if err != nil { | |
| return "", err | |
| } | |
| // Send POST request to Vault auth endpoint | |
| req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("%s/v1/auth/spiffe/login", vaultAddress), bytes.NewBuffer(jsonPayload)) | |
| if err != nil { | |
| return "", err | |
| } | |
| req.Header.Set("Content-Type", "application/json") | |
| client := &http.Client{Timeout: 5 * time.Second} | |
| resp, err := client.Do(req) | |
| if err != nil { | |
| return "", fmt.Errorf("failed sending request to Vault: %w", err) | |
| } | |
| defer resp.Body.Close() | |
| if resp.StatusCode != http.StatusOK { | |
| body, _ := io.ReadAll(resp.Body) | |
| return "", fmt.Errorf("vault login failed with status %d: %s", resp.StatusCode, string(body)) | |
| } | |
| var vaultResp VaultLoginResponse | |
| if err := json.NewDecoder(resp.Body).Decode(&vaultResp); err != nil { | |
| return "", fmt.Errorf("failed to decode Vault response: %w", err) | |
| } | |
| return vaultResp.Auth.ClientToken, nil | |
| } | |
| func main() { | |
| token, err := fetchSecretZero() | |
| if err != nil { | |
| log.Fatalf("Bootstrapping failed: %v", err) | |
| } | |
| fmt.Printf("Successfully acquired Vault Client Token: %s...\n", token[:8]) | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment