provider.go 24 KB

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