provider.go 24 KB

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