provider.go 13 KB

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