akeyless.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  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 akeyless
  13. import (
  14. "context"
  15. "encoding/json"
  16. "fmt"
  17. "net/url"
  18. "strconv"
  19. "time"
  20. "github.com/akeylesslabs/akeyless-go/v2"
  21. "k8s.io/client-go/kubernetes"
  22. typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
  23. "sigs.k8s.io/controller-runtime/pkg/client"
  24. ctrlcfg "sigs.k8s.io/controller-runtime/pkg/client/config"
  25. esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
  26. "github.com/external-secrets/external-secrets/pkg/utils"
  27. )
  28. const (
  29. defaultAPIUrl = "https://api.akeyless.io"
  30. )
  31. // https://github.com/external-secrets/external-secrets/issues/644
  32. var _ esv1beta1.SecretsClient = &Akeyless{}
  33. var _ esv1beta1.Provider = &Provider{}
  34. // Provider satisfies the provider interface.
  35. type Provider struct{}
  36. // akeylessBase satisfies the provider.SecretsClient interface.
  37. type akeylessBase struct {
  38. kube client.Client
  39. store esv1beta1.GenericStore
  40. corev1 typedcorev1.CoreV1Interface
  41. namespace string
  42. akeylessGwAPIURL string
  43. RestAPI *akeyless.V2ApiService
  44. }
  45. type Akeyless struct {
  46. Client akeylessVaultInterface
  47. url string
  48. }
  49. type akeylessVaultInterface interface {
  50. GetSecretByType(secretName, token string, version int32) (string, error)
  51. TokenFromSecretRef(ctx context.Context) (string, error)
  52. }
  53. func init() {
  54. esv1beta1.Register(&Provider{}, &esv1beta1.SecretStoreProvider{
  55. Akeyless: &esv1beta1.AkeylessProvider{},
  56. })
  57. }
  58. // NewClient constructs a new secrets client based on the provided store.
  59. func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string) (esv1beta1.SecretsClient, error) {
  60. // controller-runtime/client does not support TokenRequest or other subresource APIs
  61. // so we need to construct our own client and use it to fetch tokens
  62. // (for Kubernetes service account token auth)
  63. restCfg, err := ctrlcfg.GetConfig()
  64. if err != nil {
  65. return nil, err
  66. }
  67. clientset, err := kubernetes.NewForConfig(restCfg)
  68. if err != nil {
  69. return nil, err
  70. }
  71. return newClient(ctx, store, kube, clientset.CoreV1(), namespace)
  72. }
  73. func (p *Provider) ValidateStore(store esv1beta1.GenericStore) error {
  74. storeSpec := store.GetSpec()
  75. akeylessSpec := storeSpec.Provider.Akeyless
  76. akeylessGWApiURL := akeylessSpec.AkeylessGWApiURL
  77. if akeylessGWApiURL != nil && *akeylessGWApiURL != "" {
  78. url, err := url.Parse(*akeylessGWApiURL)
  79. if err != nil {
  80. return fmt.Errorf(errInvalidAkeylessURL)
  81. }
  82. if url.Host == "" {
  83. return fmt.Errorf(errInvalidAkeylessURL)
  84. }
  85. }
  86. if akeylessSpec.Auth.KubernetesAuth != nil {
  87. if akeylessSpec.Auth.KubernetesAuth.ServiceAccountRef != nil {
  88. if err := utils.ValidateReferentServiceAccountSelector(store, *akeylessSpec.Auth.KubernetesAuth.ServiceAccountRef); err != nil {
  89. return fmt.Errorf(errInvalidKubeSA, err)
  90. }
  91. }
  92. if akeylessSpec.Auth.KubernetesAuth.SecretRef != nil {
  93. err := utils.ValidateSecretSelector(store, *akeylessSpec.Auth.KubernetesAuth.SecretRef)
  94. if err != nil {
  95. return err
  96. }
  97. }
  98. if akeylessSpec.Auth.KubernetesAuth.AccessID == "" {
  99. return fmt.Errorf("missing kubernetes auth-method access-id")
  100. }
  101. if akeylessSpec.Auth.KubernetesAuth.K8sConfName == "" {
  102. return fmt.Errorf("missing kubernetes config name")
  103. }
  104. return nil
  105. }
  106. accessID := akeylessSpec.Auth.SecretRef.AccessID
  107. err := utils.ValidateSecretSelector(store, accessID)
  108. if err != nil {
  109. return err
  110. }
  111. if accessID.Name == "" {
  112. return fmt.Errorf(errInvalidAkeylessAccessIDName)
  113. }
  114. if accessID.Key == "" {
  115. return fmt.Errorf(errInvalidAkeylessAccessIDKey)
  116. }
  117. accessType := akeylessSpec.Auth.SecretRef.AccessType
  118. err = utils.ValidateSecretSelector(store, accessType)
  119. if err != nil {
  120. return err
  121. }
  122. accessTypeParam := akeylessSpec.Auth.SecretRef.AccessTypeParam
  123. err = utils.ValidateSecretSelector(store, accessTypeParam)
  124. if err != nil {
  125. return err
  126. }
  127. return nil
  128. }
  129. func newClient(_ context.Context, store esv1beta1.GenericStore, kube client.Client, corev1 typedcorev1.CoreV1Interface, namespace string) (esv1beta1.SecretsClient, error) {
  130. akl := &akeylessBase{
  131. kube: kube,
  132. store: store,
  133. namespace: namespace,
  134. corev1: corev1,
  135. }
  136. spec, err := GetAKeylessProvider(store)
  137. if err != nil {
  138. return nil, err
  139. }
  140. akeylessGwAPIURL := defaultAPIUrl
  141. if spec != nil && spec.AkeylessGWApiURL != nil && *spec.AkeylessGWApiURL != "" {
  142. akeylessGwAPIURL = getV2Url(*spec.AkeylessGWApiURL)
  143. }
  144. if spec.Auth == nil {
  145. return nil, fmt.Errorf("missing Auth in store config")
  146. }
  147. RestAPIClient := akeyless.NewAPIClient(&akeyless.Configuration{
  148. Servers: []akeyless.ServerConfiguration{
  149. {
  150. URL: akeylessGwAPIURL,
  151. },
  152. },
  153. }).V2Api
  154. akl.akeylessGwAPIURL = akeylessGwAPIURL
  155. akl.RestAPI = RestAPIClient
  156. return &Akeyless{Client: akl, url: akeylessGwAPIURL}, nil
  157. }
  158. func (a *Akeyless) Close(ctx context.Context) error {
  159. return nil
  160. }
  161. func (a *Akeyless) Validate() (esv1beta1.ValidationResult, error) {
  162. timeout := 15 * time.Second
  163. url := a.url
  164. if err := utils.NetworkValidate(url, timeout); err != nil {
  165. return esv1beta1.ValidationResultError, err
  166. }
  167. return esv1beta1.ValidationResultReady, nil
  168. }
  169. // Implements store.Client.GetSecret Interface.
  170. // Retrieves a secret with the secret name defined in ref.Name.
  171. func (a *Akeyless) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
  172. if utils.IsNil(a.Client) {
  173. return nil, fmt.Errorf(errUninitalizedAkeylessProvider)
  174. }
  175. token, err := a.Client.TokenFromSecretRef(ctx)
  176. if err != nil {
  177. return nil, err
  178. }
  179. version := int32(0)
  180. if ref.Version != "" {
  181. i, err := strconv.ParseInt(ref.Version, 10, 32)
  182. if err == nil {
  183. version = int32(i)
  184. }
  185. }
  186. value, err := a.Client.GetSecretByType(ref.Key, token, version)
  187. if err != nil {
  188. return nil, err
  189. }
  190. return []byte(value), nil
  191. }
  192. // Empty GetAllSecrets.
  193. func (a *Akeyless) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
  194. // TO be implemented
  195. return nil, fmt.Errorf("GetAllSecrets not implemented")
  196. }
  197. // Implements store.Client.GetSecretMap Interface.
  198. // New version of GetSecretMap.
  199. func (a *Akeyless) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
  200. if utils.IsNil(a.Client) {
  201. return nil, fmt.Errorf(errUninitalizedAkeylessProvider)
  202. }
  203. val, err := a.GetSecret(ctx, ref)
  204. if err != nil {
  205. return nil, err
  206. }
  207. // Maps the json data to a string:string map
  208. kv := make(map[string]string)
  209. err = json.Unmarshal(val, &kv)
  210. if err != nil {
  211. return nil, fmt.Errorf(errJSONSecretUnmarshal, err)
  212. }
  213. // Converts values in K:V pairs into bytes, while leaving keys as strings
  214. secretData := make(map[string][]byte)
  215. for k, v := range kv {
  216. secretData[k] = []byte(v)
  217. }
  218. return secretData, nil
  219. }