1
0
Fork 0
mirror of https://github.com/external-secrets/external-secrets.git synced 2024-12-14 11:57:59 +00:00

Refactor gcp secretmanager

* Create store client struct
* Add authentication method
* Use error handling constants
* Add GetSecretMap functionality
This commit is contained in:
Gabi 2021-05-12 14:34:10 +02:00
parent e19bb62a48
commit bf0e5c4c52

View file

@ -15,8 +15,8 @@ package secretmanager
import (
"context"
"encoding/json"
"fmt"
"log"
secretmanager "cloud.google.com/go/secretmanager/apiv1"
"github.com/googleapis/gax-go"
@ -25,7 +25,7 @@ import (
secretmanagerpb "google.golang.org/genproto/googleapis/cloud/secretmanager/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"sigs.k8s.io/controller-runtime/pkg/client"
kclient "sigs.k8s.io/controller-runtime/pkg/client"
esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
"github.com/external-secrets/external-secrets/pkg/provider"
@ -35,6 +35,18 @@ import (
const (
cloudPlatformRole = "https://www.googleapis.com/auth/cloud-platform"
defaultVersion = "latest"
errGCPSMStore = "received invalid GCPSM SecretStore resource"
errGCPSMCredSecretName = "invalid GCPSM SecretStore resource: missing GCP Secret Access Key"
errInvalidClusterStoreMissingSAKNamespace = "invalid ClusterSecretStore: missing GCP SecretAccessKey Namespace"
errFetchSAKSecret = "could not fetch SecretAccessKey secret: %w"
errMissingSAK = "missing SecretAccessKey"
errUnableProcessJSONCredentials = "failed to process the provided JSON credentials: %w"
errUnableCreateGCPSMClient = "failed to create GCP secretmanager client: %w"
errUninitalizedGCPProvider = "provider GCP is not initialized"
errClientGetSecretAccess = "unable to access Secret from SecretManager Client: %w"
errClientClose = "unable to close SecretManager client: %w"
errJSONSecretUnmarshal = "unable to unmarshal secret: %w"
)
type GoogleSecretManagerClient interface {
@ -48,47 +60,84 @@ type ProviderGCP struct {
SecretManagerClient GoogleSecretManagerClient
}
// NewClient constructs a GCP Provider.
func (sm *ProviderGCP) NewClient(ctx context.Context, store esv1alpha1.GenericStore, kube client.Client, namespace string) (provider.SecretsClient, error) {
// Fetch credential Secret
type gClient struct {
kube kclient.Client
store *esv1alpha1.GCPSMProvider
namespace string
storeKind string
credentials []byte
}
func (c *gClient) setAuth(ctx context.Context) error {
credentialsSecret := &corev1.Secret{}
credentialsSecretName := store.GetSpec().Provider.GCPSM.Auth.SecretRef.SecretAccessKey.Name
objectKey := types.NamespacedName{Name: credentialsSecretName, Namespace: store.GetNamespace()}
err := kube.Get(ctx, objectKey, credentialsSecret)
credentialsSecretName := c.store.Auth.SecretRef.SecretAccessKey.Name
if credentialsSecretName == "" {
return fmt.Errorf(errGCPSMCredSecretName)
}
objectKey := types.NamespacedName{
Name: credentialsSecretName,
Namespace: c.namespace,
}
// only ClusterStore is allowed to set namespace (and then it's required)
if c.storeKind == esv1alpha1.ClusterSecretStoreKind {
if c.store.Auth.SecretRef.SecretAccessKey.Namespace == nil {
return fmt.Errorf(errInvalidClusterStoreMissingSAKNamespace)
}
objectKey.Namespace = *c.store.Auth.SecretRef.SecretAccessKey.Namespace
}
err := c.kube.Get(ctx, objectKey, credentialsSecret)
if err != nil {
log.Panicf(err.Error(), "Failed to get credentials Secret")
return fmt.Errorf(errFetchSAKSecret, err)
}
c.credentials = credentialsSecret.Data[c.store.Auth.SecretRef.SecretAccessKey.Key]
if (c.credentials == nil) || (len(c.credentials) == 0) {
return fmt.Errorf(errMissingSAK)
}
return nil
}
// NewClient constructs a GCP Provider.
func (sm *ProviderGCP) NewClient(ctx context.Context, store esv1alpha1.GenericStore, kube kclient.Client, namespace string) (provider.SecretsClient, error) {
storeSpec := store.GetSpec()
if storeSpec == nil || storeSpec.Provider == nil || storeSpec.Provider.GCPSM == nil {
return nil, fmt.Errorf(errGCPSMStore)
}
storeSpecGCPSM := storeSpec.Provider.GCPSM
cliStore := gClient{
kube: kube,
store: storeSpecGCPSM,
namespace: namespace,
storeKind: store.GetObjectKind().GroupVersionKind().Kind,
}
if err := cliStore.setAuth(ctx); err != nil {
return nil, err
}
credentials := credentialsSecret.Data[store.GetSpec().Provider.GCPSM.Auth.SecretRef.SecretAccessKey.Key]
if len(credentials) == 0 {
return nil, fmt.Errorf("credentials GCP invalid/not provided")
}
sm.projectID = cliStore.store.ProjectID
projectID := store.GetSpec().Provider.GCPSM.ProjectID
sm.projectID = projectID
config, err := google.JWTConfigFromJSON(credentials, cloudPlatformRole)
config, err := google.JWTConfigFromJSON(cliStore.credentials, cloudPlatformRole)
if err != nil {
log.Panicf(err.Error(), "Failed to process the provided JSON credentials")
return nil, err
return nil, fmt.Errorf(errUnableProcessJSONCredentials, err)
}
ts := config.TokenSource(ctx)
client, err := secretmanager.NewClient(ctx, option.WithTokenSource(ts))
clientGCPSM, err := secretmanager.NewClient(ctx, option.WithTokenSource(ts))
if err != nil {
return nil, fmt.Errorf("failed to create GCP secretmanager client: %w", err)
return nil, fmt.Errorf(errUnableCreateGCPSMClient, err)
}
sm.SecretManagerClient = client
sm.SecretManagerClient = clientGCPSM
return sm, nil
}
// GetSecret returns a single secret from the provider.
func (sm *ProviderGCP) GetSecret(ctx context.Context, ref esv1alpha1.ExternalSecretDataRemoteRef) ([]byte, error) {
if sm.SecretManagerClient == nil || sm.projectID == "" {
return nil, fmt.Errorf("provider GCP is not initialized")
return nil, fmt.Errorf(errUninitalizedGCPProvider)
}
version := ref.Version
@ -96,31 +145,45 @@ func (sm *ProviderGCP) GetSecret(ctx context.Context, ref esv1alpha1.ExternalSec
version = defaultVersion
}
resourceName := fmt.Sprintf("projects/%s/secrets/%s/versions/%s", sm.projectID, ref.Key, version)
req := &secretmanagerpb.AccessSecretVersionRequest{
Name: resourceName,
Name: fmt.Sprintf("projects/%s/secrets/%s/versions/%s", sm.projectID, ref.Key, version),
}
result, err := sm.SecretManagerClient.AccessSecretVersion(ctx, req)
if err != nil {
return nil, err
return nil, fmt.Errorf(errClientGetSecretAccess, err)
}
err = sm.SecretManagerClient.Close()
if err != nil {
return nil, err
return nil, fmt.Errorf(errClientClose, err)
}
return []byte(string(result.Payload.Data)), nil
return result.Payload.Data, nil
}
// GetSecretMap returns multiple k/v pairs from the provider.
func (sm *ProviderGCP) GetSecretMap(ctx context.Context, ref esv1alpha1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
if sm.SecretManagerClient == nil || sm.projectID == "" {
return nil, fmt.Errorf("provider GCP is not initialized")
return nil, fmt.Errorf(errUninitalizedGCPProvider)
}
return map[string][]byte{
"noop": []byte("NOOP"),
}, nil
data, err := sm.GetSecret(ctx, ref)
if err != nil {
return nil, err
}
kv := make(map[string]string)
err = json.Unmarshal(data, &kv)
if err != nil {
return nil, fmt.Errorf(errJSONSecretUnmarshal, err)
}
secretData := make(map[string][]byte)
for k, v := range kv {
secretData[k] = []byte(v)
}
return secretData, nil
}
func init() {