akeyless.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  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. "crypto/tls"
  16. "crypto/x509"
  17. "encoding/json"
  18. "errors"
  19. "fmt"
  20. "net/http"
  21. "net/url"
  22. "strconv"
  23. "strings"
  24. "time"
  25. "github.com/akeylesslabs/akeyless-go/v3"
  26. "github.com/tidwall/gjson"
  27. corev1 "k8s.io/api/core/v1"
  28. "k8s.io/client-go/kubernetes"
  29. typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
  30. "sigs.k8s.io/controller-runtime/pkg/client"
  31. ctrlcfg "sigs.k8s.io/controller-runtime/pkg/client/config"
  32. "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
  33. esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
  34. esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
  35. "github.com/external-secrets/external-secrets/pkg/find"
  36. "github.com/external-secrets/external-secrets/pkg/utils"
  37. "github.com/external-secrets/external-secrets/pkg/utils/resolvers"
  38. )
  39. const (
  40. defaultAPIUrl = "https://api.akeyless.io"
  41. )
  42. // https://github.com/external-secrets/external-secrets/issues/644
  43. var _ esv1beta1.SecretsClient = &Akeyless{}
  44. var _ esv1beta1.Provider = &Provider{}
  45. // Provider satisfies the provider interface.
  46. type Provider struct{}
  47. // akeylessBase satisfies the provider.SecretsClient interface.
  48. type akeylessBase struct {
  49. kube client.Client
  50. store esv1beta1.GenericStore
  51. storeKind string
  52. corev1 typedcorev1.CoreV1Interface
  53. namespace string
  54. akeylessGwAPIURL string
  55. RestAPI *akeyless.V2ApiService
  56. }
  57. type Akeyless struct {
  58. Client akeylessVaultInterface
  59. url string
  60. }
  61. type Item struct {
  62. ItemName string `json:"item_name"`
  63. ItemType string `json:"item_type"`
  64. LastVersion int32 `json:"last_version"`
  65. }
  66. type akeylessVaultInterface interface {
  67. GetSecretByType(ctx context.Context, secretName, token string, version int32) (string, error)
  68. TokenFromSecretRef(ctx context.Context) (string, error)
  69. ListSecrets(ctx context.Context, path, tag, token string) ([]string, error)
  70. }
  71. func init() {
  72. esv1beta1.Register(&Provider{}, &esv1beta1.SecretStoreProvider{
  73. Akeyless: &esv1beta1.AkeylessProvider{},
  74. })
  75. }
  76. // Capabilities return the provider supported capabilities (ReadOnly, WriteOnly, ReadWrite).
  77. func (p *Provider) Capabilities() esv1beta1.SecretStoreCapabilities {
  78. return esv1beta1.SecretStoreReadOnly
  79. }
  80. // NewClient constructs a new secrets client based on the provided store.
  81. func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string) (esv1beta1.SecretsClient, error) {
  82. // controller-runtime/client does not support TokenRequest or other subresource APIs
  83. // so we need to construct our own client and use it to fetch tokens
  84. // (for Kubernetes service account token auth)
  85. restCfg, err := ctrlcfg.GetConfig()
  86. if err != nil {
  87. return nil, err
  88. }
  89. clientset, err := kubernetes.NewForConfig(restCfg)
  90. if err != nil {
  91. return nil, err
  92. }
  93. return newClient(ctx, store, kube, clientset.CoreV1(), namespace)
  94. }
  95. func (p *Provider) ValidateStore(store esv1beta1.GenericStore) (admission.Warnings, error) {
  96. storeSpec := store.GetSpec()
  97. akeylessSpec := storeSpec.Provider.Akeyless
  98. akeylessGWApiURL := akeylessSpec.AkeylessGWApiURL
  99. if akeylessGWApiURL != nil && *akeylessGWApiURL != "" {
  100. url, err := url.Parse(*akeylessGWApiURL)
  101. if err != nil {
  102. return nil, fmt.Errorf(errInvalidAkeylessURL)
  103. }
  104. if url.Host == "" {
  105. return nil, fmt.Errorf(errInvalidAkeylessURL)
  106. }
  107. }
  108. if akeylessSpec.Auth.KubernetesAuth != nil {
  109. if akeylessSpec.Auth.KubernetesAuth.ServiceAccountRef != nil {
  110. if err := utils.ValidateReferentServiceAccountSelector(store, *akeylessSpec.Auth.KubernetesAuth.ServiceAccountRef); err != nil {
  111. return nil, fmt.Errorf(errInvalidKubeSA, err)
  112. }
  113. }
  114. if akeylessSpec.Auth.KubernetesAuth.SecretRef != nil {
  115. err := utils.ValidateSecretSelector(store, *akeylessSpec.Auth.KubernetesAuth.SecretRef)
  116. if err != nil {
  117. return nil, err
  118. }
  119. }
  120. if akeylessSpec.Auth.KubernetesAuth.AccessID == "" {
  121. return nil, fmt.Errorf("missing kubernetes auth-method access-id")
  122. }
  123. if akeylessSpec.Auth.KubernetesAuth.K8sConfName == "" {
  124. return nil, fmt.Errorf("missing kubernetes config name")
  125. }
  126. return nil, nil
  127. }
  128. accessID := akeylessSpec.Auth.SecretRef.AccessID
  129. err := utils.ValidateSecretSelector(store, accessID)
  130. if err != nil {
  131. return nil, err
  132. }
  133. if accessID.Name == "" {
  134. return nil, fmt.Errorf(errInvalidAkeylessAccessIDName)
  135. }
  136. if accessID.Key == "" {
  137. return nil, fmt.Errorf(errInvalidAkeylessAccessIDKey)
  138. }
  139. accessType := akeylessSpec.Auth.SecretRef.AccessType
  140. err = utils.ValidateSecretSelector(store, accessType)
  141. if err != nil {
  142. return nil, err
  143. }
  144. accessTypeParam := akeylessSpec.Auth.SecretRef.AccessTypeParam
  145. err = utils.ValidateSecretSelector(store, accessTypeParam)
  146. if err != nil {
  147. return nil, err
  148. }
  149. return nil, nil
  150. }
  151. func newClient(_ context.Context, store esv1beta1.GenericStore, kube client.Client, corev1 typedcorev1.CoreV1Interface, namespace string) (esv1beta1.SecretsClient, error) {
  152. akl := &akeylessBase{
  153. kube: kube,
  154. store: store,
  155. namespace: namespace,
  156. corev1: corev1,
  157. storeKind: store.GetObjectKind().GroupVersionKind().Kind,
  158. }
  159. spec, err := GetAKeylessProvider(store)
  160. if err != nil {
  161. return nil, err
  162. }
  163. akeylessGwAPIURL := defaultAPIUrl
  164. if spec != nil && spec.AkeylessGWApiURL != nil && *spec.AkeylessGWApiURL != "" {
  165. akeylessGwAPIURL = getV2Url(*spec.AkeylessGWApiURL)
  166. }
  167. if spec.Auth == nil {
  168. return nil, fmt.Errorf("missing Auth in store config")
  169. }
  170. client, err := akl.getAkeylessHTTPClient(spec)
  171. if err != nil {
  172. return nil, err
  173. }
  174. RestAPIClient := akeyless.NewAPIClient(&akeyless.Configuration{
  175. HTTPClient: client,
  176. Servers: []akeyless.ServerConfiguration{
  177. {
  178. URL: akeylessGwAPIURL,
  179. },
  180. },
  181. }).V2Api
  182. akl.akeylessGwAPIURL = akeylessGwAPIURL
  183. akl.RestAPI = RestAPIClient
  184. return &Akeyless{Client: akl, url: akeylessGwAPIURL}, nil
  185. }
  186. func (a *Akeyless) Close(_ context.Context) error {
  187. return nil
  188. }
  189. func (a *Akeyless) Validate() (esv1beta1.ValidationResult, error) {
  190. timeout := 15 * time.Second
  191. url := a.url
  192. if err := utils.NetworkValidate(url, timeout); err != nil {
  193. return esv1beta1.ValidationResultError, err
  194. }
  195. return esv1beta1.ValidationResultReady, nil
  196. }
  197. func (a *Akeyless) PushSecret(_ context.Context, _ *corev1.Secret, _ esv1beta1.PushSecretData) error {
  198. return fmt.Errorf("not implemented")
  199. }
  200. func (a *Akeyless) DeleteSecret(_ context.Context, _ esv1beta1.PushSecretRemoteRef) error {
  201. return fmt.Errorf("not implemented")
  202. }
  203. // Implements store.Client.GetSecret Interface.
  204. // Retrieves a secret with the secret name defined in ref.Name.
  205. func (a *Akeyless) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
  206. if utils.IsNil(a.Client) {
  207. return nil, fmt.Errorf(errUninitalizedAkeylessProvider)
  208. }
  209. token, err := a.Client.TokenFromSecretRef(ctx)
  210. if err != nil {
  211. return nil, err
  212. }
  213. version := int32(0)
  214. if ref.Version != "" {
  215. i, err := strconv.ParseInt(ref.Version, 10, 32)
  216. if err == nil {
  217. version = int32(i)
  218. }
  219. }
  220. value, err := a.Client.GetSecretByType(ctx, ref.Key, token, version)
  221. if err != nil {
  222. return nil, err
  223. }
  224. if ref.Property == "" {
  225. if value != "" {
  226. return []byte(value), nil
  227. }
  228. return nil, fmt.Errorf("invalid value received, found no value string : %s", ref.Key)
  229. }
  230. // We need to search if a given key with a . exists before using gjson operations.
  231. idx := strings.Index(ref.Property, ".")
  232. if idx > -1 {
  233. refProperty := strings.ReplaceAll(ref.Property, ".", "\\.")
  234. val := gjson.Get(value, refProperty)
  235. if val.Exists() {
  236. return []byte(val.String()), nil
  237. }
  238. }
  239. val := gjson.Get(value, ref.Property)
  240. if !val.Exists() {
  241. return nil, fmt.Errorf("key %s does not exist in value %s", ref.Property, ref.Key)
  242. }
  243. return []byte(val.String()), nil
  244. }
  245. // Implements store.Client.GetAllSecrets Interface.
  246. // Retrieves a all secrets with defined in ref.Name or tags.
  247. func (a *Akeyless) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
  248. if utils.IsNil(a.Client) {
  249. return nil, fmt.Errorf(errUninitalizedAkeylessProvider)
  250. }
  251. searchPath := ""
  252. if ref.Path != nil {
  253. searchPath = *ref.Path
  254. if !strings.HasPrefix(searchPath, "/") {
  255. searchPath = "/" + searchPath
  256. }
  257. if !strings.HasSuffix(searchPath, "/") {
  258. searchPath += "/"
  259. }
  260. }
  261. token, err := a.Client.TokenFromSecretRef(ctx)
  262. if err != nil {
  263. return nil, err
  264. }
  265. if ref.Name != nil {
  266. potentialSecrets, err := a.Client.ListSecrets(ctx, searchPath, "", token)
  267. if err != nil {
  268. return nil, err
  269. }
  270. if len(potentialSecrets) == 0 {
  271. return nil, nil
  272. }
  273. return a.findSecretsFromName(ctx, potentialSecrets, *ref.Name, token)
  274. }
  275. if len(ref.Tags) > 0 {
  276. var potentialSecretsName []string
  277. for _, v := range ref.Tags {
  278. potentialSecrets, err := a.Client.ListSecrets(ctx, searchPath, v, token)
  279. if err != nil {
  280. return nil, err
  281. }
  282. if len(potentialSecrets) > 0 {
  283. potentialSecretsName = append(potentialSecretsName, potentialSecrets...)
  284. }
  285. }
  286. if len(potentialSecretsName) == 0 {
  287. return nil, nil
  288. }
  289. return a.getSecrets(ctx, potentialSecretsName, token)
  290. }
  291. return nil, errors.New("unexpected find operator")
  292. }
  293. func (a *Akeyless) getSecrets(ctx context.Context, candidates []string, token string) (map[string][]byte, error) {
  294. secrets := make(map[string][]byte)
  295. for _, name := range candidates {
  296. secretValue, err := a.Client.GetSecretByType(ctx, name, token, 0)
  297. if err != nil {
  298. return nil, err
  299. }
  300. if secretValue != "" {
  301. secrets[name] = []byte(secretValue)
  302. }
  303. }
  304. return secrets, nil
  305. }
  306. func (a *Akeyless) findSecretsFromName(ctx context.Context, candidates []string, ref esv1beta1.FindName, token string) (map[string][]byte, error) {
  307. secrets := make(map[string][]byte)
  308. matcher, err := find.New(ref)
  309. if err != nil {
  310. return nil, err
  311. }
  312. for _, name := range candidates {
  313. ok := matcher.MatchName(name)
  314. if ok {
  315. secretValue, err := a.Client.GetSecretByType(ctx, name, token, 0)
  316. if err != nil {
  317. return nil, err
  318. }
  319. if secretValue != "" {
  320. secrets[name] = []byte(secretValue)
  321. }
  322. }
  323. }
  324. return secrets, nil
  325. }
  326. // Implements store.Client.GetSecretMap Interface.
  327. // New version of GetSecretMap.
  328. func (a *Akeyless) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
  329. if utils.IsNil(a.Client) {
  330. return nil, fmt.Errorf(errUninitalizedAkeylessProvider)
  331. }
  332. val, err := a.GetSecret(ctx, ref)
  333. if err != nil {
  334. return nil, err
  335. }
  336. // Maps the json data to a string:string map
  337. kv := make(map[string]string)
  338. err = json.Unmarshal(val, &kv)
  339. if err != nil {
  340. return nil, fmt.Errorf(errJSONSecretUnmarshal, err)
  341. }
  342. // Converts values in K:V pairs into bytes, while leaving keys as strings
  343. secretData := make(map[string][]byte)
  344. for k, v := range kv {
  345. secretData[k] = []byte(v)
  346. }
  347. return secretData, nil
  348. }
  349. func (a *akeylessBase) getAkeylessHTTPClient(provider *esv1beta1.AkeylessProvider) (*http.Client, error) {
  350. client := &http.Client{Timeout: 30 * time.Second}
  351. if len(provider.CABundle) == 0 && provider.CAProvider == nil {
  352. return client, nil
  353. }
  354. caCertPool, err := a.getCACertPool(provider)
  355. if err != nil {
  356. return nil, err
  357. }
  358. tlsConf := &tls.Config{
  359. RootCAs: caCertPool,
  360. MinVersion: tls.VersionTLS12,
  361. }
  362. client.Transport = &http.Transport{TLSClientConfig: tlsConf}
  363. return client, nil
  364. }
  365. func (a *akeylessBase) getCACertPool(provider *esv1beta1.AkeylessProvider) (*x509.CertPool, error) {
  366. caCertPool := x509.NewCertPool()
  367. if len(provider.CABundle) > 0 {
  368. pem, err := base64decode(provider.CABundle)
  369. if err != nil {
  370. pem = provider.CABundle
  371. }
  372. ok := caCertPool.AppendCertsFromPEM(pem)
  373. if !ok {
  374. return nil, fmt.Errorf("failed to append caBundle")
  375. }
  376. }
  377. if provider.CAProvider != nil &&
  378. a.storeKind == esv1beta1.ClusterSecretStoreKind &&
  379. provider.CAProvider.Namespace == nil {
  380. return nil, fmt.Errorf("missing namespace on caProvider secret")
  381. }
  382. if provider.CAProvider != nil {
  383. var cert []byte
  384. var err error
  385. switch provider.CAProvider.Type {
  386. case esv1beta1.CAProviderTypeSecret:
  387. cert, err = a.getCertFromSecret(provider)
  388. case esv1beta1.CAProviderTypeConfigMap:
  389. cert, err = a.getCertFromConfigMap(provider)
  390. default:
  391. err = fmt.Errorf("unknown CAProvider type: %s", provider.CAProvider.Type)
  392. }
  393. if err != nil {
  394. return nil, err
  395. }
  396. pem, err := base64decode(cert)
  397. if err != nil {
  398. pem = cert
  399. }
  400. ok := caCertPool.AppendCertsFromPEM(pem)
  401. if !ok {
  402. return nil, fmt.Errorf("failed to append caBundle")
  403. }
  404. }
  405. return caCertPool, nil
  406. }
  407. func (a *akeylessBase) getCertFromSecret(provider *esv1beta1.AkeylessProvider) ([]byte, error) {
  408. secretRef := esmeta.SecretKeySelector{
  409. Name: provider.CAProvider.Name,
  410. Key: provider.CAProvider.Key,
  411. }
  412. if provider.CAProvider.Namespace != nil {
  413. secretRef.Namespace = provider.CAProvider.Namespace
  414. }
  415. ctx := context.Background()
  416. cert, err := resolvers.SecretKeyRef(ctx, a.kube, a.storeKind, a.namespace, &secretRef)
  417. if err != nil {
  418. return nil, err
  419. }
  420. return []byte(cert), nil
  421. }
  422. func (a *akeylessBase) getCertFromConfigMap(provider *esv1beta1.AkeylessProvider) ([]byte, error) {
  423. objKey := client.ObjectKey{
  424. Name: provider.CAProvider.Name,
  425. }
  426. if provider.CAProvider.Namespace != nil {
  427. objKey.Namespace = *provider.CAProvider.Namespace
  428. }
  429. configMapRef := &corev1.ConfigMap{}
  430. ctx := context.Background()
  431. err := a.kube.Get(ctx, objKey, configMapRef)
  432. if err != nil {
  433. return nil, fmt.Errorf("failed to get caProvider secret %s: %w", objKey.Name, err)
  434. }
  435. val, ok := configMapRef.Data[provider.CAProvider.Key]
  436. if !ok {
  437. return nil, fmt.Errorf("failed to get caProvider configMap %s -> %s", objKey.Name, provider.CAProvider.Key)
  438. }
  439. return []byte(val), nil
  440. }