provider.go 25 KB

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