Skip to content

Commit 6765623

Browse files
whites11claude
andcommitted
refactor(flow): use GCP KMS client library instead of raw HTTP
Replace manual metadata token fetch + raw REST API calls with the official cloud.google.com/go/kms client library. The client handles authentication via Application Default Credentials (Workload Identity on GKE) automatically. This reduces ~50 lines of HTTP plumbing to ~15 lines of typed SDK calls and eliminates manual token management, JSON serialization, and error response parsing. Pinned cloud.google.com/go/kms at v1.24.0 to stay within the google.golang.org/api v0.257.0 constraint. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent db62df5 commit 6765623

3 files changed

Lines changed: 18 additions & 50 deletions

File tree

flow/go.mod

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@ require (
9696

9797
require github.com/quasilyte/go-ruleguard/dsl v0.3.23
9898

99+
require (
100+
cloud.google.com/go/kms v1.24.0 // PINNED: v1.26.0+ requires google.golang.org/api v0.265.0
101+
cloud.google.com/go/longrunning v0.7.0 // indirect
102+
)
103+
99104
require (
100105
cel.dev/expr v0.25.1 // indirect
101106
cloud.google.com/go/auth v0.18.1

flow/go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ cloud.google.com/go/datacatalog v1.26.1 h1:bCRKA8uSQN8wGW3Tw0gwko4E9a64GRmbW1nCb
1515
cloud.google.com/go/datacatalog v1.26.1/go.mod h1:2Qcq8vsHNxMDgjgadRFmFG47Y+uuIVsyEGUrlrKEdrg=
1616
cloud.google.com/go/iam v1.5.3 h1:+vMINPiDF2ognBJ97ABAYYwRgsaqxPbQDlMnbHMjolc=
1717
cloud.google.com/go/iam v1.5.3/go.mod h1:MR3v9oLkZCTlaqljW6Eb2d3HGDGK5/bDv93jhfISFvU=
18+
cloud.google.com/go/kms v1.24.0 h1:SWltUuoPhTdv9q/P0YEAWQfoYT32O5HdfPgTiWMvrH8=
19+
cloud.google.com/go/kms v1.24.0/go.mod h1:QDH3z2SJ50lfNOE8EokKC1G40i7I0f8xTMCoiptcb5g=
1820
cloud.google.com/go/logging v1.13.1 h1:O7LvmO0kGLaHY/gq8cV7T0dyp6zJhYAOtZPX4TF3QtY=
1921
cloud.google.com/go/logging v1.13.1/go.mod h1:XAQkfkMBxQRjQek96WLPNze7vsOmay9H5PqfsNYDqvw=
2022
cloud.google.com/go/longrunning v0.7.0 h1:FV0+SYF1RIj59gyoWDRi45GiYUMM3K1qO51qoboQT1E=

flow/internal/env.go

Lines changed: 11 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,14 @@ package internal
33
import (
44
"context"
55
"encoding/base64"
6-
"encoding/json"
76
"fmt"
8-
"io"
9-
"net/http"
107
"os"
118
"reflect"
129
"strconv"
13-
"strings"
1410
"sync"
1511

12+
gcpkms "cloud.google.com/go/kms/apiv1"
13+
gcpkmspb "cloud.google.com/go/kms/apiv1/kmspb"
1614
"github.com/aws/aws-sdk-go-v2/aws"
1715
"github.com/aws/aws-sdk-go-v2/config"
1816
"github.com/aws/aws-sdk-go-v2/service/kms"
@@ -103,58 +101,21 @@ func decryptWithAwsKms(ctx context.Context, data []byte, keyID string) ([]byte,
103101
}
104102

105103
func decryptWithGcpKms(ctx context.Context, data []byte, keyID string) ([]byte, error) {
106-
// Get access token from GKE Workload Identity metadata server
107-
req, err := http.NewRequestWithContext(ctx, http.MethodGet,
108-
"http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token", http.NoBody)
104+
client, err := gcpkms.NewKeyManagementClient(ctx)
109105
if err != nil {
110-
return nil, fmt.Errorf("failed to create metadata request: %w", err)
106+
return nil, fmt.Errorf("failed to create GCP KMS client: %w", err)
111107
}
112-
req.Header.Set("Metadata-Flavor", "Google")
108+
defer client.Close()
113109

114-
resp, err := http.DefaultClient.Do(req)
115-
if err != nil {
116-
return nil, fmt.Errorf("failed to get GCP access token: %w", err)
117-
}
118-
defer resp.Body.Close()
119-
120-
var tokenResp struct {
121-
AccessToken string `json:"access_token"`
122-
}
123-
if err := json.NewDecoder(resp.Body).Decode(&tokenResp); err != nil {
124-
return nil, fmt.Errorf("failed to parse GCP token response: %w", err)
125-
}
126-
127-
// Call Cloud KMS decrypt API
128-
ciphertext := base64.StdEncoding.EncodeToString(data)
129-
body := fmt.Sprintf(`{"ciphertext":"%s"}`, ciphertext)
130-
131-
kmsURL := fmt.Sprintf("https://cloudkms.googleapis.com/v1/%s:decrypt", keyID)
132-
kmsReq, err := http.NewRequestWithContext(ctx, http.MethodPost, kmsURL, strings.NewReader(body))
133-
if err != nil {
134-
return nil, fmt.Errorf("failed to create KMS request: %w", err)
135-
}
136-
kmsReq.Header.Set("Authorization", "Bearer "+tokenResp.AccessToken)
137-
kmsReq.Header.Set("Content-Type", "application/json")
138-
139-
kmsResp, err := http.DefaultClient.Do(kmsReq)
110+
resp, err := client.Decrypt(ctx, &gcpkmspb.DecryptRequest{
111+
Name: keyID,
112+
Ciphertext: data,
113+
})
140114
if err != nil {
141-
return nil, fmt.Errorf("failed to call GCP KMS: %w", err)
142-
}
143-
defer kmsResp.Body.Close()
144-
145-
if kmsResp.StatusCode != http.StatusOK {
146-
respBody, _ := io.ReadAll(kmsResp.Body)
147-
return nil, fmt.Errorf("GCP KMS decrypt failed (%d): %s", kmsResp.StatusCode, string(respBody))
148-
}
149-
150-
var kmsResult struct {
151-
Plaintext string `json:"plaintext"`
152-
}
153-
if err := json.NewDecoder(kmsResp.Body).Decode(&kmsResult); err != nil {
154-
return nil, fmt.Errorf("failed to parse GCP KMS response: %w", err)
115+
return nil, fmt.Errorf("failed to decrypt with GCP KMS: %w", err)
155116
}
156117

157-
return base64.StdEncoding.DecodeString(kmsResult.Plaintext)
118+
return resp.Plaintext, nil
158119
}
159120

160121
var kmsCache sync.Map

0 commit comments

Comments
 (0)