provider.go 24 KB

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