provider.go 22 KB

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