provider.go 26 KB

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