provider.go 23 KB

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