provider.go 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  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. "github.com/IBM/go-sdk-core/v5/core"
  19. sm "github.com/IBM/secrets-manager-go-sdk/secretsmanagerv1"
  20. corev1 "k8s.io/api/core/v1"
  21. "k8s.io/apimachinery/pkg/types"
  22. kclient "sigs.k8s.io/controller-runtime/pkg/client"
  23. esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
  24. "github.com/external-secrets/external-secrets/pkg/provider"
  25. "github.com/external-secrets/external-secrets/pkg/provider/schema"
  26. "github.com/external-secrets/external-secrets/pkg/utils"
  27. )
  28. const (
  29. SecretsManagerEndpointEnv = "IBM_SECRETSMANAGER_ENDPOINT"
  30. STSEndpointEnv = "IBM_STS_ENDPOINT"
  31. SSMEndpointEnv = "IBM_SSM_ENDPOINT"
  32. errIBMClient = "cannot setup new ibm client: %w"
  33. errIBMCredSecretName = "invalid IBM SecretStore resource: missing IBM APIKey"
  34. errUninitalizedIBMProvider = "provider IBM is not initialized"
  35. errInvalidClusterStoreMissingSKNamespace = "invalid ClusterStore, missing namespace"
  36. errFetchSAKSecret = "could not fetch SecretAccessKey secret: %w"
  37. errMissingSAK = "missing SecretAccessKey"
  38. errJSONSecretUnmarshal = "unable to unmarshal secret: %w"
  39. )
  40. type SecretManagerClient interface {
  41. GetSecret(getSecretOptions *sm.GetSecretOptions) (result *sm.GetSecret, response *core.DetailedResponse, err error)
  42. }
  43. type providerIBM struct {
  44. IBMClient SecretManagerClient
  45. }
  46. type client struct {
  47. kube kclient.Client
  48. store *esv1alpha1.IBMProvider
  49. namespace string
  50. storeKind string
  51. credentials []byte
  52. }
  53. func (c *client) setAuth(ctx context.Context) error {
  54. credentialsSecret := &corev1.Secret{}
  55. credentialsSecretName := c.store.Auth.SecretRef.SecretAPIKey.Name
  56. if credentialsSecretName == "" {
  57. return fmt.Errorf(errIBMCredSecretName)
  58. }
  59. objectKey := types.NamespacedName{
  60. Name: credentialsSecretName,
  61. Namespace: c.namespace,
  62. }
  63. // only ClusterStore is allowed to set namespace (and then it's required)
  64. if c.storeKind == esv1alpha1.ClusterSecretStoreKind {
  65. if c.store.Auth.SecretRef.SecretAPIKey.Namespace == nil {
  66. return fmt.Errorf(errInvalidClusterStoreMissingSKNamespace)
  67. }
  68. objectKey.Namespace = *c.store.Auth.SecretRef.SecretAPIKey.Namespace
  69. }
  70. err := c.kube.Get(ctx, objectKey, credentialsSecret)
  71. if err != nil {
  72. return fmt.Errorf(errFetchSAKSecret, err)
  73. }
  74. c.credentials = credentialsSecret.Data[c.store.Auth.SecretRef.SecretAPIKey.Key]
  75. if (c.credentials == nil) || (len(c.credentials) == 0) {
  76. return fmt.Errorf(errMissingSAK)
  77. }
  78. return nil
  79. }
  80. func (ibm *providerIBM) GetSecret(ctx context.Context, ref esv1alpha1.ExternalSecretDataRemoteRef) ([]byte, error) {
  81. if utils.IsNil(ibm.IBMClient) {
  82. return nil, fmt.Errorf(errUninitalizedIBMProvider)
  83. }
  84. secretType := sm.GetSecretOptionsSecretTypeArbitraryConst
  85. secretName := ref.Key
  86. nameSplitted := strings.Split(secretName, "/")
  87. if len(nameSplitted) > 1 {
  88. secretType = nameSplitted[0]
  89. secretName = nameSplitted[1]
  90. }
  91. switch secretType {
  92. case sm.GetSecretOptionsSecretTypeArbitraryConst:
  93. response, _, err := ibm.IBMClient.GetSecret(
  94. &sm.GetSecretOptions{
  95. SecretType: core.StringPtr(sm.GetSecretOptionsSecretTypeArbitraryConst),
  96. ID: &secretName,
  97. })
  98. if err != nil {
  99. return nil, err
  100. }
  101. secret := response.Resources[0].(*sm.SecretResource)
  102. secretData := secret.SecretData.(map[string]interface{})
  103. arbitrarySecretPayload := secretData["payload"].(string)
  104. return []byte(arbitrarySecretPayload), nil
  105. case sm.CreateSecretOptionsSecretTypeUsernamePasswordConst:
  106. if ref.Property == "" {
  107. return nil, fmt.Errorf("remoteRef.property required for secret type username_password")
  108. }
  109. response, _, err := ibm.IBMClient.GetSecret(
  110. &sm.GetSecretOptions{
  111. SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeUsernamePasswordConst),
  112. ID: &secretName,
  113. })
  114. if err != nil {
  115. return nil, err
  116. }
  117. secret := response.Resources[0].(*sm.SecretResource)
  118. secretData := secret.SecretData.(map[string]interface{})
  119. if val, ok := secretData[ref.Property]; ok {
  120. return []byte(val.(string)), nil
  121. }
  122. return nil, fmt.Errorf("key %s does not exist in secret %s", ref.Property, ref.Key)
  123. case sm.CreateSecretOptionsSecretTypeIamCredentialsConst:
  124. response, _, err := ibm.IBMClient.GetSecret(
  125. &sm.GetSecretOptions{
  126. SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeIamCredentialsConst),
  127. ID: &secretName,
  128. })
  129. if err != nil {
  130. return nil, err
  131. }
  132. secret := response.Resources[0].(*sm.SecretResource)
  133. secretData := *secret.APIKey
  134. return []byte(secretData), nil
  135. case sm.CreateSecretOptionsSecretTypeImportedCertConst:
  136. if ref.Property == "" {
  137. return nil, fmt.Errorf("remoteRef.property required for secret type imported_cert")
  138. }
  139. response, _, err := ibm.IBMClient.GetSecret(
  140. &sm.GetSecretOptions{
  141. SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeImportedCertConst),
  142. ID: &secretName,
  143. })
  144. if err != nil {
  145. return nil, err
  146. }
  147. secret := response.Resources[0].(*sm.SecretResource)
  148. secretData := secret.SecretData.(map[string]interface{})
  149. if val, ok := secretData[ref.Property]; ok {
  150. return []byte(val.(string)), nil
  151. }
  152. return nil, fmt.Errorf("key %s does not exist in secret %s", ref.Property, ref.Key)
  153. default:
  154. return nil, fmt.Errorf("unknown secret type %s", secretType)
  155. }
  156. }
  157. func (ibm *providerIBM) GetSecretMap(ctx context.Context, ref esv1alpha1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
  158. if utils.IsNil(ibm.IBMClient) {
  159. return nil, fmt.Errorf(errUninitalizedIBMProvider)
  160. }
  161. secretType := sm.GetSecretOptionsSecretTypeArbitraryConst
  162. secretName := ref.Key
  163. nameSplitted := strings.Split(secretName, "/")
  164. if len(nameSplitted) > 1 {
  165. secretType = nameSplitted[0]
  166. secretName = nameSplitted[1]
  167. }
  168. switch secretType {
  169. case sm.GetSecretOptionsSecretTypeArbitraryConst:
  170. response, _, err := ibm.IBMClient.GetSecret(
  171. &sm.GetSecretOptions{
  172. SecretType: core.StringPtr(sm.GetSecretOptionsSecretTypeArbitraryConst),
  173. ID: &ref.Key,
  174. })
  175. if err != nil {
  176. return nil, err
  177. }
  178. secret := response.Resources[0].(*sm.SecretResource)
  179. secretData := secret.SecretData.(map[string]interface{})
  180. arbitrarySecretPayload := secretData["payload"].(string)
  181. kv := make(map[string]string)
  182. err = json.Unmarshal([]byte(arbitrarySecretPayload), &kv)
  183. if err != nil {
  184. return nil, fmt.Errorf(errJSONSecretUnmarshal, err)
  185. }
  186. secretMap := make(map[string][]byte)
  187. for k, v := range kv {
  188. secretMap[k] = []byte(v)
  189. }
  190. return secretMap, nil
  191. case sm.CreateSecretOptionsSecretTypeUsernamePasswordConst:
  192. response, _, err := ibm.IBMClient.GetSecret(
  193. &sm.GetSecretOptions{
  194. SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeUsernamePasswordConst),
  195. ID: &secretName,
  196. })
  197. if err != nil {
  198. return nil, err
  199. }
  200. secret := response.Resources[0].(*sm.SecretResource)
  201. secretData := secret.SecretData.(map[string]interface{})
  202. secretMap := make(map[string][]byte)
  203. for k, v := range secretData {
  204. secretMap[k] = []byte(v.(string))
  205. }
  206. return secretMap, nil
  207. case sm.CreateSecretOptionsSecretTypeIamCredentialsConst:
  208. response, _, err := ibm.IBMClient.GetSecret(
  209. &sm.GetSecretOptions{
  210. SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeIamCredentialsConst),
  211. ID: &secretName,
  212. })
  213. if err != nil {
  214. return nil, err
  215. }
  216. secret := response.Resources[0].(*sm.SecretResource)
  217. secretData := *secret.APIKey
  218. secretMap := make(map[string][]byte)
  219. secretMap["apikey"] = []byte(secretData)
  220. return secretMap, nil
  221. case sm.CreateSecretOptionsSecretTypeImportedCertConst:
  222. response, _, err := ibm.IBMClient.GetSecret(
  223. &sm.GetSecretOptions{
  224. SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeImportedCertConst),
  225. ID: &secretName,
  226. })
  227. if err != nil {
  228. return nil, err
  229. }
  230. secret := response.Resources[0].(*sm.SecretResource)
  231. secretData := secret.SecretData.(map[string]interface{})
  232. secretMap := make(map[string][]byte)
  233. for k, v := range secretData {
  234. secretMap[k] = []byte(v.(string))
  235. }
  236. return secretMap, nil
  237. default:
  238. return nil, fmt.Errorf("unknown secret type %s", secretType)
  239. }
  240. }
  241. func (ibm *providerIBM) Close(ctx context.Context) error {
  242. return nil
  243. }
  244. func (ibm *providerIBM) NewClient(ctx context.Context, store esv1alpha1.GenericStore, kube kclient.Client, namespace string) (provider.SecretsClient, error) {
  245. storeSpec := store.GetSpec()
  246. ibmSpec := storeSpec.Provider.IBM
  247. iStore := &client{
  248. kube: kube,
  249. store: ibmSpec,
  250. namespace: namespace,
  251. storeKind: store.GetObjectKind().GroupVersionKind().Kind,
  252. }
  253. if err := iStore.setAuth(ctx); err != nil {
  254. return nil, err
  255. }
  256. secretsManager, err := sm.NewSecretsManagerV1(&sm.SecretsManagerV1Options{
  257. URL: *storeSpec.Provider.IBM.ServiceURL,
  258. Authenticator: &core.IamAuthenticator{
  259. ApiKey: string(iStore.credentials),
  260. },
  261. })
  262. if err != nil {
  263. return nil, fmt.Errorf(errIBMClient, err)
  264. }
  265. ibm.IBMClient = secretsManager
  266. return ibm, nil
  267. }
  268. func init() {
  269. schema.Register(&providerIBM{}, &esv1alpha1.SecretStoreProvider{
  270. IBM: &esv1alpha1.IBMProvider{},
  271. })
  272. }