| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786 |
- /*
- Copyright © The ESO Authors
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- https://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- // Package ibm provides integration with IBM Cloud Secrets Manager
- // for External Secrets Operator.
- package ibm
- import (
- "context"
- "encoding/json"
- "errors"
- "fmt"
- "os"
- "strings"
- "time"
- "github.com/IBM/go-sdk-core/v5/core"
- sm "github.com/IBM/secrets-manager-go-sdk/v2/secretsmanagerv2"
- "github.com/google/uuid"
- "github.com/tidwall/gjson"
- corev1 "k8s.io/api/core/v1"
- kclient "sigs.k8s.io/controller-runtime/pkg/client"
- "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
- esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
- "github.com/external-secrets/external-secrets/runtime/constants"
- "github.com/external-secrets/external-secrets/runtime/esutils"
- "github.com/external-secrets/external-secrets/runtime/esutils/resolvers"
- "github.com/external-secrets/external-secrets/runtime/metrics"
- )
- const (
- certificateConst = "certificate"
- intermediateConst = "intermediate"
- privateKeyConst = "private_key"
- usernameConst = "username"
- passwordConst = "password"
- apikeyConst = "apikey"
- credentialsConst = "credentials"
- arbitraryConst = "arbitrary"
- payloadConst = "payload"
- smAPIKeyConst = "api_key"
- errIBMClient = "cannot setup new ibm client: %w"
- errUninitializedIBMProvider = "provider IBM is not initialized"
- errJSONSecretUnmarshal = "unable to unmarshal secret from JSON: %w"
- errJSONSecretMarshal = "unable to marshal secret to JSON: %w"
- errExtractingSecret = "unable to extract the fetched secret %s of type %s while performing %s"
- errNotImplemented = "not implemented"
- errKeyDoesNotExist = "key %s does not exist in secret %s"
- errFieldIsEmpty = "warn: %s is empty for secret %s\n"
- iamDefaultEndpoint = "https://iam.cloud.ibm.com"
- )
- var contextTimeout = time.Minute * 2
- // https://github.com/external-secrets/external-secrets/issues/644
- var (
- _ esv1.SecretsClient = &providerIBM{}
- _ esv1.Provider = &providerIBM{}
- )
- // SecretManagerClient defines the interface for interacting with IBM Cloud Secrets Manager.
- type SecretManagerClient interface {
- GetSecretWithContext(ctx context.Context, getSecretOptions *sm.GetSecretOptions) (result sm.SecretIntf, response *core.DetailedResponse, err error)
- GetSecretByNameTypeWithContext(ctx context.Context, getSecretByNameTypeOptions *sm.GetSecretByNameTypeOptions) (result sm.SecretIntf, response *core.DetailedResponse, err error)
- }
- type providerIBM struct {
- IBMClient SecretManagerClient
- }
- type client struct {
- kube kclient.Client
- store *esv1.IBMProvider
- namespace string
- storeKind string
- credentials []byte
- }
- func (c *client) setAuth(ctx context.Context) error {
- apiKey, err := resolvers.SecretKeyRef(ctx, c.kube, c.storeKind, c.namespace, &c.store.Auth.SecretRef.SecretAPIKey)
- if err != nil {
- return err
- }
- c.credentials = []byte(apiKey)
- return nil
- }
- func (ibm *providerIBM) DeleteSecret(_ context.Context, _ esv1.PushSecretRemoteRef) error {
- return errors.New(errNotImplemented)
- }
- func (ibm *providerIBM) SecretExists(_ context.Context, _ esv1.PushSecretRemoteRef) (bool, error) {
- return false, errors.New(errNotImplemented)
- }
- // PushSecret not implemented.
- func (ibm *providerIBM) PushSecret(_ context.Context, _ *corev1.Secret, _ esv1.PushSecretData) error {
- return errors.New(errNotImplemented)
- }
- // GetAllSecrets empty.
- func (ibm *providerIBM) GetAllSecrets(_ context.Context, _ esv1.ExternalSecretFind) (map[string][]byte, error) {
- // TO be implemented
- return nil, errors.New(errNotImplemented)
- }
- func (ibm *providerIBM) GetSecret(_ context.Context, ref esv1.ExternalSecretDataRemoteRef) ([]byte, error) {
- if esutils.IsNil(ibm.IBMClient) {
- return nil, errors.New(errUninitializedIBMProvider)
- }
- secretGroupName, secretType, secretName := parseSecretReference(ref.Key)
- return ibm.getSecretByType(secretType, secretName, secretGroupName, ref)
- }
- func parseSecretReference(key string) (string, string, string) {
- var secretGroupName string
- secretType := sm.Secret_SecretType_Arbitrary
- secretName := key
- nameSplitted := strings.Split(key, "/")
- switch len(nameSplitted) {
- case 2:
- secretType = nameSplitted[0]
- secretName = nameSplitted[1]
- case 3:
- secretGroupName = nameSplitted[0]
- secretType = nameSplitted[1]
- secretName = nameSplitted[2]
- }
- return secretGroupName, secretType, secretName
- }
- func (ibm *providerIBM) getSecretByType(secretType, secretName, secretGroupName string, ref esv1.ExternalSecretDataRemoteRef) ([]byte, error) {
- switch secretType {
- case sm.Secret_SecretType_Arbitrary:
- return getArbitrarySecret(ibm, &secretName, secretGroupName)
- case sm.Secret_SecretType_UsernamePassword:
- return ibm.getUsernamePasswordSecretWithValidation(&secretName, ref, secretGroupName)
- case sm.Secret_SecretType_IamCredentials:
- return getIamCredentialsSecret(ibm, &secretName, secretGroupName)
- case sm.Secret_SecretType_ServiceCredentials:
- return getServiceCredentialsSecret(ibm, &secretName, secretGroupName)
- case sm.Secret_SecretType_ImportedCert:
- return ibm.getImportCertSecretWithValidation(&secretName, ref, secretGroupName)
- case sm.Secret_SecretType_PublicCert:
- return ibm.getPublicCertSecretWithValidation(&secretName, ref, secretGroupName)
- case sm.Secret_SecretType_PrivateCert:
- return ibm.getPrivateCertSecretWithValidation(&secretName, ref, secretGroupName)
- case sm.Secret_SecretType_Kv:
- return ibm.getKVSecret(&secretName, secretGroupName, ref)
- case sm.Secret_SecretType_CustomCredentials:
- return ibm.getCustomCredentialsSecret(&secretName, secretGroupName, ref)
- default:
- return nil, fmt.Errorf("unknown secret type %s", secretType)
- }
- }
- func (ibm *providerIBM) getUsernamePasswordSecretWithValidation(secretName *string, ref esv1.ExternalSecretDataRemoteRef, secretGroupName string) ([]byte, error) {
- if ref.Property == "" {
- return nil, errors.New("remoteRef.property required for secret type username_password")
- }
- return getUsernamePasswordSecret(ibm, secretName, ref, secretGroupName)
- }
- func (ibm *providerIBM) getImportCertSecretWithValidation(secretName *string, ref esv1.ExternalSecretDataRemoteRef, secretGroupName string) ([]byte, error) {
- if ref.Property == "" {
- return nil, errors.New("remoteRef.property required for secret type imported_cert")
- }
- return getImportCertSecret(ibm, secretName, ref, secretGroupName)
- }
- func (ibm *providerIBM) getPublicCertSecretWithValidation(secretName *string, ref esv1.ExternalSecretDataRemoteRef, secretGroupName string) ([]byte, error) {
- if ref.Property == "" {
- return nil, errors.New("remoteRef.property required for secret type public_cert")
- }
- return getPublicCertSecret(ibm, secretName, ref, secretGroupName)
- }
- func (ibm *providerIBM) getPrivateCertSecretWithValidation(secretName *string, ref esv1.ExternalSecretDataRemoteRef, secretGroupName string) ([]byte, error) {
- if ref.Property == "" {
- return nil, errors.New("remoteRef.property required for secret type private_cert")
- }
- return getPrivateCertSecret(ibm, secretName, ref, secretGroupName)
- }
- func (ibm *providerIBM) getKVSecret(secretName *string, secretGroupName string, ref esv1.ExternalSecretDataRemoteRef) ([]byte, error) {
- response, err := getSecretData(ibm, secretName, sm.Secret_SecretType_Kv, secretGroupName)
- if err != nil {
- return nil, err
- }
- secret, ok := response.(*sm.KVSecret)
- if !ok {
- return nil, fmt.Errorf(errExtractingSecret, *secretName, sm.Secret_SecretType_Kv, "GetSecret")
- }
- return getKVOrCustomCredentialsSecret(ref, secret.Data)
- }
- func (ibm *providerIBM) getCustomCredentialsSecret(secretName *string, secretGroupName string, ref esv1.ExternalSecretDataRemoteRef) ([]byte, error) {
- response, err := getSecretData(ibm, secretName, sm.Secret_SecretType_CustomCredentials, secretGroupName)
- if err != nil {
- return nil, err
- }
- secret, ok := response.(*sm.CustomCredentialsSecret)
- if !ok {
- return nil, fmt.Errorf(errExtractingSecret, *secretName, sm.Secret_SecretType_CustomCredentials, "GetSecret")
- }
- return getKVOrCustomCredentialsSecret(ref, secret.CredentialsContent)
- }
- func getArbitrarySecret(ibm *providerIBM, secretName *string, secretGroupName string) ([]byte, error) {
- response, err := getSecretData(ibm, secretName, sm.Secret_SecretType_Arbitrary, secretGroupName)
- if err != nil {
- return nil, err
- }
- secMap, err := formSecretMap(response)
- if err != nil {
- return nil, err
- }
- if val, ok := secMap[payloadConst]; ok {
- return []byte(val.(string)), nil
- }
- return nil, fmt.Errorf(errKeyDoesNotExist, payloadConst, *secretName)
- }
- func getImportCertSecret(ibm *providerIBM, secretName *string, ref esv1.ExternalSecretDataRemoteRef, secretGroupName string) ([]byte, error) {
- response, err := getSecretData(ibm, secretName, sm.Secret_SecretType_ImportedCert, secretGroupName)
- if err != nil {
- return nil, err
- }
- secMap, err := formSecretMap(response)
- if err != nil {
- return nil, err
- }
- val, ok := secMap[ref.Property]
- if ok {
- return []byte(val.(string)), nil
- } else if ref.Property == intermediateConst {
- // we want to return an empty string in case the secret doesn't contain an intermediate certificate
- // this is to ensure that secret of type 'kubernetes.io/tls' gets created as expected, even with an empty intermediate certificate
- fmt.Printf(errFieldIsEmpty, intermediateConst, *secretName)
- return []byte(""), nil
- } else if ref.Property == privateKeyConst {
- // we want to return an empty string in case the secret doesn't contain a private key
- // this is to ensure that secret of type 'kubernetes.io/tls' gets created as expected, even with an empty private key
- fmt.Printf(errFieldIsEmpty, privateKeyConst, *secretName)
- return []byte(""), nil
- }
- return nil, fmt.Errorf(errKeyDoesNotExist, ref.Property, ref.Key)
- }
- func getPublicCertSecret(ibm *providerIBM, secretName *string, ref esv1.ExternalSecretDataRemoteRef, secretGroupName string) ([]byte, error) {
- response, err := getSecretData(ibm, secretName, sm.Secret_SecretType_PublicCert, secretGroupName)
- if err != nil {
- return nil, err
- }
- secMap, err := formSecretMap(response)
- if err != nil {
- return nil, err
- }
- if val, ok := secMap[ref.Property]; ok {
- return []byte(val.(string)), nil
- }
- return nil, fmt.Errorf(errKeyDoesNotExist, ref.Property, ref.Key)
- }
- func getPrivateCertSecret(ibm *providerIBM, secretName *string, ref esv1.ExternalSecretDataRemoteRef, secretGroupName string) ([]byte, error) {
- response, err := getSecretData(ibm, secretName, sm.Secret_SecretType_PrivateCert, secretGroupName)
- if err != nil {
- return nil, err
- }
- secMap, err := formSecretMap(response)
- if err != nil {
- return nil, err
- }
- if val, ok := secMap[ref.Property]; ok {
- return []byte(val.(string)), nil
- }
- return nil, fmt.Errorf(errKeyDoesNotExist, ref.Property, ref.Key)
- }
- func getIamCredentialsSecret(ibm *providerIBM, secretName *string, secretGroupName string) ([]byte, error) {
- response, err := getSecretData(ibm, secretName, sm.Secret_SecretType_IamCredentials, secretGroupName)
- if err != nil {
- return nil, err
- }
- secMap, err := formSecretMap(response)
- if err != nil {
- return nil, err
- }
- if val, ok := secMap[smAPIKeyConst]; ok {
- return []byte(val.(string)), nil
- }
- return nil, fmt.Errorf(errKeyDoesNotExist, smAPIKeyConst, *secretName)
- }
- func getServiceCredentialsSecret(ibm *providerIBM, secretName *string, secretGroupName string) ([]byte, error) {
- response, err := getSecretData(ibm, secretName, sm.Secret_SecretType_ServiceCredentials, secretGroupName)
- if err != nil {
- return nil, err
- }
- secMap, err := formSecretMap(response)
- if err != nil {
- return nil, err
- }
- if val, ok := secMap[credentialsConst]; ok {
- mval, err := json.Marshal(val)
- if err != nil {
- return nil, fmt.Errorf("failed to marshal secret map for service credentials secret: %w", err)
- }
- return mval, nil
- }
- return nil, fmt.Errorf(errKeyDoesNotExist, credentialsConst, *secretName)
- }
- func getUsernamePasswordSecret(ibm *providerIBM, secretName *string, ref esv1.ExternalSecretDataRemoteRef, secretGroupName string) ([]byte, error) {
- response, err := getSecretData(ibm, secretName, sm.Secret_SecretType_UsernamePassword, secretGroupName)
- if err != nil {
- return nil, err
- }
- secMap, err := formSecretMap(response)
- if err != nil {
- return nil, err
- }
- if val, ok := secMap[ref.Property]; ok {
- return []byte(val.(string)), nil
- }
- return nil, fmt.Errorf(errKeyDoesNotExist, ref.Property, ref.Key)
- }
- // Returns a secret of type kv or custom credentials and supports json path.
- func getKVOrCustomCredentialsSecret(ref esv1.ExternalSecretDataRemoteRef, credentialsData map[string]any) ([]byte, error) {
- payloadJSONByte, err := json.Marshal(credentialsData)
- if err != nil {
- return nil, fmt.Errorf("marshaling payload from secret failed. %w", err)
- }
- payloadJSON := string(payloadJSONByte)
- // no property requested, return the entire payload
- if ref.Property == "" {
- return []byte(payloadJSON), nil
- }
- // returns the requested key
- // consider that the key contains a ".". this could be one of 2 options
- // a) "." is part of the key name
- // b) "." is symbole for JSON path
- if ref.Property != "" {
- refProperty := ref.Property
- // a) "." is part the key name
- // escape "."
- idx := strings.Index(refProperty, ".")
- if idx > 0 {
- refProperty = strings.ReplaceAll(refProperty, ".", "\\.")
- val := gjson.Get(payloadJSON, refProperty)
- if val.Exists() {
- return []byte(val.String()), nil
- }
- }
- // b) "." is symbole for JSON path
- // try to get value for this path
- val := gjson.Get(payloadJSON, ref.Property)
- if !val.Exists() {
- return nil, fmt.Errorf(errKeyDoesNotExist, ref.Property, ref.Key)
- }
- return []byte(val.String()), nil
- }
- return nil, fmt.Errorf("no property provided for secret %s", ref.Key)
- }
- func getSecretData(ibm *providerIBM, secretName *string, secretType, secretGroupName string) (sm.SecretIntf, error) {
- _, err := uuid.Parse(*secretName)
- if err != nil {
- // secret name has been provided instead of id
- if secretGroupName == "" {
- // secret group name is not provided
- return nil, errors.New("failed to fetch the secret, secret group name is missing")
- }
- // secret group name is provided along with secret name,
- // follow the new mechanism by calling GetSecretByNameTypeWithContext
- ctx, cancel := context.WithTimeout(context.Background(), contextTimeout)
- defer cancel()
- response, _, err := ibm.IBMClient.GetSecretByNameTypeWithContext(
- ctx,
- &sm.GetSecretByNameTypeOptions{
- Name: secretName,
- SecretGroupName: &secretGroupName,
- SecretType: &secretType,
- })
- metrics.ObserveAPICall(constants.ProviderIBMSM, constants.CallIBMSMGetSecretByNameType, err)
- if err != nil {
- return nil, err
- }
- return response, nil
- }
- ctx, cancel := context.WithTimeout(context.Background(), contextTimeout)
- defer cancel()
- response, _, err := ibm.IBMClient.GetSecretWithContext(
- ctx,
- &sm.GetSecretOptions{
- ID: secretName,
- })
- metrics.ObserveAPICall(constants.ProviderIBMSM, constants.CallIBMSMGetSecret, err)
- if err != nil {
- return nil, err
- }
- return response, nil
- }
- func (ibm *providerIBM) GetSecretMap(_ context.Context, ref esv1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
- if esutils.IsNil(ibm.IBMClient) {
- return nil, errors.New(errUninitializedIBMProvider)
- }
- var secretGroupName string
- secretType := sm.Secret_SecretType_Arbitrary
- secretName := ref.Key
- nameSplitted := strings.Split(secretName, "/")
- switch len(nameSplitted) {
- case 2:
- secretType = nameSplitted[0]
- secretName = nameSplitted[1]
- case 3:
- secretGroupName = nameSplitted[0]
- secretType = nameSplitted[1]
- secretName = nameSplitted[2]
- }
- secretMap := make(map[string][]byte)
- secMapBytes := make(map[string][]byte)
- response, err := getSecretData(ibm, &secretName, secretType, secretGroupName)
- if err != nil {
- return nil, err
- }
- secMap, err := formSecretMap(response)
- if err != nil {
- return nil, err
- }
- if ref.MetadataPolicy == esv1.ExternalSecretMetadataPolicyFetch {
- secretMap = populateSecretMap(secretMap, secMap)
- }
- secMapBytes = populateSecretMap(secMapBytes, secMap)
- checkNilFn := func(propertyList []string) error {
- for _, prop := range propertyList {
- if _, ok := secMap[prop]; !ok {
- return fmt.Errorf(errKeyDoesNotExist, prop, secretName)
- }
- }
- return nil
- }
- switch secretType {
- case sm.Secret_SecretType_Arbitrary:
- if err := checkNilFn([]string{payloadConst}); err != nil {
- return nil, err
- }
- secretMap[arbitraryConst] = secMapBytes[payloadConst]
- return secretMap, nil
- case sm.Secret_SecretType_UsernamePassword:
- if err := checkNilFn([]string{usernameConst, passwordConst}); err != nil {
- return nil, err
- }
- secretMap[usernameConst] = secMapBytes[usernameConst]
- secretMap[passwordConst] = secMapBytes[passwordConst]
- return secretMap, nil
- case sm.Secret_SecretType_IamCredentials:
- if err := checkNilFn([]string{smAPIKeyConst}); err != nil {
- return nil, err
- }
- secretMap[apikeyConst] = secMapBytes[smAPIKeyConst]
- return secretMap, nil
- case sm.Secret_SecretType_ServiceCredentials:
- if err := checkNilFn([]string{credentialsConst}); err != nil {
- return nil, err
- }
- secretMap[credentialsConst] = secMapBytes[credentialsConst]
- return secretMap, nil
- case sm.Secret_SecretType_ImportedCert:
- if err := checkNilFn([]string{certificateConst}); err != nil {
- return nil, err
- }
- secretMap[certificateConst] = secMapBytes[certificateConst]
- if v1, ok := secMapBytes[intermediateConst]; ok {
- secretMap[intermediateConst] = v1
- } else {
- fmt.Printf(errFieldIsEmpty, intermediateConst, secretName)
- secretMap[intermediateConst] = []byte("")
- }
- if v2, ok := secMapBytes[privateKeyConst]; ok {
- secretMap[privateKeyConst] = v2
- } else {
- fmt.Printf(errFieldIsEmpty, privateKeyConst, secretName)
- secretMap[privateKeyConst] = []byte("")
- }
- return secretMap, nil
- case sm.Secret_SecretType_PublicCert:
- if err := checkNilFn([]string{certificateConst, intermediateConst, privateKeyConst}); err != nil {
- return nil, err
- }
- secretMap[certificateConst] = secMapBytes[certificateConst]
- secretMap[intermediateConst] = secMapBytes[intermediateConst]
- secretMap[privateKeyConst] = secMapBytes[privateKeyConst]
- return secretMap, nil
- case sm.Secret_SecretType_PrivateCert:
- if err := checkNilFn([]string{certificateConst, privateKeyConst}); err != nil {
- return nil, err
- }
- secretMap[certificateConst] = secMapBytes[certificateConst]
- secretMap[privateKeyConst] = secMapBytes[privateKeyConst]
- return secretMap, nil
- case sm.Secret_SecretType_Kv:
- secretData, ok := response.(*sm.KVSecret)
- if !ok {
- return nil, fmt.Errorf(errExtractingSecret, secretName, sm.Secret_SecretType_Kv, "GetSecretMap")
- }
- secret, err := getKVOrCustomCredentialsSecret(ref, secretData.Data)
- if err != nil {
- return nil, err
- }
- m := make(map[string]any)
- err = json.Unmarshal(secret, &m)
- if err != nil {
- return nil, fmt.Errorf(errJSONSecretUnmarshal, err)
- }
- secretMap = byteArrayMap(m, secretMap)
- return secretMap, nil
- case sm.Secret_SecretType_CustomCredentials:
- secretData, ok := response.(*sm.CustomCredentialsSecret)
- if !ok {
- return nil, fmt.Errorf(errExtractingSecret, secretName, sm.Secret_SecretType_CustomCredentials, "GetSecretMap")
- }
- secret, err := getKVOrCustomCredentialsSecret(ref, secretData.CredentialsContent)
- if err != nil {
- return nil, err
- }
- m := make(map[string]any)
- err = json.Unmarshal(secret, &m)
- if err != nil {
- return nil, fmt.Errorf(errJSONSecretUnmarshal, err)
- }
- secretMap = byteArrayMap(m, secretMap)
- return secretMap, nil
- default:
- return nil, fmt.Errorf("unknown secret type %s", secretType)
- }
- }
- func byteArrayMap(secretData map[string]any, secretMap map[string][]byte) map[string][]byte {
- var err error
- for k, v := range secretData {
- secretMap[k], err = esutils.GetByteValue(v)
- if err != nil {
- return nil
- }
- }
- return secretMap
- }
- func (ibm *providerIBM) Close(_ context.Context) error {
- return nil
- }
- func (ibm *providerIBM) Validate() (esv1.ValidationResult, error) {
- return esv1.ValidationResultReady, nil
- }
- func (ibm *providerIBM) ValidateStore(store esv1.GenericStore) (admission.Warnings, error) {
- storeSpec := store.GetSpec()
- ibmSpec := storeSpec.Provider.IBM
- if ibmSpec.ServiceURL == nil {
- return nil, errors.New("serviceURL is required")
- }
- containerRef := ibmSpec.Auth.ContainerAuth
- secretRef := ibmSpec.Auth.SecretRef
- missingContainerRef := esutils.IsNil(containerRef)
- missingSecretRef := esutils.IsNil(secretRef)
- if missingContainerRef == missingSecretRef {
- // since both are equal, if one is missing assume both are missing
- if missingContainerRef {
- return nil, errors.New("missing auth method")
- }
- return nil, errors.New("too many auth methods defined")
- }
- if !missingContainerRef {
- // catch undefined container auth profile
- if containerRef.Profile == "" {
- return nil, errors.New("container auth profile cannot be empty")
- }
- // proceed with container auth
- if containerRef.TokenLocation == "" {
- containerRef.TokenLocation = "/var/run/secrets/tokens/vault-token"
- }
- if _, err := os.Open(containerRef.TokenLocation); err != nil {
- return nil, fmt.Errorf("cannot read container auth token %s. %w", containerRef.TokenLocation, err)
- }
- return nil, nil
- }
- // proceed with API Key Auth validation
- secretKeyRef := secretRef.SecretAPIKey
- err := esutils.ValidateSecretSelector(store, secretKeyRef)
- if err != nil {
- return nil, err
- }
- if secretKeyRef.Name == "" {
- return nil, errors.New("secretAPIKey.name cannot be empty")
- }
- if secretKeyRef.Key == "" {
- return nil, errors.New("secretAPIKey.key cannot be empty")
- }
- return nil, nil
- }
- // Capabilities return the provider supported capabilities (ReadOnly, WriteOnly, ReadWrite).
- func (ibm *providerIBM) Capabilities() esv1.SecretStoreCapabilities {
- return esv1.SecretStoreReadOnly
- }
- func (ibm *providerIBM) NewClient(ctx context.Context, store esv1.GenericStore, kube kclient.Client, namespace string) (esv1.SecretsClient, error) {
- storeSpec := store.GetSpec()
- ibmSpec := storeSpec.Provider.IBM
- iStore := &client{
- kube: kube,
- store: ibmSpec,
- namespace: namespace,
- storeKind: store.GetObjectKind().GroupVersionKind().Kind,
- }
- var err error
- var secretsManager *sm.SecretsManagerV2
- containerAuth := iStore.store.Auth.ContainerAuth
- if !esutils.IsNil(containerAuth) && containerAuth.Profile != "" {
- // container-based auth
- containerAuthProfile := iStore.store.Auth.ContainerAuth.Profile
- containerAuthToken := iStore.store.Auth.ContainerAuth.TokenLocation
- containerAuthEndpoint := iStore.store.Auth.ContainerAuth.IAMEndpoint
- if containerAuthToken == "" {
- // API default path
- containerAuthToken = "/var/run/secrets/tokens/vault-token"
- }
- if containerAuthEndpoint == "" {
- // API default path
- containerAuthEndpoint = iamDefaultEndpoint
- }
- authenticator, err := core.NewContainerAuthenticatorBuilder().
- SetIAMProfileName(containerAuthProfile).
- SetCRTokenFilename(containerAuthToken).
- SetURL(containerAuthEndpoint).
- Build()
- if err != nil {
- return nil, fmt.Errorf(errIBMClient, err)
- }
- secretsManager, err = sm.NewSecretsManagerV2(&sm.SecretsManagerV2Options{
- URL: *storeSpec.Provider.IBM.ServiceURL,
- Authenticator: authenticator,
- })
- if err != nil {
- return nil, fmt.Errorf(errIBMClient, err)
- }
- } else {
- // API Key-based auth
- if err := iStore.setAuth(ctx); err != nil {
- return nil, err
- }
- iamEndpoint := iStore.store.Auth.SecretRef.IAMEndpoint
- if iamEndpoint == "" {
- // API default path
- iamEndpoint = iamDefaultEndpoint
- }
- secretsManager, err = sm.NewSecretsManagerV2(&sm.SecretsManagerV2Options{
- URL: *storeSpec.Provider.IBM.ServiceURL,
- Authenticator: &core.IamAuthenticator{
- ApiKey: string(iStore.credentials),
- URL: iamEndpoint,
- },
- })
- }
- // Setup retry options, but only if present
- if storeSpec.RetrySettings != nil {
- var retryAmount int
- var retryDuration time.Duration
- if storeSpec.RetrySettings.MaxRetries != nil {
- retryAmount = int(*storeSpec.RetrySettings.MaxRetries)
- } else {
- retryAmount = 3
- }
- if storeSpec.RetrySettings.RetryInterval != nil {
- retryDuration, err = time.ParseDuration(*storeSpec.RetrySettings.RetryInterval)
- } else {
- retryDuration = 5 * time.Second
- }
- if err == nil {
- secretsManager.Service.EnableRetries(retryAmount, retryDuration)
- }
- }
- if err != nil {
- return nil, fmt.Errorf(errIBMClient, err)
- }
- ibm.IBMClient = secretsManager
- return ibm, nil
- }
- // populateSecretMap populates the secretMap with metadata information that is pulled from IBM provider.
- func populateSecretMap(secretMap map[string][]byte, secretDataMap map[string]any) map[string][]byte {
- for key, value := range secretDataMap {
- secretMap[key] = fmt.Appendf(nil, "%v", value)
- }
- return secretMap
- }
- func formSecretMap(secretData any) (map[string]any, error) {
- secretDataMap := make(map[string]any)
- data, err := json.Marshal(secretData)
- if err != nil {
- return nil, fmt.Errorf(errJSONSecretMarshal, err)
- }
- if err := json.Unmarshal(data, &secretDataMap); err != nil {
- return nil, fmt.Errorf(errJSONSecretUnmarshal, err)
- }
- return secretDataMap, nil
- }
- // NewProvider creates a new Provider instance.
- func NewProvider() esv1.Provider {
- return &providerIBM{}
- }
- // ProviderSpec returns the provider specification for registration.
- func ProviderSpec() *esv1.SecretStoreProvider {
- return &esv1.SecretStoreProvider{
- IBM: &esv1.IBMProvider{},
- }
- }
- // MaintenanceStatus returns the maintenance status of the provider.
- func MaintenanceStatus() esv1.MaintenanceStatus {
- return esv1.MaintenanceStatusMaintained
- }
|