provider.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349
  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. return getArbitrarySecret(ibm, &secretName)
  94. case sm.CreateSecretOptionsSecretTypeUsernamePasswordConst:
  95. if ref.Property == "" {
  96. return nil, fmt.Errorf("remoteRef.property required for secret type username_password")
  97. }
  98. return getUsernamePasswordSecret(ibm, &secretName, ref)
  99. case sm.CreateSecretOptionsSecretTypeIamCredentialsConst:
  100. return getIamCredentialsSecret(ibm, &secretName)
  101. case sm.CreateSecretOptionsSecretTypeImportedCertConst:
  102. if ref.Property == "" {
  103. return nil, fmt.Errorf("remoteRef.property required for secret type imported_cert")
  104. }
  105. return getImportCertSecret(ibm, &secretName, ref)
  106. default:
  107. return nil, fmt.Errorf("unknown secret type %s", secretType)
  108. }
  109. }
  110. func getArbitrarySecret(ibm *providerIBM, secretName *string) ([]byte, error) {
  111. response, _, err := ibm.IBMClient.GetSecret(
  112. &sm.GetSecretOptions{
  113. SecretType: core.StringPtr(sm.GetSecretOptionsSecretTypeArbitraryConst),
  114. ID: secretName,
  115. })
  116. if err != nil {
  117. return nil, err
  118. }
  119. secret := response.Resources[0].(*sm.SecretResource)
  120. secretData := secret.SecretData.(map[string]interface{})
  121. arbitrarySecretPayload := secretData["payload"].(string)
  122. return []byte(arbitrarySecretPayload), nil
  123. }
  124. func getImportCertSecret(ibm *providerIBM, secretName *string, ref esv1alpha1.ExternalSecretDataRemoteRef) ([]byte, error) {
  125. response, _, err := ibm.IBMClient.GetSecret(
  126. &sm.GetSecretOptions{
  127. SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeImportedCertConst),
  128. ID: secretName,
  129. })
  130. if err != nil {
  131. return nil, err
  132. }
  133. secret := response.Resources[0].(*sm.SecretResource)
  134. secretData := secret.SecretData.(map[string]interface{})
  135. if val, ok := secretData[ref.Property]; ok {
  136. return []byte(val.(string)), nil
  137. }
  138. return nil, fmt.Errorf("key %s does not exist in secret %s", ref.Property, ref.Key)
  139. }
  140. func getIamCredentialsSecret(ibm *providerIBM, secretName *string) ([]byte, error) {
  141. response, _, err := ibm.IBMClient.GetSecret(
  142. &sm.GetSecretOptions{
  143. SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeIamCredentialsConst),
  144. ID: secretName,
  145. })
  146. if err != nil {
  147. return nil, err
  148. }
  149. secret := response.Resources[0].(*sm.SecretResource)
  150. secretData := *secret.APIKey
  151. return []byte(secretData), nil
  152. }
  153. func getUsernamePasswordSecret(ibm *providerIBM, secretName *string, ref esv1alpha1.ExternalSecretDataRemoteRef) ([]byte, error) {
  154. response, _, err := ibm.IBMClient.GetSecret(
  155. &sm.GetSecretOptions{
  156. SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeUsernamePasswordConst),
  157. ID: secretName,
  158. })
  159. if err != nil {
  160. return nil, err
  161. }
  162. secret := response.Resources[0].(*sm.SecretResource)
  163. secretData := secret.SecretData.(map[string]interface{})
  164. if val, ok := secretData[ref.Property]; ok {
  165. return []byte(val.(string)), nil
  166. }
  167. return nil, fmt.Errorf("key %s does not exist in secret %s", ref.Property, ref.Key)
  168. }
  169. func (ibm *providerIBM) GetSecretMap(ctx context.Context, ref esv1alpha1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
  170. if utils.IsNil(ibm.IBMClient) {
  171. return nil, fmt.Errorf(errUninitalizedIBMProvider)
  172. }
  173. secretType := sm.GetSecretOptionsSecretTypeArbitraryConst
  174. secretName := ref.Key
  175. nameSplitted := strings.Split(secretName, "/")
  176. if len(nameSplitted) > 1 {
  177. secretType = nameSplitted[0]
  178. secretName = nameSplitted[1]
  179. }
  180. switch secretType {
  181. case sm.GetSecretOptionsSecretTypeArbitraryConst:
  182. response, _, err := ibm.IBMClient.GetSecret(
  183. &sm.GetSecretOptions{
  184. SecretType: core.StringPtr(sm.GetSecretOptionsSecretTypeArbitraryConst),
  185. ID: &ref.Key,
  186. })
  187. if err != nil {
  188. return nil, err
  189. }
  190. secret := response.Resources[0].(*sm.SecretResource)
  191. secretData := secret.SecretData.(map[string]interface{})
  192. arbitrarySecretPayload := secretData["payload"].(string)
  193. kv := make(map[string]interface{})
  194. err = json.Unmarshal([]byte(arbitrarySecretPayload), &kv)
  195. if err != nil {
  196. return nil, fmt.Errorf(errJSONSecretUnmarshal, err)
  197. }
  198. secretMap := byteArrayMap(kv)
  199. return secretMap, nil
  200. case sm.CreateSecretOptionsSecretTypeUsernamePasswordConst:
  201. response, _, err := ibm.IBMClient.GetSecret(
  202. &sm.GetSecretOptions{
  203. SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeUsernamePasswordConst),
  204. ID: &secretName,
  205. })
  206. if err != nil {
  207. return nil, err
  208. }
  209. secret := response.Resources[0].(*sm.SecretResource)
  210. secretData := secret.SecretData.(map[string]interface{})
  211. secretMap := byteArrayMap(secretData)
  212. return secretMap, nil
  213. case sm.CreateSecretOptionsSecretTypeIamCredentialsConst:
  214. response, _, err := ibm.IBMClient.GetSecret(
  215. &sm.GetSecretOptions{
  216. SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeIamCredentialsConst),
  217. ID: &secretName,
  218. })
  219. if err != nil {
  220. return nil, err
  221. }
  222. secret := response.Resources[0].(*sm.SecretResource)
  223. secretData := *secret.APIKey
  224. secretMap := make(map[string][]byte)
  225. secretMap["apikey"] = []byte(secretData)
  226. return secretMap, nil
  227. case sm.CreateSecretOptionsSecretTypeImportedCertConst:
  228. response, _, err := ibm.IBMClient.GetSecret(
  229. &sm.GetSecretOptions{
  230. SecretType: core.StringPtr(sm.CreateSecretOptionsSecretTypeImportedCertConst),
  231. ID: &secretName,
  232. })
  233. if err != nil {
  234. return nil, err
  235. }
  236. secret := response.Resources[0].(*sm.SecretResource)
  237. secretData := secret.SecretData.(map[string]interface{})
  238. secretMap := byteArrayMap(secretData)
  239. return secretMap, nil
  240. default:
  241. return nil, fmt.Errorf("unknown secret type %s", secretType)
  242. }
  243. }
  244. func byteArrayMap(secretData map[string]interface{}) map[string][]byte {
  245. secretMap := make(map[string][]byte)
  246. for k, v := range secretData {
  247. secretMap[k] = []byte(v.(string))
  248. }
  249. return secretMap
  250. }
  251. func (ibm *providerIBM) Close(ctx context.Context) error {
  252. return nil
  253. }
  254. func (ibm *providerIBM) NewClient(ctx context.Context, store esv1alpha1.GenericStore, kube kclient.Client, namespace string) (provider.SecretsClient, error) {
  255. storeSpec := store.GetSpec()
  256. ibmSpec := storeSpec.Provider.IBM
  257. iStore := &client{
  258. kube: kube,
  259. store: ibmSpec,
  260. namespace: namespace,
  261. storeKind: store.GetObjectKind().GroupVersionKind().Kind,
  262. }
  263. if err := iStore.setAuth(ctx); err != nil {
  264. return nil, err
  265. }
  266. secretsManager, err := sm.NewSecretsManagerV1(&sm.SecretsManagerV1Options{
  267. URL: *storeSpec.Provider.IBM.ServiceURL,
  268. Authenticator: &core.IamAuthenticator{
  269. ApiKey: string(iStore.credentials),
  270. },
  271. })
  272. if err != nil {
  273. return nil, fmt.Errorf(errIBMClient, err)
  274. }
  275. ibm.IBMClient = secretsManager
  276. return ibm, nil
  277. }
  278. func init() {
  279. schema.Register(&providerIBM{}, &esv1alpha1.SecretStoreProvider{
  280. IBM: &esv1alpha1.IBMProvider{},
  281. })
  282. }