Explorar o código

Fix the leak in GCPSM when the secret operator cannot find the secret. (#722)

* fix(gcp): Fix the leak in GCPSM when the secret operator cannot find the secret.

The IAM client has an internal gRPC connection,
but if the secret fetch fails, the goroutine created by the gRPC connection will leak.

Therefore, close the IAM client when the creation of the GCPSM client fails.

* test: fix build error on fakeIAMClient
castaneai %!s(int64=4) %!d(string=hai) anos
pai
achega
3fd3cc0186

+ 15 - 0
pkg/provider/gcp/secretmanager/secretsmanager.go

@@ -63,6 +63,7 @@ type GoogleSecretManagerClient interface {
 type ProviderGCP struct {
 type ProviderGCP struct {
 	projectID           string
 	projectID           string
 	SecretManagerClient GoogleSecretManagerClient
 	SecretManagerClient GoogleSecretManagerClient
+	gClient             *gClient
 }
 }
 
 
 type gClient struct {
 type gClient struct {
@@ -86,6 +87,10 @@ func (c *gClient) getTokenSource(ctx context.Context, store esv1alpha1.GenericSt
 	return google.DefaultTokenSource(ctx, CloudPlatformRole)
 	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) {
 func serviceAccountTokenSource(ctx context.Context, store esv1alpha1.GenericStore, kube kclient.Client, namespace string) (oauth2.TokenSource, error) {
 	spec := store.GetSpec()
 	spec := store.GetSpec()
 	if spec == nil || spec.Provider.GCPSM == nil {
 	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,
 		storeKind:        store.GetObjectKind().GroupVersionKind().Kind,
 		workloadIdentity: wi,
 		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
 	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 {
 func (sm *ProviderGCP) Close(ctx context.Context) error {
 	err := sm.SecretManagerClient.Close()
 	err := sm.SecretManagerClient.Close()
+	if sm.gClient != nil {
+		err = sm.gClient.Close()
+	}
 	if err != nil {
 	if err != nil {
 		return fmt.Errorf(errClientClose, err)
 		return fmt.Errorf(errClientClose, err)
 	}
 	}

+ 5 - 0
pkg/provider/gcp/secretmanager/secretsmanager_workload_identity.go

@@ -62,6 +62,7 @@ type workloadIdentity struct {
 // interface to GCP IAM API.
 // interface to GCP IAM API.
 type IamClient interface {
 type IamClient interface {
 	GenerateAccessToken(ctx context.Context, req *credentialspb.GenerateAccessTokenRequest, opts ...gax.CallOption) (*credentialspb.GenerateAccessTokenResponse, error)
 	GenerateAccessToken(ctx context.Context, req *credentialspb.GenerateAccessTokenRequest, opts ...gax.CallOption) (*credentialspb.GenerateAccessTokenResponse, error)
+	Close() error
 }
 }
 
 
 // interface to securetoken/identitybindingtoken API.
 // interface to securetoken/identitybindingtoken API.
@@ -154,6 +155,10 @@ func (w *workloadIdentity) TokenSource(ctx context.Context, store esv1alpha1.Gen
 	}), nil
 	}), nil
 }
 }
 
 
+func (w *workloadIdentity) Close() error {
+	return w.iamClient.Close()
+}
+
 func newIAMClient(ctx context.Context) (IamClient, error) {
 func newIAMClient(ctx context.Context) (IamClient, error) {
 	iamOpts := []option.ClientOption{
 	iamOpts := []option.ClientOption{
 		option.WithUserAgent("external-secrets-operator"),
 		option.WithUserAgent("external-secrets-operator"),

+ 4 - 0
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...)
 	return f.generateAccessTokenFunc(ctx, req, opts...)
 }
 }
 
 
+func (f *fakeIAMClient) Close() error {
+	return nil
+}
+
 // fake SA Token Generator.
 // fake SA Token Generator.
 type fakeSATokenGen struct {
 type fakeSATokenGen struct {
 	GenerateFunc func(context.Context, string, string, string) (*authv1.TokenRequest, error)
 	GenerateFunc func(context.Context, string, string, string) (*authv1.TokenRequest, error)