provider.go 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748
  1. /*
  2. Licensed under the Apache License, Version 2.0 (the "License");
  3. you may not use this file except in compliance with the License.
  4. You may obtain a copy of the License at
  5. http://www.apache.org/licenses/LICENSE-2.0
  6. Unless required by applicable law or agreed to in writing, software
  7. distributed under the License is distributed on an "AS IS" BASIS,
  8. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. See the License for the specific language governing permissions and
  10. limitations under the License.
  11. */
  12. package ibm
  13. import (
  14. "context"
  15. "encoding/json"
  16. "fmt"
  17. "os"
  18. "strconv"
  19. "strings"
  20. "time"
  21. "github.com/IBM/go-sdk-core/v5/core"
  22. sm "github.com/IBM/secrets-manager-go-sdk/v2/secretsmanagerv2"
  23. "github.com/google/uuid"
  24. "github.com/tidwall/gjson"
  25. corev1 "k8s.io/api/core/v1"
  26. apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
  27. "k8s.io/apimachinery/pkg/types"
  28. kclient "sigs.k8s.io/controller-runtime/pkg/client"
  29. esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
  30. "github.com/external-secrets/external-secrets/pkg/constants"
  31. "github.com/external-secrets/external-secrets/pkg/metrics"
  32. "github.com/external-secrets/external-secrets/pkg/utils"
  33. )
  34. const (
  35. SecretsManagerEndpointEnv = "IBM_SECRETSMANAGER_ENDPOINT"
  36. STSEndpointEnv = "IBM_STS_ENDPOINT"
  37. SSMEndpointEnv = "IBM_SSM_ENDPOINT"
  38. certificateConst = "certificate"
  39. intermediateConst = "intermediate"
  40. privateKeyConst = "private_key"
  41. usernameConst = "username"
  42. passwordConst = "password"
  43. apikeyConst = "apikey"
  44. arbitraryConst = "arbitrary"
  45. payloadConst = "payload"
  46. smAPIKeyConst = "api_key"
  47. errIBMClient = "cannot setup new ibm client: %w"
  48. errIBMCredSecretName = "invalid IBM SecretStore resource: missing IBM APIKey"
  49. errUninitalizedIBMProvider = "provider IBM is not initialized"
  50. errInvalidClusterStoreMissingSKNamespace = "invalid ClusterStore, missing namespace"
  51. errFetchSAKSecret = "could not fetch SecretAccessKey secret: %w"
  52. errMissingSAK = "missing SecretAccessKey"
  53. errJSONSecretUnmarshal = "unable to unmarshal secret: %w"
  54. errJSONSecretMarshal = "unable to marshal secret: %w"
  55. errExtractingSecret = "unable to extract the fetched secret %s of type %s while performing %s"
  56. defaultCacheSize = 100
  57. defaultCacheExpiry = 1 * time.Hour
  58. )
  59. var contextTimeout = time.Minute * 2
  60. // https://github.com/external-secrets/external-secrets/issues/644
  61. var (
  62. _ esv1beta1.SecretsClient = &providerIBM{}
  63. _ esv1beta1.Provider = &providerIBM{}
  64. )
  65. type SecretManagerClient interface {
  66. GetSecretWithContext(ctx context.Context, getSecretOptions *sm.GetSecretOptions) (result sm.SecretIntf, response *core.DetailedResponse, err error)
  67. ListSecretsWithContext(ctx context.Context, listSecretsOptions *sm.ListSecretsOptions) (result *sm.SecretMetadataPaginatedCollection, response *core.DetailedResponse, err error)
  68. }
  69. type providerIBM struct {
  70. IBMClient SecretManagerClient
  71. cache cacheIntf
  72. }
  73. type client struct {
  74. kube kclient.Client
  75. store *esv1beta1.IBMProvider
  76. namespace string
  77. storeKind string
  78. credentials []byte
  79. }
  80. func (c *client) setAuth(ctx context.Context) error {
  81. credentialsSecret := &corev1.Secret{}
  82. credentialsSecretName := c.store.Auth.SecretRef.SecretAPIKey.Name
  83. if credentialsSecretName == "" {
  84. return fmt.Errorf(errIBMCredSecretName)
  85. }
  86. objectKey := types.NamespacedName{
  87. Name: credentialsSecretName,
  88. Namespace: c.namespace,
  89. }
  90. // only ClusterStore is allowed to set namespace (and then it's required)
  91. if c.storeKind == esv1beta1.ClusterSecretStoreKind {
  92. if c.store.Auth.SecretRef.SecretAPIKey.Namespace == nil {
  93. return fmt.Errorf(errInvalidClusterStoreMissingSKNamespace)
  94. }
  95. objectKey.Namespace = *c.store.Auth.SecretRef.SecretAPIKey.Namespace
  96. }
  97. err := c.kube.Get(ctx, objectKey, credentialsSecret)
  98. if err != nil {
  99. return fmt.Errorf(errFetchSAKSecret, err)
  100. }
  101. c.credentials = credentialsSecret.Data[c.store.Auth.SecretRef.SecretAPIKey.Key]
  102. if (c.credentials == nil) || (len(c.credentials) == 0) {
  103. return fmt.Errorf(errMissingSAK)
  104. }
  105. return nil
  106. }
  107. func (ibm *providerIBM) DeleteSecret(_ context.Context, _ esv1beta1.PushRemoteRef) error {
  108. return fmt.Errorf("not implemented")
  109. }
  110. // Not Implemented PushSecret.
  111. func (ibm *providerIBM) PushSecret(_ context.Context, _ []byte, _ *apiextensionsv1.JSON, _ esv1beta1.PushRemoteRef) error {
  112. return fmt.Errorf("not implemented")
  113. }
  114. // Empty GetAllSecrets.
  115. func (ibm *providerIBM) GetAllSecrets(_ context.Context, _ esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
  116. // TO be implemented
  117. return nil, fmt.Errorf("GetAllSecrets not implemented")
  118. }
  119. func (ibm *providerIBM) GetSecret(_ context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
  120. if utils.IsNil(ibm.IBMClient) {
  121. return nil, fmt.Errorf(errUninitalizedIBMProvider)
  122. }
  123. secretType := sm.Secret_SecretType_Arbitrary
  124. secretName := ref.Key
  125. nameSplitted := strings.Split(secretName, "/")
  126. if len(nameSplitted) > 1 {
  127. secretType = nameSplitted[0]
  128. secretName = nameSplitted[1]
  129. }
  130. switch secretType {
  131. case sm.Secret_SecretType_Arbitrary:
  132. return getArbitrarySecret(ibm, &secretName)
  133. case sm.Secret_SecretType_UsernamePassword:
  134. if ref.Property == "" {
  135. return nil, fmt.Errorf("remoteRef.property required for secret type username_password")
  136. }
  137. return getUsernamePasswordSecret(ibm, &secretName, ref)
  138. case sm.Secret_SecretType_IamCredentials:
  139. return getIamCredentialsSecret(ibm, &secretName)
  140. case sm.Secret_SecretType_ImportedCert:
  141. if ref.Property == "" {
  142. return nil, fmt.Errorf("remoteRef.property required for secret type imported_cert")
  143. }
  144. return getImportCertSecret(ibm, &secretName, ref)
  145. case sm.Secret_SecretType_PublicCert:
  146. if ref.Property == "" {
  147. return nil, fmt.Errorf("remoteRef.property required for secret type public_cert")
  148. }
  149. return getPublicCertSecret(ibm, &secretName, ref)
  150. case sm.Secret_SecretType_PrivateCert:
  151. if ref.Property == "" {
  152. return nil, fmt.Errorf("remoteRef.property required for secret type private_cert")
  153. }
  154. return getPrivateCertSecret(ibm, &secretName, ref)
  155. case sm.Secret_SecretType_Kv:
  156. response, err := getSecretData(ibm, &secretName, sm.Secret_SecretType_Kv)
  157. if err != nil {
  158. return nil, err
  159. }
  160. secret, ok := response.(*sm.KVSecret)
  161. if !ok {
  162. return nil, fmt.Errorf(errExtractingSecret, secretName, sm.Secret_SecretType_Kv, "GetSecret")
  163. }
  164. return getKVSecret(ref, secret)
  165. default:
  166. return nil, fmt.Errorf("unknown secret type %s", secretType)
  167. }
  168. }
  169. func getArbitrarySecret(ibm *providerIBM, secretName *string) ([]byte, error) {
  170. response, err := getSecretData(ibm, secretName, sm.Secret_SecretType_Arbitrary)
  171. if err != nil {
  172. return nil, err
  173. }
  174. secMap, err := formSecretMap(response)
  175. if err != nil {
  176. return nil, err
  177. }
  178. if val, ok := secMap[payloadConst]; ok {
  179. return []byte(val.(string)), nil
  180. }
  181. return nil, fmt.Errorf("key %s does not exist in secret %s", payloadConst, *secretName)
  182. }
  183. func getImportCertSecret(ibm *providerIBM, secretName *string, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
  184. response, err := getSecretData(ibm, secretName, sm.Secret_SecretType_ImportedCert)
  185. if err != nil {
  186. return nil, err
  187. }
  188. secMap, err := formSecretMap(response)
  189. if err != nil {
  190. return nil, err
  191. }
  192. val, ok := secMap[ref.Property]
  193. if ok {
  194. return []byte(val.(string)), nil
  195. } else if ref.Property == privateKeyConst {
  196. // we want to return an empty string in case the secret doesn't contain a private key
  197. // this is to ensure that secret of type 'kubernetes.io/tls' gets created as expected, even with an empty private key
  198. fmt.Printf("warn: %s is empty for secret %s\n", privateKeyConst, *secretName)
  199. return []byte(""), nil
  200. }
  201. return nil, fmt.Errorf("key %s does not exist in secret %s", ref.Property, ref.Key)
  202. }
  203. func getPublicCertSecret(ibm *providerIBM, secretName *string, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
  204. response, err := getSecretData(ibm, secretName, sm.Secret_SecretType_PublicCert)
  205. if err != nil {
  206. return nil, err
  207. }
  208. secMap, err := formSecretMap(response)
  209. if err != nil {
  210. return nil, err
  211. }
  212. if val, ok := secMap[ref.Property]; ok {
  213. return []byte(val.(string)), nil
  214. }
  215. return nil, fmt.Errorf("key %s does not exist in secret %s", ref.Property, ref.Key)
  216. }
  217. func getPrivateCertSecret(ibm *providerIBM, secretName *string, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
  218. response, err := getSecretData(ibm, secretName, sm.Secret_SecretType_PrivateCert)
  219. if err != nil {
  220. return nil, err
  221. }
  222. secMap, err := formSecretMap(response)
  223. if err != nil {
  224. return nil, err
  225. }
  226. if val, ok := secMap[ref.Property]; ok {
  227. return []byte(val.(string)), nil
  228. }
  229. return nil, fmt.Errorf("key %s does not exist in secret %s", ref.Property, ref.Key)
  230. }
  231. func getIamCredentialsSecret(ibm *providerIBM, secretName *string) ([]byte, error) {
  232. response, err := getSecretData(ibm, secretName, sm.Secret_SecretType_IamCredentials)
  233. if err != nil {
  234. return nil, err
  235. }
  236. secMap, err := formSecretMap(response)
  237. if err != nil {
  238. return nil, err
  239. }
  240. if val, ok := secMap[smAPIKeyConst]; ok {
  241. return []byte(val.(string)), nil
  242. }
  243. return nil, fmt.Errorf("key %s does not exist in secret %s", smAPIKeyConst, *secretName)
  244. }
  245. func getUsernamePasswordSecret(ibm *providerIBM, secretName *string, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
  246. response, err := getSecretData(ibm, secretName, sm.Secret_SecretType_UsernamePassword)
  247. if err != nil {
  248. return nil, err
  249. }
  250. secMap, err := formSecretMap(response)
  251. if err != nil {
  252. return nil, err
  253. }
  254. if val, ok := secMap[ref.Property]; ok {
  255. return []byte(val.(string)), nil
  256. }
  257. return nil, fmt.Errorf("key %s does not exist in secret %s", ref.Property, ref.Key)
  258. }
  259. // Returns a secret of type kv and supports json path.
  260. func getKVSecret(ref esv1beta1.ExternalSecretDataRemoteRef, secret *sm.KVSecret) ([]byte, error) {
  261. payloadJSONByte, err := json.Marshal(secret.Data)
  262. if err != nil {
  263. return nil, fmt.Errorf("marshaling payload from secret failed. %w", err)
  264. }
  265. payloadJSON := string(payloadJSONByte)
  266. // no property requested, return the entire payload
  267. if ref.Property == "" {
  268. return []byte(payloadJSON), nil
  269. }
  270. // returns the requested key
  271. // consider that the key contains a ".". this could be one of 2 options
  272. // a) "." is part of the key name
  273. // b) "." is symbole for JSON path
  274. if ref.Property != "" {
  275. refProperty := ref.Property
  276. // a) "." is part the key name
  277. // escape "."
  278. idx := strings.Index(refProperty, ".")
  279. if idx > 0 {
  280. refProperty = strings.ReplaceAll(refProperty, ".", "\\.")
  281. val := gjson.Get(payloadJSON, refProperty)
  282. if val.Exists() {
  283. return []byte(val.String()), nil
  284. }
  285. }
  286. // b) "." is symbole for JSON path
  287. // try to get value for this path
  288. val := gjson.Get(payloadJSON, ref.Property)
  289. if !val.Exists() {
  290. return nil, fmt.Errorf("key %s does not exist in secret %s", ref.Property, ref.Key)
  291. }
  292. return []byte(val.String()), nil
  293. }
  294. return nil, fmt.Errorf("no property provided for secret %s", ref.Key)
  295. }
  296. func getSecretData(ibm *providerIBM, secretName *string, secretType string) (sm.SecretIntf, error) {
  297. var givenName *string
  298. var cachedKey string
  299. // parse given secretName for a uuid or a secret name
  300. _, err := uuid.Parse(*secretName)
  301. if err != nil {
  302. givenName = secretName
  303. cachedKey = fmt.Sprintf("%s/%s", secretType, *givenName)
  304. isCached, cacheData := ibm.cache.GetData(cachedKey)
  305. tmp := string(cacheData)
  306. cachedName := &tmp
  307. if isCached && *cachedName != "" {
  308. secretName = cachedName
  309. } else {
  310. secretName, err = findSecretByName(ibm, givenName, secretType)
  311. if err != nil {
  312. return nil, err
  313. }
  314. ibm.cache.PutData(cachedKey, []byte(*secretName))
  315. }
  316. }
  317. ctx, cancel := context.WithTimeout(context.Background(), contextTimeout)
  318. defer cancel()
  319. response, _, err := ibm.IBMClient.GetSecretWithContext(
  320. ctx,
  321. &sm.GetSecretOptions{
  322. ID: secretName,
  323. })
  324. metrics.ObserveAPICall(constants.ProviderIBMSM, constants.CallIBMSMGetSecret, err)
  325. if err != nil {
  326. return nil, err
  327. }
  328. return response, nil
  329. }
  330. func findSecretByName(ibm *providerIBM, secretName *string, secretType string) (*string, error) {
  331. var secretID *string
  332. ctx, cancel := context.WithTimeout(context.Background(), contextTimeout)
  333. defer cancel()
  334. response, _, err := ibm.IBMClient.ListSecretsWithContext(ctx,
  335. &sm.ListSecretsOptions{
  336. Search: secretName,
  337. })
  338. metrics.ObserveAPICall(constants.ProviderIBMSM, constants.CallIBMSMListSecrets, err)
  339. if err != nil {
  340. return nil, err
  341. }
  342. found := 0
  343. for _, r := range response.Secrets {
  344. foundsecretID, foundSecretName, err := extractSecretMetadata(r, secretName, secretType)
  345. if err == nil {
  346. if *foundSecretName == *secretName {
  347. found++
  348. secretID = foundsecretID
  349. }
  350. }
  351. }
  352. if found == 0 {
  353. return nil, fmt.Errorf("failed to find a secret for the given secretName %s", *secretName)
  354. }
  355. if found > 1 {
  356. return nil, fmt.Errorf("found more than one secret matching for the given secretName %s, cannot proceed further", *secretName)
  357. }
  358. return secretID, nil
  359. }
  360. func (ibm *providerIBM) GetSecretMap(_ context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
  361. if utils.IsNil(ibm.IBMClient) {
  362. return nil, fmt.Errorf(errUninitalizedIBMProvider)
  363. }
  364. secretType := sm.Secret_SecretType_Arbitrary
  365. secretName := ref.Key
  366. nameSplitted := strings.Split(secretName, "/")
  367. if len(nameSplitted) > 1 {
  368. secretType = nameSplitted[0]
  369. secretName = nameSplitted[1]
  370. }
  371. secretMap := make(map[string][]byte)
  372. secMapBytes := make(map[string][]byte)
  373. response, err := getSecretData(ibm, &secretName, secretType)
  374. if err != nil {
  375. return nil, err
  376. }
  377. secMap, err := formSecretMap(response)
  378. if err != nil {
  379. return nil, err
  380. }
  381. if ref.MetadataPolicy == esv1beta1.ExternalSecretMetadataPolicyFetch {
  382. secretMap = populateSecretMap(secretMap, secMap)
  383. }
  384. secMapBytes = populateSecretMap(secMapBytes, secMap)
  385. checkNilFn := func(propertyList []string) error {
  386. for _, prop := range propertyList {
  387. if _, ok := secMap[prop]; !ok {
  388. return fmt.Errorf("key %s does not exist in secret %s", prop, secretName)
  389. }
  390. }
  391. return nil
  392. }
  393. switch secretType {
  394. case sm.Secret_SecretType_Arbitrary:
  395. if err := checkNilFn([]string{payloadConst}); err != nil {
  396. return nil, err
  397. }
  398. secretMap[arbitraryConst] = secMapBytes[payloadConst]
  399. return secretMap, nil
  400. case sm.Secret_SecretType_UsernamePassword:
  401. if err := checkNilFn([]string{usernameConst, passwordConst}); err != nil {
  402. return nil, err
  403. }
  404. secretMap[usernameConst] = secMapBytes[usernameConst]
  405. secretMap[passwordConst] = secMapBytes[passwordConst]
  406. return secretMap, nil
  407. case sm.Secret_SecretType_IamCredentials:
  408. if err := checkNilFn([]string{smAPIKeyConst}); err != nil {
  409. return nil, err
  410. }
  411. secretMap[apikeyConst] = secMapBytes[smAPIKeyConst]
  412. return secretMap, nil
  413. case sm.Secret_SecretType_ImportedCert:
  414. if err := checkNilFn([]string{certificateConst, intermediateConst}); err != nil {
  415. return nil, err
  416. }
  417. secretMap[certificateConst] = secMapBytes[certificateConst]
  418. secretMap[intermediateConst] = secMapBytes[intermediateConst]
  419. if v, ok := secMapBytes[privateKeyConst]; ok {
  420. secretMap[privateKeyConst] = v
  421. } else {
  422. fmt.Printf("warn: %s is empty for secret %s\n", privateKeyConst, secretName)
  423. secretMap[privateKeyConst] = []byte("")
  424. }
  425. return secretMap, nil
  426. case sm.Secret_SecretType_PublicCert:
  427. if err := checkNilFn([]string{certificateConst, intermediateConst, privateKeyConst}); err != nil {
  428. return nil, err
  429. }
  430. secretMap[certificateConst] = secMapBytes[certificateConst]
  431. secretMap[intermediateConst] = secMapBytes[intermediateConst]
  432. secretMap[privateKeyConst] = secMapBytes[privateKeyConst]
  433. return secretMap, nil
  434. case sm.Secret_SecretType_PrivateCert:
  435. if err := checkNilFn([]string{certificateConst, privateKeyConst}); err != nil {
  436. return nil, err
  437. }
  438. secretMap[certificateConst] = secMapBytes[certificateConst]
  439. secretMap[privateKeyConst] = secMapBytes[privateKeyConst]
  440. return secretMap, nil
  441. case sm.Secret_SecretType_Kv:
  442. secretData, ok := response.(*sm.KVSecret)
  443. if !ok {
  444. return nil, fmt.Errorf(errExtractingSecret, secretName, sm.Secret_SecretType_Kv, "GetSecretMap")
  445. }
  446. secret, err := getKVSecret(ref, secretData)
  447. if err != nil {
  448. return nil, err
  449. }
  450. m := make(map[string]interface{})
  451. err = json.Unmarshal(secret, &m)
  452. if err != nil {
  453. return nil, fmt.Errorf(errJSONSecretUnmarshal, err)
  454. }
  455. secretMap = byteArrayMap(m, secretMap)
  456. return secretMap, nil
  457. default:
  458. return nil, fmt.Errorf("unknown secret type %s", secretType)
  459. }
  460. }
  461. func byteArrayMap(secretData map[string]interface{}, secretMap map[string][]byte) map[string][]byte {
  462. var err error
  463. for k, v := range secretData {
  464. secretMap[k], err = getTypedKey(v)
  465. if err != nil {
  466. return nil
  467. }
  468. }
  469. return secretMap
  470. }
  471. // kudos Vault Provider - convert from various types.
  472. func getTypedKey(v interface{}) ([]byte, error) {
  473. switch t := v.(type) {
  474. case string:
  475. return []byte(t), nil
  476. case map[string]interface{}:
  477. return json.Marshal(t)
  478. case map[string]string:
  479. return json.Marshal(t)
  480. case []byte:
  481. return t, nil
  482. // also covers int and float32 due to json.Marshal
  483. case float64:
  484. return []byte(strconv.FormatFloat(t, 'f', -1, 64)), nil
  485. case bool:
  486. return []byte(strconv.FormatBool(t)), nil
  487. case nil:
  488. return []byte(nil), nil
  489. default:
  490. return nil, fmt.Errorf("secret not in expected format")
  491. }
  492. }
  493. func (ibm *providerIBM) Close(_ context.Context) error {
  494. return nil
  495. }
  496. func (ibm *providerIBM) Validate() (esv1beta1.ValidationResult, error) {
  497. return esv1beta1.ValidationResultReady, nil
  498. }
  499. func (ibm *providerIBM) ValidateStore(store esv1beta1.GenericStore) error {
  500. storeSpec := store.GetSpec()
  501. ibmSpec := storeSpec.Provider.IBM
  502. if ibmSpec.ServiceURL == nil {
  503. return fmt.Errorf("serviceURL is required")
  504. }
  505. containerRef := ibmSpec.Auth.ContainerAuth
  506. secretRef := ibmSpec.Auth.SecretRef
  507. missingContainerRef := utils.IsNil(containerRef)
  508. missingSecretRef := utils.IsNil(secretRef)
  509. if missingContainerRef == missingSecretRef {
  510. // since both are equal, if one is missing assume both are missing
  511. if missingContainerRef {
  512. return fmt.Errorf("missing auth method")
  513. }
  514. return fmt.Errorf("too many auth methods defined")
  515. }
  516. if !missingContainerRef {
  517. // catch undefined container auth profile
  518. if containerRef.Profile == "" {
  519. return fmt.Errorf("container auth profile cannot be empty")
  520. }
  521. // proceed with container auth
  522. if containerRef.TokenLocation == "" {
  523. containerRef.TokenLocation = "/var/run/secrets/tokens/vault-token"
  524. }
  525. if _, err := os.Open(containerRef.TokenLocation); err != nil {
  526. return fmt.Errorf("cannot read container auth token %s. %w", containerRef.TokenLocation, err)
  527. }
  528. return nil
  529. }
  530. // proceed with API Key Auth validation
  531. secretKeyRef := secretRef.SecretAPIKey
  532. err := utils.ValidateSecretSelector(store, secretKeyRef)
  533. if err != nil {
  534. return err
  535. }
  536. if secretKeyRef.Name == "" {
  537. return fmt.Errorf("secretAPIKey.name cannot be empty")
  538. }
  539. if secretKeyRef.Key == "" {
  540. return fmt.Errorf("secretAPIKey.key cannot be empty")
  541. }
  542. return nil
  543. }
  544. // Capabilities return the provider supported capabilities (ReadOnly, WriteOnly, ReadWrite).
  545. func (ibm *providerIBM) Capabilities() esv1beta1.SecretStoreCapabilities {
  546. return esv1beta1.SecretStoreReadOnly
  547. }
  548. func (ibm *providerIBM) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string) (esv1beta1.SecretsClient, error) {
  549. storeSpec := store.GetSpec()
  550. ibmSpec := storeSpec.Provider.IBM
  551. iStore := &client{
  552. kube: kube,
  553. store: ibmSpec,
  554. namespace: namespace,
  555. storeKind: store.GetObjectKind().GroupVersionKind().Kind,
  556. }
  557. var err error
  558. var secretsManager *sm.SecretsManagerV2
  559. containerAuth := iStore.store.Auth.ContainerAuth
  560. if !utils.IsNil(containerAuth) && containerAuth.Profile != "" {
  561. // container-based auth
  562. containerAuthProfile := iStore.store.Auth.ContainerAuth.Profile
  563. containerAuthToken := iStore.store.Auth.ContainerAuth.TokenLocation
  564. containerAuthEndpoint := iStore.store.Auth.ContainerAuth.IAMEndpoint
  565. if containerAuthToken == "" {
  566. // API default path
  567. containerAuthToken = "/var/run/secrets/tokens/vault-token"
  568. }
  569. if containerAuthEndpoint == "" {
  570. // API default path
  571. containerAuthEndpoint = "https://iam.cloud.ibm.com"
  572. }
  573. authenticator, err := core.NewContainerAuthenticatorBuilder().
  574. SetIAMProfileName(containerAuthProfile).
  575. SetCRTokenFilename(containerAuthToken).
  576. SetURL(containerAuthEndpoint).
  577. Build()
  578. if err != nil {
  579. return nil, fmt.Errorf(errIBMClient, err)
  580. }
  581. secretsManager, err = sm.NewSecretsManagerV2(&sm.SecretsManagerV2Options{
  582. URL: *storeSpec.Provider.IBM.ServiceURL,
  583. Authenticator: authenticator,
  584. })
  585. if err != nil {
  586. return nil, fmt.Errorf(errIBMClient, err)
  587. }
  588. } else {
  589. // API Key-based auth
  590. if err := iStore.setAuth(ctx); err != nil {
  591. return nil, err
  592. }
  593. secretsManager, err = sm.NewSecretsManagerV2(&sm.SecretsManagerV2Options{
  594. URL: *storeSpec.Provider.IBM.ServiceURL,
  595. Authenticator: &core.IamAuthenticator{
  596. ApiKey: string(iStore.credentials),
  597. },
  598. })
  599. }
  600. // Setup retry options, but only if present
  601. if storeSpec.RetrySettings != nil {
  602. var retryAmount int
  603. var retryDuration time.Duration
  604. if storeSpec.RetrySettings.MaxRetries != nil {
  605. retryAmount = int(*storeSpec.RetrySettings.MaxRetries)
  606. } else {
  607. retryAmount = 3
  608. }
  609. if storeSpec.RetrySettings.RetryInterval != nil {
  610. retryDuration, err = time.ParseDuration(*storeSpec.RetrySettings.RetryInterval)
  611. } else {
  612. retryDuration = 5 * time.Second
  613. }
  614. if err == nil {
  615. secretsManager.Service.EnableRetries(retryAmount, retryDuration)
  616. }
  617. }
  618. if err != nil {
  619. return nil, fmt.Errorf(errIBMClient, err)
  620. }
  621. ibm.IBMClient = secretsManager
  622. ibm.cache = NewCache(defaultCacheSize, defaultCacheExpiry)
  623. return ibm, nil
  624. }
  625. func init() {
  626. esv1beta1.Register(&providerIBM{}, &esv1beta1.SecretStoreProvider{
  627. IBM: &esv1beta1.IBMProvider{},
  628. })
  629. }
  630. // populateSecretMap populates the secretMap with metadata information that is pulled from IBM provider.
  631. func populateSecretMap(secretMap map[string][]byte, secretDataMap map[string]interface{}) map[string][]byte {
  632. for key, value := range secretDataMap {
  633. secretMap[key] = []byte(fmt.Sprintf("%v", value))
  634. }
  635. return secretMap
  636. }
  637. func formSecretMap(secretData interface{}) (map[string]interface{}, error) {
  638. secretDataMap := make(map[string]interface{})
  639. data, err := json.Marshal(secretData)
  640. if err != nil {
  641. return nil, fmt.Errorf(errJSONSecretMarshal, err)
  642. }
  643. if err := json.Unmarshal(data, &secretDataMap); err != nil {
  644. return nil, fmt.Errorf(errJSONSecretUnmarshal, err)
  645. }
  646. return secretDataMap, nil
  647. }