provider.go 24 KB

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