provider.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  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/provider"
  26. "github.com/external-secrets/external-secrets/pkg/provider/schema"
  27. "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. func (c *client) setAuth(ctx context.Context) error {
  55. credentialsSecret := &corev1.Secret{}
  56. credentialsSecretName := c.store.Auth.SecretRef.SecretAPIKey.Name
  57. if credentialsSecretName == "" {
  58. return fmt.Errorf(errIBMCredSecretName)
  59. }
  60. objectKey := types.NamespacedName{
  61. Name: credentialsSecretName,
  62. Namespace: c.namespace,
  63. }
  64. // only ClusterStore is allowed to set namespace (and then it's required)
  65. if c.storeKind == esv1beta1.ClusterSecretStoreKind {
  66. if c.store.Auth.SecretRef.SecretAPIKey.Namespace == nil {
  67. return fmt.Errorf(errInvalidClusterStoreMissingSKNamespace)
  68. }
  69. objectKey.Namespace = *c.store.Auth.SecretRef.SecretAPIKey.Namespace
  70. }
  71. err := c.kube.Get(ctx, objectKey, credentialsSecret)
  72. if err != nil {
  73. return fmt.Errorf(errFetchSAKSecret, err)
  74. }
  75. c.credentials = credentialsSecret.Data[c.store.Auth.SecretRef.SecretAPIKey.Key]
  76. if (c.credentials == nil) || (len(c.credentials) == 0) {
  77. return fmt.Errorf(errMissingSAK)
  78. }
  79. return nil
  80. }
  81. // Empty GetAllSecrets.
  82. func (ibm *providerIBM) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
  83. // TO be implemented
  84. return nil, fmt.Errorf("GetAllSecrets not implemented")
  85. }
  86. func (ibm *providerIBM) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
  87. if utils.IsNil(ibm.IBMClient) {
  88. return nil, fmt.Errorf(errUninitalizedIBMProvider)
  89. }
  90. secretType := sm.GetSecretOptionsSecretTypeArbitraryConst
  91. secretName := ref.Key
  92. nameSplitted := strings.Split(secretName, "/")
  93. if len(nameSplitted) > 1 {
  94. secretType = nameSplitted[0]
  95. secretName = nameSplitted[1]
  96. }
  97. switch secretType {
  98. case sm.GetSecretOptionsSecretTypeArbitraryConst:
  99. return getArbitrarySecret(ibm, &secretName)
  100. case sm.CreateSecretOptionsSecretTypeUsernamePasswordConst:
  101. if ref.Property == "" {
  102. return nil, fmt.Errorf("remoteRef.property required for secret type username_password")
  103. }
  104. return getUsernamePasswordSecret(ibm, &secretName, ref)
  105. case sm.CreateSecretOptionsSecretTypeIamCredentialsConst:
  106. return getIamCredentialsSecret(ibm, &secretName)
  107. case sm.CreateSecretOptionsSecretTypeImportedCertConst:
  108. if ref.Property == "" {
  109. return nil, fmt.Errorf("remoteRef.property required for secret type imported_cert")
  110. }
  111. return getImportCertSecret(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 (ibm *providerIBM) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
  176. if utils.IsNil(ibm.IBMClient) {
  177. return nil, fmt.Errorf(errUninitalizedIBMProvider)
  178. }
  179. secretType := sm.GetSecretOptionsSecretTypeArbitraryConst
  180. secretName := ref.Key
  181. nameSplitted := strings.Split(secretName, "/")
  182. if len(nameSplitted) > 1 {
  183. secretType = nameSplitted[0]
  184. secretName = nameSplitted[1]
  185. }
  186. switch secretType {
  187. case sm.GetSecretOptionsSecretTypeArbitraryConst:
  188. response, _, err := ibm.IBMClient.GetSecret(
  189. &sm.GetSecretOptions{
  190. SecretType: core.StringPtr(sm.GetSecretOptionsSecretTypeArbitraryConst),
  191. ID: &ref.Key,
  192. })
  193. if err != nil {
  194. return nil, err
  195. }
  196. secret := response.Resources[0].(*sm.SecretResource)
  197. secretData := secret.SecretData.(map[string]interface{})
  198. arbitrarySecretPayload := secretData["payload"].(string)
  199. kv := make(map[string]interface{})
  200. err = json.Unmarshal([]byte(arbitrarySecretPayload), &kv)
  201. if err != nil {
  202. return nil, fmt.Errorf(errJSONSecretUnmarshal, err)
  203. }
  204. secretMap := byteArrayMap(kv)
  205. return secretMap, nil
  206. case sm.CreateSecretOptionsSecretTypeUsernamePasswordConst:
  207. response, _, err := ibm.IBMClient.GetSecret(
  208. &sm.GetSecretOptions{
  209. SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeUsernamePasswordConst),
  210. ID: &secretName,
  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. secretMap := byteArrayMap(secretData)
  218. return secretMap, nil
  219. case sm.CreateSecretOptionsSecretTypeIamCredentialsConst:
  220. response, _, err := ibm.IBMClient.GetSecret(
  221. &sm.GetSecretOptions{
  222. SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeIamCredentialsConst),
  223. ID: &secretName,
  224. })
  225. if err != nil {
  226. return nil, err
  227. }
  228. secret := response.Resources[0].(*sm.SecretResource)
  229. secretData := *secret.APIKey
  230. secretMap := make(map[string][]byte)
  231. secretMap["apikey"] = []byte(secretData)
  232. return secretMap, nil
  233. case sm.CreateSecretOptionsSecretTypeImportedCertConst:
  234. response, _, err := ibm.IBMClient.GetSecret(
  235. &sm.GetSecretOptions{
  236. SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeImportedCertConst),
  237. ID: &secretName,
  238. })
  239. if err != nil {
  240. return nil, err
  241. }
  242. secret := response.Resources[0].(*sm.SecretResource)
  243. secretData := secret.SecretData.(map[string]interface{})
  244. secretMap := byteArrayMap(secretData)
  245. return secretMap, nil
  246. default:
  247. return nil, fmt.Errorf("unknown secret type %s", secretType)
  248. }
  249. }
  250. func byteArrayMap(secretData map[string]interface{}) map[string][]byte {
  251. secretMap := make(map[string][]byte)
  252. for k, v := range secretData {
  253. secretMap[k] = []byte(v.(string))
  254. }
  255. return secretMap
  256. }
  257. func (ibm *providerIBM) Close(ctx context.Context) error {
  258. return nil
  259. }
  260. func (ibm *providerIBM) Validate() error {
  261. return nil
  262. }
  263. func (ibm *providerIBM) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string) (provider.SecretsClient, error) {
  264. storeSpec := store.GetSpec()
  265. ibmSpec := storeSpec.Provider.IBM
  266. iStore := &client{
  267. kube: kube,
  268. store: ibmSpec,
  269. namespace: namespace,
  270. storeKind: store.GetObjectKind().GroupVersionKind().Kind,
  271. }
  272. if err := iStore.setAuth(ctx); err != nil {
  273. return nil, err
  274. }
  275. secretsManager, err := sm.NewSecretsManagerV1(&sm.SecretsManagerV1Options{
  276. URL: *storeSpec.Provider.IBM.ServiceURL,
  277. Authenticator: &core.IamAuthenticator{
  278. ApiKey: string(iStore.credentials),
  279. },
  280. })
  281. // Setup retry options, but only if present
  282. if storeSpec.RetrySettings != nil {
  283. var retryAmount int
  284. var retryDuration time.Duration
  285. if storeSpec.RetrySettings.MaxRetries != nil {
  286. retryAmount = int(*storeSpec.RetrySettings.MaxRetries)
  287. } else {
  288. retryAmount = 3
  289. }
  290. if storeSpec.RetrySettings.RetryInterval != nil {
  291. retryDuration, err = time.ParseDuration(*storeSpec.RetrySettings.RetryInterval)
  292. } else {
  293. retryDuration = 5 * time.Second
  294. }
  295. if err == nil {
  296. secretsManager.Service.EnableRetries(retryAmount, retryDuration)
  297. }
  298. }
  299. if err != nil {
  300. return nil, fmt.Errorf(errIBMClient, err)
  301. }
  302. ibm.IBMClient = secretsManager
  303. return ibm, nil
  304. }
  305. func init() {
  306. schema.Register(&providerIBM{}, &esv1beta1.SecretStoreProvider{
  307. IBM: &esv1beta1.IBMProvider{},
  308. })
  309. }