provider.go 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785
  1. /*
  2. Copyright © The ESO Authors
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. https://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. // Package ibm provides integration with IBM Cloud Secrets Manager
  14. // for External Secrets Operator.
  15. package ibm
  16. import (
  17. "context"
  18. "encoding/json"
  19. "errors"
  20. "fmt"
  21. "os"
  22. "strings"
  23. "time"
  24. "github.com/IBM/go-sdk-core/v5/core"
  25. sm "github.com/IBM/secrets-manager-go-sdk/v2/secretsmanagerv2"
  26. "github.com/google/uuid"
  27. "github.com/tidwall/gjson"
  28. corev1 "k8s.io/api/core/v1"
  29. kclient "sigs.k8s.io/controller-runtime/pkg/client"
  30. "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
  31. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  32. "github.com/external-secrets/external-secrets/runtime/esutils"
  33. "github.com/external-secrets/external-secrets/runtime/esutils/resolvers"
  34. "github.com/external-secrets/external-secrets/runtime/metrics"
  35. )
  36. const (
  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. errUninitializedIBMProvider = "provider IBM is not initialized"
  49. errJSONSecretUnmarshal = "unable to unmarshal secret from JSON: %w"
  50. errJSONSecretMarshal = "unable to marshal secret to JSON: %w"
  51. errExtractingSecret = "unable to extract the fetched secret %s of type %s while performing %s"
  52. errNotImplemented = "not implemented"
  53. errKeyDoesNotExist = "key %s does not exist in secret %s"
  54. errFieldIsEmpty = "warn: %s is empty for secret %s\n"
  55. iamDefaultEndpoint = "https://iam.cloud.ibm.com"
  56. )
  57. var contextTimeout = time.Minute * 2
  58. // https://github.com/external-secrets/external-secrets/issues/644
  59. var (
  60. _ esv1.SecretsClient = &providerIBM{}
  61. _ esv1.Provider = &providerIBM{}
  62. )
  63. // SecretManagerClient defines the interface for interacting with IBM Cloud Secrets Manager.
  64. type SecretManagerClient interface {
  65. GetSecretWithContext(ctx context.Context, getSecretOptions *sm.GetSecretOptions) (result sm.SecretIntf, 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. }
  71. type client struct {
  72. kube kclient.Client
  73. store *esv1.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, _ esv1.PushSecretRemoteRef) error {
  87. return errors.New(errNotImplemented)
  88. }
  89. func (ibm *providerIBM) SecretExists(_ context.Context, _ esv1.PushSecretRemoteRef) (bool, error) {
  90. return false, errors.New(errNotImplemented)
  91. }
  92. // PushSecret not implemented.
  93. func (ibm *providerIBM) PushSecret(_ context.Context, _ *corev1.Secret, _ esv1.PushSecretData) error {
  94. return errors.New(errNotImplemented)
  95. }
  96. // GetAllSecrets empty.
  97. func (ibm *providerIBM) GetAllSecrets(_ context.Context, _ esv1.ExternalSecretFind) (map[string][]byte, error) {
  98. // TO be implemented
  99. return nil, errors.New(errNotImplemented)
  100. }
  101. func (ibm *providerIBM) GetSecret(_ context.Context, ref esv1.ExternalSecretDataRemoteRef) ([]byte, error) {
  102. if esutils.IsNil(ibm.IBMClient) {
  103. return nil, errors.New(errUninitializedIBMProvider)
  104. }
  105. secretGroupName, secretType, secretName := parseSecretReference(ref.Key)
  106. return ibm.getSecretByType(secretType, secretName, secretGroupName, ref)
  107. }
  108. func parseSecretReference(key string) (string, string, string) {
  109. var secretGroupName string
  110. secretType := sm.Secret_SecretType_Arbitrary
  111. secretName := key
  112. nameSplitted := strings.Split(key, "/")
  113. switch len(nameSplitted) {
  114. case 2:
  115. secretType = nameSplitted[0]
  116. secretName = nameSplitted[1]
  117. case 3:
  118. secretGroupName = nameSplitted[0]
  119. secretType = nameSplitted[1]
  120. secretName = nameSplitted[2]
  121. }
  122. return secretGroupName, secretType, secretName
  123. }
  124. func (ibm *providerIBM) getSecretByType(secretType, secretName, secretGroupName string, ref esv1.ExternalSecretDataRemoteRef) ([]byte, error) {
  125. switch secretType {
  126. case sm.Secret_SecretType_Arbitrary:
  127. return getArbitrarySecret(ibm, &secretName, secretGroupName)
  128. case sm.Secret_SecretType_UsernamePassword:
  129. return ibm.getUsernamePasswordSecretWithValidation(&secretName, ref, secretGroupName)
  130. case sm.Secret_SecretType_IamCredentials:
  131. return getIamCredentialsSecret(ibm, &secretName, secretGroupName)
  132. case sm.Secret_SecretType_ServiceCredentials:
  133. return getServiceCredentialsSecret(ibm, &secretName, secretGroupName)
  134. case sm.Secret_SecretType_ImportedCert:
  135. return ibm.getImportCertSecretWithValidation(&secretName, ref, secretGroupName)
  136. case sm.Secret_SecretType_PublicCert:
  137. return ibm.getPublicCertSecretWithValidation(&secretName, ref, secretGroupName)
  138. case sm.Secret_SecretType_PrivateCert:
  139. return ibm.getPrivateCertSecretWithValidation(&secretName, ref, secretGroupName)
  140. case sm.Secret_SecretType_Kv:
  141. return ibm.getKVSecret(&secretName, secretGroupName, ref)
  142. case sm.Secret_SecretType_CustomCredentials:
  143. return ibm.getCustomCredentialsSecret(&secretName, secretGroupName, ref)
  144. default:
  145. return nil, fmt.Errorf("unknown secret type %s", secretType)
  146. }
  147. }
  148. func (ibm *providerIBM) getUsernamePasswordSecretWithValidation(secretName *string, ref esv1.ExternalSecretDataRemoteRef, secretGroupName string) ([]byte, error) {
  149. if ref.Property == "" {
  150. return nil, errors.New("remoteRef.property required for secret type username_password")
  151. }
  152. return getUsernamePasswordSecret(ibm, secretName, ref, secretGroupName)
  153. }
  154. func (ibm *providerIBM) getImportCertSecretWithValidation(secretName *string, ref esv1.ExternalSecretDataRemoteRef, secretGroupName string) ([]byte, error) {
  155. if ref.Property == "" {
  156. return nil, errors.New("remoteRef.property required for secret type imported_cert")
  157. }
  158. return getImportCertSecret(ibm, secretName, ref, secretGroupName)
  159. }
  160. func (ibm *providerIBM) getPublicCertSecretWithValidation(secretName *string, ref esv1.ExternalSecretDataRemoteRef, secretGroupName string) ([]byte, error) {
  161. if ref.Property == "" {
  162. return nil, errors.New("remoteRef.property required for secret type public_cert")
  163. }
  164. return getPublicCertSecret(ibm, secretName, ref, secretGroupName)
  165. }
  166. func (ibm *providerIBM) getPrivateCertSecretWithValidation(secretName *string, ref esv1.ExternalSecretDataRemoteRef, secretGroupName string) ([]byte, error) {
  167. if ref.Property == "" {
  168. return nil, errors.New("remoteRef.property required for secret type private_cert")
  169. }
  170. return getPrivateCertSecret(ibm, secretName, ref, secretGroupName)
  171. }
  172. func (ibm *providerIBM) getKVSecret(secretName *string, secretGroupName string, ref esv1.ExternalSecretDataRemoteRef) ([]byte, error) {
  173. response, err := getSecretData(ibm, secretName, sm.Secret_SecretType_Kv, secretGroupName)
  174. if err != nil {
  175. return nil, err
  176. }
  177. secret, ok := response.(*sm.KVSecret)
  178. if !ok {
  179. return nil, fmt.Errorf(errExtractingSecret, *secretName, sm.Secret_SecretType_Kv, "GetSecret")
  180. }
  181. return getKVOrCustomCredentialsSecret(ref, secret.Data)
  182. }
  183. func (ibm *providerIBM) getCustomCredentialsSecret(secretName *string, secretGroupName string, ref esv1.ExternalSecretDataRemoteRef) ([]byte, error) {
  184. response, err := getSecretData(ibm, secretName, sm.Secret_SecretType_CustomCredentials, secretGroupName)
  185. if err != nil {
  186. return nil, err
  187. }
  188. secret, ok := response.(*sm.CustomCredentialsSecret)
  189. if !ok {
  190. return nil, fmt.Errorf(errExtractingSecret, *secretName, sm.Secret_SecretType_CustomCredentials, "GetSecret")
  191. }
  192. return getKVOrCustomCredentialsSecret(ref, secret.CredentialsContent)
  193. }
  194. func getArbitrarySecret(ibm *providerIBM, secretName *string, secretGroupName string) ([]byte, error) {
  195. response, err := getSecretData(ibm, secretName, sm.Secret_SecretType_Arbitrary, secretGroupName)
  196. if err != nil {
  197. return nil, err
  198. }
  199. secMap, err := formSecretMap(response)
  200. if err != nil {
  201. return nil, err
  202. }
  203. if val, ok := secMap[payloadConst]; ok {
  204. return []byte(val.(string)), nil
  205. }
  206. return nil, fmt.Errorf(errKeyDoesNotExist, payloadConst, *secretName)
  207. }
  208. func getImportCertSecret(ibm *providerIBM, secretName *string, ref esv1.ExternalSecretDataRemoteRef, secretGroupName string) ([]byte, error) {
  209. response, err := getSecretData(ibm, secretName, sm.Secret_SecretType_ImportedCert, secretGroupName)
  210. if err != nil {
  211. return nil, err
  212. }
  213. secMap, err := formSecretMap(response)
  214. if err != nil {
  215. return nil, err
  216. }
  217. val, ok := secMap[ref.Property]
  218. if ok {
  219. return []byte(val.(string)), nil
  220. } else if ref.Property == intermediateConst {
  221. // we want to return an empty string in case the secret doesn't contain an intermediate certificate
  222. // this is to ensure that secret of type 'kubernetes.io/tls' gets created as expected, even with an empty intermediate certificate
  223. fmt.Printf(errFieldIsEmpty, intermediateConst, *secretName)
  224. return []byte(""), nil
  225. } else if ref.Property == privateKeyConst {
  226. // we want to return an empty string in case the secret doesn't contain a private key
  227. // this is to ensure that secret of type 'kubernetes.io/tls' gets created as expected, even with an empty private key
  228. fmt.Printf(errFieldIsEmpty, privateKeyConst, *secretName)
  229. return []byte(""), nil
  230. }
  231. return nil, fmt.Errorf(errKeyDoesNotExist, ref.Property, ref.Key)
  232. }
  233. func getPublicCertSecret(ibm *providerIBM, secretName *string, ref esv1.ExternalSecretDataRemoteRef, secretGroupName string) ([]byte, error) {
  234. response, err := getSecretData(ibm, secretName, sm.Secret_SecretType_PublicCert, 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[ref.Property]; ok {
  243. return []byte(val.(string)), nil
  244. }
  245. return nil, fmt.Errorf(errKeyDoesNotExist, ref.Property, ref.Key)
  246. }
  247. func getPrivateCertSecret(ibm *providerIBM, secretName *string, ref esv1.ExternalSecretDataRemoteRef, secretGroupName string) ([]byte, error) {
  248. response, err := getSecretData(ibm, secretName, sm.Secret_SecretType_PrivateCert, secretGroupName)
  249. if err != nil {
  250. return nil, err
  251. }
  252. secMap, err := formSecretMap(response)
  253. if err != nil {
  254. return nil, err
  255. }
  256. if val, ok := secMap[ref.Property]; ok {
  257. return []byte(val.(string)), nil
  258. }
  259. return nil, fmt.Errorf(errKeyDoesNotExist, ref.Property, ref.Key)
  260. }
  261. func getIamCredentialsSecret(ibm *providerIBM, secretName *string, secretGroupName string) ([]byte, error) {
  262. response, err := getSecretData(ibm, secretName, sm.Secret_SecretType_IamCredentials, secretGroupName)
  263. if err != nil {
  264. return nil, err
  265. }
  266. secMap, err := formSecretMap(response)
  267. if err != nil {
  268. return nil, err
  269. }
  270. if val, ok := secMap[smAPIKeyConst]; ok {
  271. return []byte(val.(string)), nil
  272. }
  273. return nil, fmt.Errorf(errKeyDoesNotExist, smAPIKeyConst, *secretName)
  274. }
  275. func getServiceCredentialsSecret(ibm *providerIBM, secretName *string, secretGroupName string) ([]byte, error) {
  276. response, err := getSecretData(ibm, secretName, sm.Secret_SecretType_ServiceCredentials, secretGroupName)
  277. if err != nil {
  278. return nil, err
  279. }
  280. secMap, err := formSecretMap(response)
  281. if err != nil {
  282. return nil, err
  283. }
  284. if val, ok := secMap[credentialsConst]; ok {
  285. mval, err := json.Marshal(val)
  286. if err != nil {
  287. return nil, fmt.Errorf("failed to marshal secret map for service credentials secret: %w", err)
  288. }
  289. return mval, nil
  290. }
  291. return nil, fmt.Errorf(errKeyDoesNotExist, credentialsConst, *secretName)
  292. }
  293. func getUsernamePasswordSecret(ibm *providerIBM, secretName *string, ref esv1.ExternalSecretDataRemoteRef, secretGroupName string) ([]byte, error) {
  294. response, err := getSecretData(ibm, secretName, sm.Secret_SecretType_UsernamePassword, secretGroupName)
  295. if err != nil {
  296. return nil, err
  297. }
  298. secMap, err := formSecretMap(response)
  299. if err != nil {
  300. return nil, err
  301. }
  302. if val, ok := secMap[ref.Property]; ok {
  303. return []byte(val.(string)), nil
  304. }
  305. return nil, fmt.Errorf(errKeyDoesNotExist, ref.Property, ref.Key)
  306. }
  307. // Returns a secret of type kv or custom credentials and supports json path.
  308. func getKVOrCustomCredentialsSecret(ref esv1.ExternalSecretDataRemoteRef, credentialsData map[string]any) ([]byte, error) {
  309. payloadJSONByte, err := json.Marshal(credentialsData)
  310. if err != nil {
  311. return nil, fmt.Errorf("marshaling payload from secret failed. %w", err)
  312. }
  313. payloadJSON := string(payloadJSONByte)
  314. // no property requested, return the entire payload
  315. if ref.Property == "" {
  316. return []byte(payloadJSON), nil
  317. }
  318. // returns the requested key
  319. // consider that the key contains a ".". this could be one of 2 options
  320. // a) "." is part of the key name
  321. // b) "." is symbole for JSON path
  322. if ref.Property != "" {
  323. refProperty := ref.Property
  324. // a) "." is part the key name
  325. // escape "."
  326. idx := strings.Index(refProperty, ".")
  327. if idx > 0 {
  328. refProperty = strings.ReplaceAll(refProperty, ".", "\\.")
  329. val := gjson.Get(payloadJSON, refProperty)
  330. if val.Exists() {
  331. return []byte(val.String()), nil
  332. }
  333. }
  334. // b) "." is symbole for JSON path
  335. // try to get value for this path
  336. val := gjson.Get(payloadJSON, ref.Property)
  337. if !val.Exists() {
  338. return nil, fmt.Errorf(errKeyDoesNotExist, ref.Property, ref.Key)
  339. }
  340. return []byte(val.String()), nil
  341. }
  342. return nil, fmt.Errorf("no property provided for secret %s", ref.Key)
  343. }
  344. func getSecretData(ibm *providerIBM, secretName *string, secretType, secretGroupName string) (sm.SecretIntf, error) {
  345. _, err := uuid.Parse(*secretName)
  346. if err != nil {
  347. // secret name has been provided instead of id
  348. if secretGroupName == "" {
  349. // secret group name is not provided
  350. return nil, errors.New("failed to fetch the secret, secret group name is missing")
  351. }
  352. // secret group name is provided along with secret name,
  353. // follow the new mechanism by calling GetSecretByNameTypeWithContext
  354. ctx, cancel := context.WithTimeout(context.Background(), contextTimeout)
  355. defer cancel()
  356. response, _, err := ibm.IBMClient.GetSecretByNameTypeWithContext(
  357. ctx,
  358. &sm.GetSecretByNameTypeOptions{
  359. Name: secretName,
  360. SecretGroupName: &secretGroupName,
  361. SecretType: &secretType,
  362. })
  363. metrics.ObserveAPICall(ProviderIBMSM, CallIBMSMGetSecretByNameType, err)
  364. if err != nil {
  365. return nil, err
  366. }
  367. return response, nil
  368. }
  369. ctx, cancel := context.WithTimeout(context.Background(), contextTimeout)
  370. defer cancel()
  371. response, _, err := ibm.IBMClient.GetSecretWithContext(
  372. ctx,
  373. &sm.GetSecretOptions{
  374. ID: secretName,
  375. })
  376. metrics.ObserveAPICall(ProviderIBMSM, CallIBMSMGetSecret, err)
  377. if err != nil {
  378. return nil, err
  379. }
  380. return response, nil
  381. }
  382. func (ibm *providerIBM) GetSecretMap(_ context.Context, ref esv1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
  383. if esutils.IsNil(ibm.IBMClient) {
  384. return nil, errors.New(errUninitializedIBMProvider)
  385. }
  386. var secretGroupName string
  387. secretType := sm.Secret_SecretType_Arbitrary
  388. secretName := ref.Key
  389. nameSplitted := strings.Split(secretName, "/")
  390. switch len(nameSplitted) {
  391. case 2:
  392. secretType = nameSplitted[0]
  393. secretName = nameSplitted[1]
  394. case 3:
  395. secretGroupName = nameSplitted[0]
  396. secretType = nameSplitted[1]
  397. secretName = nameSplitted[2]
  398. }
  399. secretMap := make(map[string][]byte)
  400. secMapBytes := make(map[string][]byte)
  401. response, err := getSecretData(ibm, &secretName, secretType, secretGroupName)
  402. if err != nil {
  403. return nil, err
  404. }
  405. secMap, err := formSecretMap(response)
  406. if err != nil {
  407. return nil, err
  408. }
  409. if ref.MetadataPolicy == esv1.ExternalSecretMetadataPolicyFetch {
  410. secretMap = populateSecretMap(secretMap, secMap)
  411. }
  412. secMapBytes = populateSecretMap(secMapBytes, secMap)
  413. checkNilFn := func(propertyList []string) error {
  414. for _, prop := range propertyList {
  415. if _, ok := secMap[prop]; !ok {
  416. return fmt.Errorf(errKeyDoesNotExist, prop, secretName)
  417. }
  418. }
  419. return nil
  420. }
  421. switch secretType {
  422. case sm.Secret_SecretType_Arbitrary:
  423. if err := checkNilFn([]string{payloadConst}); err != nil {
  424. return nil, err
  425. }
  426. secretMap[arbitraryConst] = secMapBytes[payloadConst]
  427. return secretMap, nil
  428. case sm.Secret_SecretType_UsernamePassword:
  429. if err := checkNilFn([]string{usernameConst, passwordConst}); err != nil {
  430. return nil, err
  431. }
  432. secretMap[usernameConst] = secMapBytes[usernameConst]
  433. secretMap[passwordConst] = secMapBytes[passwordConst]
  434. return secretMap, nil
  435. case sm.Secret_SecretType_IamCredentials:
  436. if err := checkNilFn([]string{smAPIKeyConst}); err != nil {
  437. return nil, err
  438. }
  439. secretMap[apikeyConst] = secMapBytes[smAPIKeyConst]
  440. return secretMap, nil
  441. case sm.Secret_SecretType_ServiceCredentials:
  442. if err := checkNilFn([]string{credentialsConst}); err != nil {
  443. return nil, err
  444. }
  445. secretMap[credentialsConst] = secMapBytes[credentialsConst]
  446. return secretMap, nil
  447. case sm.Secret_SecretType_ImportedCert:
  448. if err := checkNilFn([]string{certificateConst}); err != nil {
  449. return nil, err
  450. }
  451. secretMap[certificateConst] = secMapBytes[certificateConst]
  452. if v1, ok := secMapBytes[intermediateConst]; ok {
  453. secretMap[intermediateConst] = v1
  454. } else {
  455. fmt.Printf(errFieldIsEmpty, intermediateConst, secretName)
  456. secretMap[intermediateConst] = []byte("")
  457. }
  458. if v2, ok := secMapBytes[privateKeyConst]; ok {
  459. secretMap[privateKeyConst] = v2
  460. } else {
  461. fmt.Printf(errFieldIsEmpty, privateKeyConst, secretName)
  462. secretMap[privateKeyConst] = []byte("")
  463. }
  464. return secretMap, nil
  465. case sm.Secret_SecretType_PublicCert:
  466. if err := checkNilFn([]string{certificateConst, intermediateConst, privateKeyConst}); err != nil {
  467. return nil, err
  468. }
  469. secretMap[certificateConst] = secMapBytes[certificateConst]
  470. secretMap[intermediateConst] = secMapBytes[intermediateConst]
  471. secretMap[privateKeyConst] = secMapBytes[privateKeyConst]
  472. return secretMap, nil
  473. case sm.Secret_SecretType_PrivateCert:
  474. if err := checkNilFn([]string{certificateConst, privateKeyConst}); err != nil {
  475. return nil, err
  476. }
  477. secretMap[certificateConst] = secMapBytes[certificateConst]
  478. secretMap[privateKeyConst] = secMapBytes[privateKeyConst]
  479. return secretMap, nil
  480. case sm.Secret_SecretType_Kv:
  481. secretData, ok := response.(*sm.KVSecret)
  482. if !ok {
  483. return nil, fmt.Errorf(errExtractingSecret, secretName, sm.Secret_SecretType_Kv, "GetSecretMap")
  484. }
  485. secret, err := getKVOrCustomCredentialsSecret(ref, secretData.Data)
  486. if err != nil {
  487. return nil, err
  488. }
  489. m := make(map[string]any)
  490. err = json.Unmarshal(secret, &m)
  491. if err != nil {
  492. return nil, fmt.Errorf(errJSONSecretUnmarshal, err)
  493. }
  494. secretMap = byteArrayMap(m, secretMap)
  495. return secretMap, nil
  496. case sm.Secret_SecretType_CustomCredentials:
  497. secretData, ok := response.(*sm.CustomCredentialsSecret)
  498. if !ok {
  499. return nil, fmt.Errorf(errExtractingSecret, secretName, sm.Secret_SecretType_CustomCredentials, "GetSecretMap")
  500. }
  501. secret, err := getKVOrCustomCredentialsSecret(ref, secretData.CredentialsContent)
  502. if err != nil {
  503. return nil, err
  504. }
  505. m := make(map[string]any)
  506. err = json.Unmarshal(secret, &m)
  507. if err != nil {
  508. return nil, fmt.Errorf(errJSONSecretUnmarshal, err)
  509. }
  510. secretMap = byteArrayMap(m, secretMap)
  511. return secretMap, nil
  512. default:
  513. return nil, fmt.Errorf("unknown secret type %s", secretType)
  514. }
  515. }
  516. func byteArrayMap(secretData map[string]any, secretMap map[string][]byte) map[string][]byte {
  517. var err error
  518. for k, v := range secretData {
  519. secretMap[k], err = esutils.GetByteValue(v)
  520. if err != nil {
  521. return nil
  522. }
  523. }
  524. return secretMap
  525. }
  526. func (ibm *providerIBM) Close(_ context.Context) error {
  527. return nil
  528. }
  529. func (ibm *providerIBM) Validate() (esv1.ValidationResult, error) {
  530. return esv1.ValidationResultReady, nil
  531. }
  532. func (ibm *providerIBM) ValidateStore(store esv1.GenericStore) (admission.Warnings, error) {
  533. storeSpec := store.GetSpec()
  534. ibmSpec := storeSpec.Provider.IBM
  535. if ibmSpec.ServiceURL == nil {
  536. return nil, errors.New("serviceURL is required")
  537. }
  538. containerRef := ibmSpec.Auth.ContainerAuth
  539. secretRef := ibmSpec.Auth.SecretRef
  540. missingContainerRef := esutils.IsNil(containerRef)
  541. missingSecretRef := esutils.IsNil(secretRef)
  542. if missingContainerRef == missingSecretRef {
  543. // since both are equal, if one is missing assume both are missing
  544. if missingContainerRef {
  545. return nil, errors.New("missing auth method")
  546. }
  547. return nil, errors.New("too many auth methods defined")
  548. }
  549. if !missingContainerRef {
  550. // catch undefined container auth profile
  551. if containerRef.Profile == "" {
  552. return nil, errors.New("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 nil, fmt.Errorf("cannot read container auth token %s. %w", containerRef.TokenLocation, err)
  560. }
  561. return nil, nil
  562. }
  563. // proceed with API Key Auth validation
  564. secretKeyRef := secretRef.SecretAPIKey
  565. err := esutils.ValidateSecretSelector(store, secretKeyRef)
  566. if err != nil {
  567. return nil, err
  568. }
  569. if secretKeyRef.Name == "" {
  570. return nil, errors.New("secretAPIKey.name cannot be empty")
  571. }
  572. if secretKeyRef.Key == "" {
  573. return nil, errors.New("secretAPIKey.key cannot be empty")
  574. }
  575. return nil, nil
  576. }
  577. // Capabilities return the provider supported capabilities (ReadOnly, WriteOnly, ReadWrite).
  578. func (ibm *providerIBM) Capabilities() esv1.SecretStoreCapabilities {
  579. return esv1.SecretStoreReadOnly
  580. }
  581. func (ibm *providerIBM) NewClient(ctx context.Context, store esv1.GenericStore, kube kclient.Client, namespace string) (esv1.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 !esutils.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 = iamDefaultEndpoint
  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. iamEndpoint := iStore.store.Auth.SecretRef.IAMEndpoint
  627. if iamEndpoint == "" {
  628. // API default path
  629. iamEndpoint = iamDefaultEndpoint
  630. }
  631. secretsManager, err = sm.NewSecretsManagerV2(&sm.SecretsManagerV2Options{
  632. URL: *storeSpec.Provider.IBM.ServiceURL,
  633. Authenticator: &core.IamAuthenticator{
  634. ApiKey: string(iStore.credentials),
  635. URL: iamEndpoint,
  636. },
  637. })
  638. }
  639. // Setup retry options, but only if present
  640. if storeSpec.RetrySettings != nil {
  641. var retryAmount int
  642. var retryDuration time.Duration
  643. if storeSpec.RetrySettings.MaxRetries != nil {
  644. retryAmount = int(*storeSpec.RetrySettings.MaxRetries)
  645. } else {
  646. retryAmount = 3
  647. }
  648. if storeSpec.RetrySettings.RetryInterval != nil {
  649. retryDuration, err = time.ParseDuration(*storeSpec.RetrySettings.RetryInterval)
  650. } else {
  651. retryDuration = 5 * time.Second
  652. }
  653. if err == nil {
  654. secretsManager.Service.EnableRetries(retryAmount, retryDuration)
  655. }
  656. }
  657. if err != nil {
  658. return nil, fmt.Errorf(errIBMClient, err)
  659. }
  660. ibm.IBMClient = secretsManager
  661. return ibm, nil
  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]any) map[string][]byte {
  665. for key, value := range secretDataMap {
  666. secretMap[key] = fmt.Appendf(nil, "%v", value)
  667. }
  668. return secretMap
  669. }
  670. func formSecretMap(secretData any) (map[string]any, error) {
  671. secretDataMap := make(map[string]any)
  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. }
  681. // NewProvider creates a new Provider instance.
  682. func NewProvider() esv1.Provider {
  683. return &providerIBM{}
  684. }
  685. // ProviderSpec returns the provider specification for registration.
  686. func ProviderSpec() *esv1.SecretStoreProvider {
  687. return &esv1.SecretStoreProvider{
  688. IBM: &esv1.IBMProvider{},
  689. }
  690. }
  691. // MaintenanceStatus returns the maintenance status of the provider.
  692. func MaintenanceStatus() esv1.MaintenanceStatus {
  693. return esv1.MaintenanceStatusMaintained
  694. }