diff --git a/pkg/provider/gcp/secretmanager/secretsmanager.go b/pkg/provider/gcp/secretmanager/secretsmanager.go index 87ad28883..a6400806d 100644 --- a/pkg/provider/gcp/secretmanager/secretsmanager.go +++ b/pkg/provider/gcp/secretmanager/secretsmanager.go @@ -63,6 +63,7 @@ type GoogleSecretManagerClient interface { type ProviderGCP struct { projectID string SecretManagerClient GoogleSecretManagerClient + gClient *gClient } type gClient struct { @@ -86,6 +87,10 @@ func (c *gClient) getTokenSource(ctx context.Context, store esv1alpha1.GenericSt return google.DefaultTokenSource(ctx, CloudPlatformRole) } +func (c *gClient) Close() error { + return c.workloadIdentity.Close() +} + func serviceAccountTokenSource(ctx context.Context, store esv1alpha1.GenericStore, kube kclient.Client, namespace string) (oauth2.TokenSource, error) { spec := store.GetSpec() if spec == nil || spec.Provider.GCPSM == nil { @@ -146,6 +151,13 @@ func (sm *ProviderGCP) NewClient(ctx context.Context, store esv1alpha1.GenericSt storeKind: store.GetObjectKind().GroupVersionKind().Kind, workloadIdentity: wi, } + sm.gClient = &cliStore + defer func() { + // closes IAMClient to prevent gRPC connection leak in case of an error. + if sm.SecretManagerClient == nil { + _ = sm.gClient.Close() + } + }() sm.projectID = cliStore.store.ProjectID @@ -239,6 +251,9 @@ func (sm *ProviderGCP) GetSecretMap(ctx context.Context, ref esv1alpha1.External func (sm *ProviderGCP) Close(ctx context.Context) error { err := sm.SecretManagerClient.Close() + if sm.gClient != nil { + err = sm.gClient.Close() + } if err != nil { return fmt.Errorf(errClientClose, err) } diff --git a/pkg/provider/gcp/secretmanager/secretsmanager_workload_identity.go b/pkg/provider/gcp/secretmanager/secretsmanager_workload_identity.go index 9604871da..a38ace701 100644 --- a/pkg/provider/gcp/secretmanager/secretsmanager_workload_identity.go +++ b/pkg/provider/gcp/secretmanager/secretsmanager_workload_identity.go @@ -62,6 +62,7 @@ type workloadIdentity struct { // interface to GCP IAM API. type IamClient interface { GenerateAccessToken(ctx context.Context, req *credentialspb.GenerateAccessTokenRequest, opts ...gax.CallOption) (*credentialspb.GenerateAccessTokenResponse, error) + Close() error } // interface to securetoken/identitybindingtoken API. @@ -154,6 +155,10 @@ func (w *workloadIdentity) TokenSource(ctx context.Context, store esv1alpha1.Gen }), nil } +func (w *workloadIdentity) Close() error { + return w.iamClient.Close() +} + func newIAMClient(ctx context.Context) (IamClient, error) { iamOpts := []option.ClientOption{ option.WithUserAgent("external-secrets-operator"), diff --git a/pkg/provider/gcp/secretmanager/secretsmanager_workload_identity_test.go b/pkg/provider/gcp/secretmanager/secretsmanager_workload_identity_test.go index 9409c9400..fa5582291 100644 --- a/pkg/provider/gcp/secretmanager/secretsmanager_workload_identity_test.go +++ b/pkg/provider/gcp/secretmanager/secretsmanager_workload_identity_test.go @@ -357,6 +357,10 @@ func (f *fakeIAMClient) GenerateAccessToken(ctx context.Context, req *credential return f.generateAccessTokenFunc(ctx, req, opts...) } +func (f *fakeIAMClient) Close() error { + return nil +} + // fake SA Token Generator. type fakeSATokenGen struct { GenerateFunc func(context.Context, string, string, string) (*authv1.TokenRequest, error)