provider.go 23 KB

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