Skip to content

Commit 93c3dff

Browse files
authored
Merge pull request #7 from pluralsh/workload-identity
feat: workload identity for kubeconfig credentials
2 parents 7b167fc + 44e8552 commit 93c3dff

File tree

4 files changed

+52
-46
lines changed

4 files changed

+52
-46
lines changed

cloud/scope/clients.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ func defaultClientOptions(ctx context.Context, credentialsRef *infrav1.ObjectRef
8282
if err != nil {
8383
return nil, fmt.Errorf("getting gcp credentials from reference %s: %w", credentialsRef, err)
8484
}
85-
opts = append(opts, option.WithCredentialsJSON(rawData))
85+
opts = append(opts, option.WithCredentialsJSON(rawData.JSON))
8686
}
8787

8888
return opts, nil

cloud/scope/credentials.go

Lines changed: 48 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ package scope
1818

1919
import (
2020
"context"
21-
"encoding/json"
2221
"fmt"
23-
"os"
2422

2523
"github.com/pkg/errors"
24+
"golang.org/x/oauth2"
25+
"golang.org/x/oauth2/google"
2626
corev1 "k8s.io/api/core/v1"
2727
"k8s.io/apimachinery/pkg/types"
2828
infrav1 "sigs.k8s.io/cluster-api-provider-gcp/api/v1beta1"
@@ -35,31 +35,53 @@ const (
3535
ConfigFileEnvVar = "GOOGLE_APPLICATION_CREDENTIALS"
3636
)
3737

38+
var (
39+
gcpScopes = []string{
40+
"https://www.googleapis.com/auth/cloud-platform",
41+
"https://www.googleapis.com/auth/userinfo.email",
42+
}
43+
)
44+
3845
// Credential is a struct to hold GCP credential data.
3946
type Credential struct {
40-
Type string `json:"type"`
41-
ProjectID string `json:"project_id"`
42-
ClientEmail string `json:"client_email"`
43-
ClientID string `json:"client_id"`
47+
token oauth2.TokenSource
48+
}
49+
50+
// GetToken returns the access token of the loaded GCP credentials.
51+
func (c *Credential) GetToken(ctx context.Context) (string, error) {
52+
token, err := c.token.Token()
53+
if err != nil {
54+
return "", err
55+
}
56+
return token.AccessToken, nil
4457
}
4558

4659
func getCredentials(ctx context.Context, credentialsRef *infrav1.ObjectReference, crClient client.Client) (*Credential, error) {
47-
var credentialData []byte
60+
var credential *google.Credentials
4861
var err error
4962

5063
if credentialsRef != nil {
51-
credentialData, err = getCredentialDataFromRef(ctx, credentialsRef, crClient)
64+
credential, err = getCredentialDataFromRef(ctx, credentialsRef, crClient)
5265
} else {
53-
credentialData, err = getCredentialDataUsingADC()
66+
credential, err = getCredentialDataUsingADC(ctx)
5467
}
5568
if err != nil {
5669
return nil, fmt.Errorf("getting credential data: %w", err)
5770
}
5871

59-
return parseCredential(credentialData)
72+
token := credential.TokenSource
73+
if token == nil {
74+
return nil, errors.New("failed retrieving token from credentials")
75+
}
76+
77+
credentials := &Credential{
78+
token: token,
79+
}
80+
81+
return credentials, nil
6082
}
6183

62-
func getCredentialDataFromRef(ctx context.Context, credentialsRef *infrav1.ObjectReference, crClient client.Client) ([]byte, error) {
84+
func getCredentialDataFromRef(ctx context.Context, credentialsRef *infrav1.ObjectReference, crClient client.Client) (*google.Credentials, error) {
6385
secretRefName := types.NamespacedName{
6486
Name: credentialsRef.Name,
6587
Namespace: credentialsRef.Namespace,
@@ -75,27 +97,25 @@ func getCredentialDataFromRef(ctx context.Context, credentialsRef *infrav1.Objec
7597
return nil, errors.New("no credentials key in secret")
7698
}
7799

78-
return rawData, nil
79-
}
80-
81-
func getCredentialDataUsingADC() ([]byte, error) {
82-
credsPath := os.Getenv(ConfigFileEnvVar)
83-
if credsPath == "" {
84-
return nil, fmt.Errorf("no ADC environment variable found for credentials (expect %s)", ConfigFileEnvVar)
85-
}
86-
87-
byteValue, err := os.ReadFile(credsPath) //nolint:gosec // We need to read a file here
100+
creds, err := google.CredentialsFromJSON(ctx, rawData, gcpScopes...)
88101
if err != nil {
89-
return nil, fmt.Errorf("reading credentials from file %s: %w", credsPath, err)
102+
return nil, fmt.Errorf("getting credentials from json: %w", err)
90103
}
91-
return byteValue, nil
104+
if creds == nil {
105+
return nil, errors.New("failed finding default credentials, cred is nil")
106+
}
107+
108+
return creds, nil
92109
}
93110

94-
func parseCredential(rawData []byte) (*Credential, error) {
95-
var credential Credential
96-
err := json.Unmarshal(rawData, &credential)
111+
func getCredentialDataUsingADC(ctx context.Context) (*google.Credentials, error) {
112+
creds, err := google.FindDefaultCredentials(ctx, gcpScopes...)
97113
if err != nil {
98-
return nil, err
114+
return nil, fmt.Errorf("getting credentials from json: %w", err)
99115
}
100-
return &credential, nil
116+
if creds == nil {
117+
return nil, errors.New("failed finding default credentials, cred is nil")
118+
}
119+
120+
return creds, nil
101121
}

cloud/services/container/clusters/kubeconfig.go

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import (
2222
"fmt"
2323

2424
"cloud.google.com/go/container/apiv1/containerpb"
25-
"cloud.google.com/go/iam/credentials/apiv1/credentialspb"
2625
"github.com/go-logr/logr"
2726
"github.com/pkg/errors"
2827
corev1 "k8s.io/api/core/v1"
@@ -147,7 +146,7 @@ func (s *Service) createCAPIKubeconfigSecret(ctx context.Context, cluster *conta
147146
return fmt.Errorf("creating base kubeconfig: %w", err)
148147
}
149148

150-
token, err := s.generateToken(ctx)
149+
token, err := s.scope.GetCredential().GetToken(ctx)
151150
if err != nil {
152151
log.Error(err, "failed generating token")
153152
return err
@@ -184,7 +183,7 @@ func (s *Service) updateCAPIKubeconfigSecret(ctx context.Context, configSecret *
184183
return errors.Wrap(err, "failed to convert kubeconfig Secret into a clientcmdapi.Config")
185184
}
186185

187-
token, err := s.generateToken(ctx)
186+
token, err := s.scope.GetCredential().GetToken(ctx)
188187
if err != nil {
189188
return err
190189
}
@@ -239,17 +238,3 @@ func (s *Service) createBaseKubeConfig(contextName string, cluster *containerpb.
239238

240239
return cfg, nil
241240
}
242-
243-
func (s *Service) generateToken(ctx context.Context) (string, error) {
244-
req := &credentialspb.GenerateAccessTokenRequest{
245-
Name: fmt.Sprintf("projects/-/serviceAccounts/%s", s.scope.GetCredential().ClientEmail),
246-
Scope: []string{
247-
GkeScope,
248-
},
249-
}
250-
resp, err := s.scope.CredentialsClient().GenerateAccessToken(ctx, req)
251-
if err != nil {
252-
return "", errors.Errorf("error generating access token: %v", err)
253-
}
254-
return resp.AccessToken, nil
255-
}

cloud/services/container/clusters/reconcile.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ func (s *Service) createCluster(ctx context.Context, log *logr.Logger) error {
263263
WorkloadIdentityConfig: s.createWorkloadIdentityConfig(),
264264
NetworkConfig: s.createNetworkConfig(),
265265
AddonsConfig: s.createAddonsConfig(),
266+
ResourceLabels: s.scope.GCPManagedCluster.Labels,
266267
}
267268

268269
if s.scope.GCPManagedControlPlane.Spec.ControlPlaneVersion != nil {

0 commit comments

Comments
 (0)