provider.go 15 KB

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