provider.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  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.CreateSecretOptionsSecretTypePublicCertConst:
  111. if ref.Property == "" {
  112. return nil, fmt.Errorf("remoteRef.property required for secret type public_cert")
  113. }
  114. return getPublicCertSecret(ibm, &secretName, ref)
  115. default:
  116. return nil, fmt.Errorf("unknown secret type %s", secretType)
  117. }
  118. }
  119. func getArbitrarySecret(ibm *providerIBM, secretName *string) ([]byte, error) {
  120. response, _, err := ibm.IBMClient.GetSecret(
  121. &sm.GetSecretOptions{
  122. SecretType: core.StringPtr(sm.GetSecretOptionsSecretTypeArbitraryConst),
  123. ID: secretName,
  124. })
  125. if err != nil {
  126. return nil, err
  127. }
  128. secret := response.Resources[0].(*sm.SecretResource)
  129. secretData := secret.SecretData.(map[string]interface{})
  130. arbitrarySecretPayload := secretData["payload"].(string)
  131. return []byte(arbitrarySecretPayload), nil
  132. }
  133. func getImportCertSecret(ibm *providerIBM, secretName *string, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
  134. response, _, err := ibm.IBMClient.GetSecret(
  135. &sm.GetSecretOptions{
  136. SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeImportedCertConst),
  137. ID: secretName,
  138. })
  139. if err != nil {
  140. return nil, err
  141. }
  142. secret := response.Resources[0].(*sm.SecretResource)
  143. secretData := secret.SecretData.(map[string]interface{})
  144. if val, ok := secretData[ref.Property]; ok {
  145. return []byte(val.(string)), nil
  146. }
  147. return nil, fmt.Errorf("key %s does not exist in secret %s", ref.Property, ref.Key)
  148. }
  149. func getPublicCertSecret(ibm *providerIBM, secretName *string, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
  150. response, _, err := ibm.IBMClient.GetSecret(
  151. &sm.GetSecretOptions{
  152. SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypePublicCertConst),
  153. ID: secretName,
  154. })
  155. if err != nil {
  156. return nil, err
  157. }
  158. secret := response.Resources[0].(*sm.SecretResource)
  159. secretData := secret.SecretData.(map[string]interface{})
  160. if val, ok := secretData[ref.Property]; ok {
  161. return []byte(val.(string)), nil
  162. }
  163. return nil, fmt.Errorf("key %s does not exist in secret %s", ref.Property, ref.Key)
  164. }
  165. func getIamCredentialsSecret(ibm *providerIBM, secretName *string) ([]byte, error) {
  166. response, _, err := ibm.IBMClient.GetSecret(
  167. &sm.GetSecretOptions{
  168. SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeIamCredentialsConst),
  169. ID: secretName,
  170. })
  171. if err != nil {
  172. return nil, err
  173. }
  174. secret := response.Resources[0].(*sm.SecretResource)
  175. secretData := *secret.APIKey
  176. return []byte(secretData), nil
  177. }
  178. func getUsernamePasswordSecret(ibm *providerIBM, secretName *string, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
  179. response, _, err := ibm.IBMClient.GetSecret(
  180. &sm.GetSecretOptions{
  181. SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeUsernamePasswordConst),
  182. ID: secretName,
  183. })
  184. if err != nil {
  185. return nil, err
  186. }
  187. secret := response.Resources[0].(*sm.SecretResource)
  188. secretData := secret.SecretData.(map[string]interface{})
  189. if val, ok := secretData[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. func (ibm *providerIBM) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
  195. if utils.IsNil(ibm.IBMClient) {
  196. return nil, fmt.Errorf(errUninitalizedIBMProvider)
  197. }
  198. secretType := sm.GetSecretOptionsSecretTypeArbitraryConst
  199. secretName := ref.Key
  200. nameSplitted := strings.Split(secretName, "/")
  201. if len(nameSplitted) > 1 {
  202. secretType = nameSplitted[0]
  203. secretName = nameSplitted[1]
  204. }
  205. switch secretType {
  206. case sm.GetSecretOptionsSecretTypeArbitraryConst:
  207. response, _, err := ibm.IBMClient.GetSecret(
  208. &sm.GetSecretOptions{
  209. SecretType: core.StringPtr(sm.GetSecretOptionsSecretTypeArbitraryConst),
  210. ID: &ref.Key,
  211. })
  212. if err != nil {
  213. return nil, err
  214. }
  215. secret := response.Resources[0].(*sm.SecretResource)
  216. secretData := secret.SecretData.(map[string]interface{})
  217. arbitrarySecretPayload := secretData["payload"].(string)
  218. kv := make(map[string]interface{})
  219. err = json.Unmarshal([]byte(arbitrarySecretPayload), &kv)
  220. if err != nil {
  221. return nil, fmt.Errorf(errJSONSecretUnmarshal, err)
  222. }
  223. secretMap := byteArrayMap(kv)
  224. return secretMap, nil
  225. case sm.CreateSecretOptionsSecretTypeUsernamePasswordConst:
  226. response, _, err := ibm.IBMClient.GetSecret(
  227. &sm.GetSecretOptions{
  228. SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeUsernamePasswordConst),
  229. ID: &secretName,
  230. })
  231. if err != nil {
  232. return nil, err
  233. }
  234. secret := response.Resources[0].(*sm.SecretResource)
  235. secretData := secret.SecretData.(map[string]interface{})
  236. secretMap := byteArrayMap(secretData)
  237. return secretMap, nil
  238. case sm.CreateSecretOptionsSecretTypeIamCredentialsConst:
  239. response, _, err := ibm.IBMClient.GetSecret(
  240. &sm.GetSecretOptions{
  241. SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeIamCredentialsConst),
  242. ID: &secretName,
  243. })
  244. if err != nil {
  245. return nil, err
  246. }
  247. secret := response.Resources[0].(*sm.SecretResource)
  248. secretData := *secret.APIKey
  249. secretMap := make(map[string][]byte)
  250. secretMap["apikey"] = []byte(secretData)
  251. return secretMap, nil
  252. case sm.CreateSecretOptionsSecretTypeImportedCertConst:
  253. response, _, err := ibm.IBMClient.GetSecret(
  254. &sm.GetSecretOptions{
  255. SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeImportedCertConst),
  256. ID: &secretName,
  257. })
  258. if err != nil {
  259. return nil, err
  260. }
  261. secret := response.Resources[0].(*sm.SecretResource)
  262. secretData := secret.SecretData.(map[string]interface{})
  263. secretMap := byteArrayMap(secretData)
  264. return secretMap, nil
  265. case sm.CreateSecretOptionsSecretTypePublicCertConst:
  266. response, _, err := ibm.IBMClient.GetSecret(
  267. &sm.GetSecretOptions{
  268. SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypePublicCertConst),
  269. ID: &secretName,
  270. })
  271. if err != nil {
  272. return nil, err
  273. }
  274. secret := response.Resources[0].(*sm.SecretResource)
  275. secretData := secret.SecretData.(map[string]interface{})
  276. secretMap := byteArrayMap(secretData)
  277. return secretMap, nil
  278. default:
  279. return nil, fmt.Errorf("unknown secret type %s", secretType)
  280. }
  281. }
  282. func byteArrayMap(secretData map[string]interface{}) map[string][]byte {
  283. secretMap := make(map[string][]byte)
  284. for k, v := range secretData {
  285. secretMap[k] = []byte(v.(string))
  286. }
  287. return secretMap
  288. }
  289. func (ibm *providerIBM) Close(ctx context.Context) error {
  290. return nil
  291. }
  292. func (ibm *providerIBM) Validate() error {
  293. return nil
  294. }
  295. func (ibm *providerIBM) ValidateStore(store esv1beta1.GenericStore) error {
  296. storeSpec := store.GetSpec()
  297. ibmSpec := storeSpec.Provider.IBM
  298. if ibmSpec.ServiceURL == nil {
  299. return fmt.Errorf("serviceURL is required")
  300. }
  301. secretRef := ibmSpec.Auth.SecretRef.SecretAPIKey
  302. err := utils.ValidateSecretSelector(store, secretRef)
  303. if err != nil {
  304. return err
  305. }
  306. if secretRef.Name == "" {
  307. return fmt.Errorf("secretAPIKey.name cannot be empty")
  308. }
  309. if secretRef.Key == "" {
  310. return fmt.Errorf("secretAPIKey.key cannot be empty")
  311. }
  312. return nil
  313. }
  314. func (ibm *providerIBM) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string) (esv1beta1.SecretsClient, error) {
  315. storeSpec := store.GetSpec()
  316. ibmSpec := storeSpec.Provider.IBM
  317. iStore := &client{
  318. kube: kube,
  319. store: ibmSpec,
  320. namespace: namespace,
  321. storeKind: store.GetObjectKind().GroupVersionKind().Kind,
  322. }
  323. if err := iStore.setAuth(ctx); err != nil {
  324. return nil, err
  325. }
  326. secretsManager, err := sm.NewSecretsManagerV1(&sm.SecretsManagerV1Options{
  327. URL: *storeSpec.Provider.IBM.ServiceURL,
  328. Authenticator: &core.IamAuthenticator{
  329. ApiKey: string(iStore.credentials),
  330. },
  331. })
  332. // Setup retry options, but only if present
  333. if storeSpec.RetrySettings != nil {
  334. var retryAmount int
  335. var retryDuration time.Duration
  336. if storeSpec.RetrySettings.MaxRetries != nil {
  337. retryAmount = int(*storeSpec.RetrySettings.MaxRetries)
  338. } else {
  339. retryAmount = 3
  340. }
  341. if storeSpec.RetrySettings.RetryInterval != nil {
  342. retryDuration, err = time.ParseDuration(*storeSpec.RetrySettings.RetryInterval)
  343. } else {
  344. retryDuration = 5 * time.Second
  345. }
  346. if err == nil {
  347. secretsManager.Service.EnableRetries(retryAmount, retryDuration)
  348. }
  349. }
  350. if err != nil {
  351. return nil, fmt.Errorf(errIBMClient, err)
  352. }
  353. ibm.IBMClient = secretsManager
  354. return ibm, nil
  355. }
  356. func init() {
  357. esv1beta1.Register(&providerIBM{}, &esv1beta1.SecretStoreProvider{
  358. IBM: &esv1beta1.IBMProvider{},
  359. })
  360. }