provider.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575
  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. "strconv"
  18. "strings"
  19. "time"
  20. core "github.com/IBM/go-sdk-core/v5/core"
  21. sm "github.com/IBM/secrets-manager-go-sdk/secretsmanagerv1"
  22. gjson "github.com/tidwall/gjson"
  23. corev1 "k8s.io/api/core/v1"
  24. types "k8s.io/apimachinery/pkg/types"
  25. ctrl "sigs.k8s.io/controller-runtime"
  26. kclient "sigs.k8s.io/controller-runtime/pkg/client"
  27. esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
  28. utils "github.com/external-secrets/external-secrets/pkg/utils"
  29. )
  30. const (
  31. SecretsManagerEndpointEnv = "IBM_SECRETSMANAGER_ENDPOINT"
  32. STSEndpointEnv = "IBM_STS_ENDPOINT"
  33. SSMEndpointEnv = "IBM_SSM_ENDPOINT"
  34. errIBMClient = "cannot setup new ibm client: %w"
  35. errIBMCredSecretName = "invalid IBM SecretStore resource: missing IBM APIKey"
  36. errUninitalizedIBMProvider = "provider IBM is not initialized"
  37. errInvalidClusterStoreMissingSKNamespace = "invalid ClusterStore, missing namespace"
  38. errFetchSAKSecret = "could not fetch SecretAccessKey secret: %w"
  39. errMissingSAK = "missing SecretAccessKey"
  40. errJSONSecretUnmarshal = "unable to unmarshal secret: %w"
  41. )
  42. type SecretManagerClient interface {
  43. GetSecret(getSecretOptions *sm.GetSecretOptions) (result *sm.GetSecret, response *core.DetailedResponse, err error)
  44. }
  45. type providerIBM struct {
  46. IBMClient SecretManagerClient
  47. }
  48. type client struct {
  49. kube kclient.Client
  50. store *esv1beta1.IBMProvider
  51. namespace string
  52. storeKind string
  53. credentials []byte
  54. }
  55. var log = ctrl.Log.WithName("provider").WithName("ibm").WithName("secretsmanager")
  56. func (c *client) setAuth(ctx context.Context) error {
  57. credentialsSecret := &corev1.Secret{}
  58. credentialsSecretName := c.store.Auth.SecretRef.SecretAPIKey.Name
  59. if credentialsSecretName == "" {
  60. return fmt.Errorf(errIBMCredSecretName)
  61. }
  62. objectKey := types.NamespacedName{
  63. Name: credentialsSecretName,
  64. Namespace: c.namespace,
  65. }
  66. // only ClusterStore is allowed to set namespace (and then it's required)
  67. if c.storeKind == esv1beta1.ClusterSecretStoreKind {
  68. if c.store.Auth.SecretRef.SecretAPIKey.Namespace == nil {
  69. return fmt.Errorf(errInvalidClusterStoreMissingSKNamespace)
  70. }
  71. objectKey.Namespace = *c.store.Auth.SecretRef.SecretAPIKey.Namespace
  72. }
  73. err := c.kube.Get(ctx, objectKey, credentialsSecret)
  74. if err != nil {
  75. return fmt.Errorf(errFetchSAKSecret, err)
  76. }
  77. c.credentials = credentialsSecret.Data[c.store.Auth.SecretRef.SecretAPIKey.Key]
  78. if (c.credentials == nil) || (len(c.credentials) == 0) {
  79. return fmt.Errorf(errMissingSAK)
  80. }
  81. return nil
  82. }
  83. // Empty GetAllSecrets.
  84. func (ibm *providerIBM) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
  85. // TO be implemented
  86. return nil, fmt.Errorf("GetAllSecrets not implemented")
  87. }
  88. func (ibm *providerIBM) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
  89. if utils.IsNil(ibm.IBMClient) {
  90. return nil, fmt.Errorf(errUninitalizedIBMProvider)
  91. }
  92. secretType := sm.GetSecretOptionsSecretTypeArbitraryConst
  93. secretName := ref.Key
  94. nameSplitted := strings.Split(secretName, "/")
  95. if len(nameSplitted) > 1 {
  96. secretType = nameSplitted[0]
  97. secretName = nameSplitted[1]
  98. }
  99. switch secretType {
  100. case sm.GetSecretOptionsSecretTypeArbitraryConst:
  101. return getArbitrarySecret(ibm, &secretName)
  102. case sm.CreateSecretOptionsSecretTypeUsernamePasswordConst:
  103. if ref.Property == "" {
  104. return nil, fmt.Errorf("remoteRef.property required for secret type username_password")
  105. }
  106. return getUsernamePasswordSecret(ibm, &secretName, ref)
  107. case sm.CreateSecretOptionsSecretTypeIamCredentialsConst:
  108. return getIamCredentialsSecret(ibm, &secretName)
  109. case sm.CreateSecretOptionsSecretTypeImportedCertConst:
  110. if ref.Property == "" {
  111. return nil, fmt.Errorf("remoteRef.property required for secret type imported_cert")
  112. }
  113. return getImportCertSecret(ibm, &secretName, ref)
  114. case sm.CreateSecretOptionsSecretTypePublicCertConst:
  115. if ref.Property == "" {
  116. return nil, fmt.Errorf("remoteRef.property required for secret type public_cert")
  117. }
  118. return getPublicCertSecret(ibm, &secretName, ref)
  119. case sm.CreateSecretOptionsSecretTypeKvConst:
  120. return getKVSecret(ibm, &secretName, ref)
  121. default:
  122. return nil, fmt.Errorf("unknown secret type %s", secretType)
  123. }
  124. }
  125. func getArbitrarySecret(ibm *providerIBM, secretName *string) ([]byte, error) {
  126. response, _, err := ibm.IBMClient.GetSecret(
  127. &sm.GetSecretOptions{
  128. SecretType: core.StringPtr(sm.GetSecretOptionsSecretTypeArbitraryConst),
  129. ID: secretName,
  130. })
  131. if err != nil {
  132. return nil, err
  133. }
  134. secret := response.Resources[0].(*sm.SecretResource)
  135. secretData := secret.SecretData.(map[string]interface{})
  136. arbitrarySecretPayload := secretData["payload"].(string)
  137. return []byte(arbitrarySecretPayload), nil
  138. }
  139. func getImportCertSecret(ibm *providerIBM, secretName *string, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
  140. response, _, err := ibm.IBMClient.GetSecret(
  141. &sm.GetSecretOptions{
  142. SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeImportedCertConst),
  143. ID: secretName,
  144. })
  145. if err != nil {
  146. return nil, err
  147. }
  148. secret := response.Resources[0].(*sm.SecretResource)
  149. secretData := secret.SecretData.(map[string]interface{})
  150. if val, ok := secretData[ref.Property]; ok {
  151. return []byte(val.(string)), nil
  152. }
  153. return nil, fmt.Errorf("key %s does not exist in secret %s", ref.Property, ref.Key)
  154. }
  155. func getPublicCertSecret(ibm *providerIBM, secretName *string, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
  156. response, _, err := ibm.IBMClient.GetSecret(
  157. &sm.GetSecretOptions{
  158. SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypePublicCertConst),
  159. ID: secretName,
  160. })
  161. if err != nil {
  162. return nil, err
  163. }
  164. secret := response.Resources[0].(*sm.SecretResource)
  165. secretData := secret.SecretData.(map[string]interface{})
  166. if val, ok := secretData[ref.Property]; ok {
  167. return []byte(val.(string)), nil
  168. }
  169. return nil, fmt.Errorf("key %s does not exist in secret %s", ref.Property, ref.Key)
  170. }
  171. func getIamCredentialsSecret(ibm *providerIBM, secretName *string) ([]byte, error) {
  172. response, _, err := ibm.IBMClient.GetSecret(
  173. &sm.GetSecretOptions{
  174. SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeIamCredentialsConst),
  175. ID: secretName,
  176. })
  177. if err != nil {
  178. return nil, err
  179. }
  180. secret := response.Resources[0].(*sm.SecretResource)
  181. secretData := *secret.APIKey
  182. return []byte(secretData), nil
  183. }
  184. func getUsernamePasswordSecret(ibm *providerIBM, secretName *string, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
  185. response, _, err := ibm.IBMClient.GetSecret(
  186. &sm.GetSecretOptions{
  187. SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeUsernamePasswordConst),
  188. ID: secretName,
  189. })
  190. if err != nil {
  191. return nil, err
  192. }
  193. secret := response.Resources[0].(*sm.SecretResource)
  194. secretData := secret.SecretData.(map[string]interface{})
  195. if val, ok := secretData[ref.Property]; ok {
  196. return []byte(val.(string)), nil
  197. }
  198. return nil, fmt.Errorf("key %s does not exist in secret %s", ref.Property, ref.Key)
  199. }
  200. // Returns a secret of type kv and supports json path.
  201. func getKVSecret(ibm *providerIBM, secretName *string, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
  202. secret, err := getSecretByType(ibm, secretName, sm.CreateSecretOptionsSecretTypeKvConst)
  203. if err != nil {
  204. return nil, err
  205. }
  206. log.Info("fetching secret", "secretName", secretName, "key", ref.Key)
  207. secretData := secret.SecretData.(map[string]interface{})
  208. payload, ok := secretData["payload"]
  209. if !ok {
  210. return nil, fmt.Errorf("no payload returned for secret %s", ref.Key)
  211. }
  212. payloadJSON := payload
  213. payloadJSONMap, ok := payloadJSON.(map[string]interface{})
  214. if ok {
  215. var payloadJSONByte []byte
  216. payloadJSONByte, err = json.Marshal(payloadJSONMap)
  217. if err != nil {
  218. return nil, fmt.Errorf("marshaling payload from secret failed. %w", err)
  219. }
  220. payloadJSON = string(payloadJSONByte)
  221. }
  222. // no property requested, return the entire payload
  223. if ref.Property == "" {
  224. return []byte(payloadJSON.(string)), nil
  225. }
  226. // returns the requested key
  227. // consider that the key contains a ".". this could be one of 2 options
  228. // a) "." is part of the key name
  229. // b) "." is symbole for JSON path
  230. if ref.Property != "" {
  231. refProperty := ref.Property
  232. // a) "." is part the key name
  233. // escape "."
  234. idx := strings.Index(refProperty, ".")
  235. if idx > 0 {
  236. refProperty = strings.ReplaceAll(refProperty, ".", "\\.")
  237. val := gjson.Get(payloadJSON.(string), refProperty)
  238. if val.Exists() {
  239. return []byte(val.String()), nil
  240. }
  241. }
  242. // b) "." is symbole for JSON path
  243. // try to get value for this path
  244. val := gjson.Get(payloadJSON.(string), ref.Property)
  245. if !val.Exists() {
  246. return nil, fmt.Errorf("key %s does not exist in secret %s", ref.Property, ref.Key)
  247. }
  248. return []byte(val.String()), nil
  249. }
  250. return nil, fmt.Errorf("no property provided for secret %s", ref.Key)
  251. }
  252. func getSecretByType(ibm *providerIBM, secretName *string, secretType string) (*sm.SecretResource, error) {
  253. response, _, err := ibm.IBMClient.GetSecret(
  254. &sm.GetSecretOptions{
  255. SecretType: core.StringPtr(secretType),
  256. ID: secretName,
  257. })
  258. if err != nil {
  259. return nil, err
  260. }
  261. secret := response.Resources[0].(*sm.SecretResource)
  262. return secret, nil
  263. }
  264. func (ibm *providerIBM) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
  265. if utils.IsNil(ibm.IBMClient) {
  266. return nil, fmt.Errorf(errUninitalizedIBMProvider)
  267. }
  268. secretType := sm.GetSecretOptionsSecretTypeArbitraryConst
  269. secretName := ref.Key
  270. nameSplitted := strings.Split(secretName, "/")
  271. if len(nameSplitted) > 1 {
  272. secretType = nameSplitted[0]
  273. secretName = nameSplitted[1]
  274. }
  275. switch secretType {
  276. case sm.GetSecretOptionsSecretTypeArbitraryConst:
  277. response, _, err := ibm.IBMClient.GetSecret(
  278. &sm.GetSecretOptions{
  279. SecretType: core.StringPtr(sm.GetSecretOptionsSecretTypeArbitraryConst),
  280. ID: &ref.Key,
  281. })
  282. if err != nil {
  283. return nil, err
  284. }
  285. secret := response.Resources[0].(*sm.SecretResource)
  286. secretData := secret.SecretData.(map[string]interface{})
  287. arbitrarySecretPayload := secretData["payload"].(string)
  288. kv := make(map[string]interface{})
  289. err = json.Unmarshal([]byte(arbitrarySecretPayload), &kv)
  290. if err != nil {
  291. return nil, fmt.Errorf(errJSONSecretUnmarshal, err)
  292. }
  293. secretMap := byteArrayMap(kv)
  294. return secretMap, nil
  295. case sm.CreateSecretOptionsSecretTypeUsernamePasswordConst:
  296. response, _, err := ibm.IBMClient.GetSecret(
  297. &sm.GetSecretOptions{
  298. SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeUsernamePasswordConst),
  299. ID: &secretName,
  300. })
  301. if err != nil {
  302. return nil, err
  303. }
  304. secret := response.Resources[0].(*sm.SecretResource)
  305. secretData := secret.SecretData.(map[string]interface{})
  306. secretMap := byteArrayMap(secretData)
  307. return secretMap, nil
  308. case sm.CreateSecretOptionsSecretTypeIamCredentialsConst:
  309. response, _, err := ibm.IBMClient.GetSecret(
  310. &sm.GetSecretOptions{
  311. SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeIamCredentialsConst),
  312. ID: &secretName,
  313. })
  314. if err != nil {
  315. return nil, err
  316. }
  317. secret := response.Resources[0].(*sm.SecretResource)
  318. secretData := *secret.APIKey
  319. secretMap := make(map[string][]byte)
  320. secretMap["apikey"] = []byte(secretData)
  321. return secretMap, nil
  322. case sm.CreateSecretOptionsSecretTypeImportedCertConst:
  323. response, _, err := ibm.IBMClient.GetSecret(
  324. &sm.GetSecretOptions{
  325. SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeImportedCertConst),
  326. ID: &secretName,
  327. })
  328. if err != nil {
  329. return nil, err
  330. }
  331. secret := response.Resources[0].(*sm.SecretResource)
  332. secretData := secret.SecretData.(map[string]interface{})
  333. secretMap := byteArrayMap(secretData)
  334. return secretMap, nil
  335. case sm.CreateSecretOptionsSecretTypePublicCertConst:
  336. response, _, err := ibm.IBMClient.GetSecret(
  337. &sm.GetSecretOptions{
  338. SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypePublicCertConst),
  339. ID: &secretName,
  340. })
  341. if err != nil {
  342. return nil, err
  343. }
  344. secret := response.Resources[0].(*sm.SecretResource)
  345. secretData := secret.SecretData.(map[string]interface{})
  346. secretMap := byteArrayMap(secretData)
  347. return secretMap, nil
  348. case sm.CreateSecretOptionsSecretTypeKvConst:
  349. secret, err := getKVSecret(ibm, &secretName, ref)
  350. if err != nil {
  351. return nil, err
  352. }
  353. m := make(map[string]interface{})
  354. err = json.Unmarshal(secret, &m)
  355. if err != nil {
  356. return nil, err
  357. }
  358. secretMap := byteArrayMap(m)
  359. return secretMap, nil
  360. default:
  361. return nil, fmt.Errorf("unknown secret type %s", secretType)
  362. }
  363. }
  364. func byteArrayMap(secretData map[string]interface{}) map[string][]byte {
  365. var err error
  366. secretMap := make(map[string][]byte)
  367. for k, v := range secretData {
  368. secretMap[k], err = getTypedKey(v)
  369. if err != nil {
  370. return nil
  371. }
  372. }
  373. return secretMap
  374. }
  375. // kudos Vault Provider - convert from various types.
  376. func getTypedKey(v interface{}) ([]byte, error) {
  377. switch t := v.(type) {
  378. case string:
  379. return []byte(t), nil
  380. case map[string]interface{}:
  381. return json.Marshal(t)
  382. case map[string]string:
  383. return json.Marshal(t)
  384. case []byte:
  385. return t, nil
  386. // also covers int and float32 due to json.Marshal
  387. case float64:
  388. return []byte(strconv.FormatFloat(t, 'f', -1, 64)), nil
  389. case bool:
  390. return []byte(strconv.FormatBool(t)), nil
  391. case nil:
  392. return []byte(nil), nil
  393. default:
  394. return nil, fmt.Errorf("secret not in expected format")
  395. }
  396. }
  397. func (ibm *providerIBM) Close(ctx context.Context) error {
  398. return nil
  399. }
  400. func (ibm *providerIBM) Validate() error {
  401. return nil
  402. }
  403. func (ibm *providerIBM) ValidateStore(store esv1beta1.GenericStore) error {
  404. storeSpec := store.GetSpec()
  405. ibmSpec := storeSpec.Provider.IBM
  406. if ibmSpec.ServiceURL == nil {
  407. return fmt.Errorf("serviceURL is required")
  408. }
  409. secretRef := ibmSpec.Auth.SecretRef.SecretAPIKey
  410. err := utils.ValidateSecretSelector(store, secretRef)
  411. if err != nil {
  412. return err
  413. }
  414. if secretRef.Name == "" {
  415. return fmt.Errorf("secretAPIKey.name cannot be empty")
  416. }
  417. if secretRef.Key == "" {
  418. return fmt.Errorf("secretAPIKey.key cannot be empty")
  419. }
  420. return nil
  421. }
  422. func (ibm *providerIBM) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string) (esv1beta1.SecretsClient, error) {
  423. storeSpec := store.GetSpec()
  424. ibmSpec := storeSpec.Provider.IBM
  425. iStore := &client{
  426. kube: kube,
  427. store: ibmSpec,
  428. namespace: namespace,
  429. storeKind: store.GetObjectKind().GroupVersionKind().Kind,
  430. }
  431. if err := iStore.setAuth(ctx); err != nil {
  432. return nil, err
  433. }
  434. secretsManager, err := sm.NewSecretsManagerV1(&sm.SecretsManagerV1Options{
  435. URL: *storeSpec.Provider.IBM.ServiceURL,
  436. Authenticator: &core.IamAuthenticator{
  437. ApiKey: string(iStore.credentials),
  438. },
  439. })
  440. // Setup retry options, but only if present
  441. if storeSpec.RetrySettings != nil {
  442. var retryAmount int
  443. var retryDuration time.Duration
  444. if storeSpec.RetrySettings.MaxRetries != nil {
  445. retryAmount = int(*storeSpec.RetrySettings.MaxRetries)
  446. } else {
  447. retryAmount = 3
  448. }
  449. if storeSpec.RetrySettings.RetryInterval != nil {
  450. retryDuration, err = time.ParseDuration(*storeSpec.RetrySettings.RetryInterval)
  451. } else {
  452. retryDuration = 5 * time.Second
  453. }
  454. if err == nil {
  455. secretsManager.Service.EnableRetries(retryAmount, retryDuration)
  456. }
  457. }
  458. if err != nil {
  459. return nil, fmt.Errorf(errIBMClient, err)
  460. }
  461. ibm.IBMClient = secretsManager
  462. return ibm, nil
  463. }
  464. func init() {
  465. esv1beta1.Register(&providerIBM{}, &esv1beta1.SecretStoreProvider{
  466. IBM: &esv1beta1.IBMProvider{},
  467. })
  468. }