provider.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  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 vault
  13. import (
  14. "context"
  15. "fmt"
  16. "net/http"
  17. vault "github.com/hashicorp/vault/api"
  18. //nolint
  19. . "github.com/onsi/ginkgo/v2"
  20. //nolint
  21. . "github.com/onsi/gomega"
  22. v1 "k8s.io/api/core/v1"
  23. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  24. "github.com/external-secrets/external-secrets-e2e/framework"
  25. "github.com/external-secrets/external-secrets-e2e/framework/addon"
  26. esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
  27. esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
  28. )
  29. type vaultProvider struct {
  30. url string
  31. client *vault.Client
  32. framework *framework.Framework
  33. }
  34. const (
  35. certAuthProviderName = "cert-auth-provider"
  36. appRoleAuthProviderName = "app-role-provider"
  37. kvv1ProviderName = "kv-v1-provider"
  38. jwtProviderName = "jwt-provider"
  39. jwtProviderSecretName = "jwt-provider-credentials"
  40. jwtK8sProviderName = "jwt-k8s-provider"
  41. kubernetesProviderName = "kubernetes-provider"
  42. referentSecretName = "referent-secret"
  43. referentKey = "referent-secret-key"
  44. )
  45. var (
  46. secretStorePath = "secret"
  47. )
  48. func newVaultProvider(f *framework.Framework) *vaultProvider {
  49. prov := &vaultProvider{
  50. framework: f,
  51. }
  52. BeforeEach(prov.BeforeEach)
  53. return prov
  54. }
  55. // CreateSecret creates a secret in both kv v1 and v2 provider.
  56. func (s *vaultProvider) CreateSecret(key string, val framework.SecretEntry) {
  57. req := s.client.NewRequest(http.MethodPost, fmt.Sprintf("/v1/secret/data/%s", key))
  58. req.BodyBytes = []byte(fmt.Sprintf(`{"data": %s}`, val.Value))
  59. _, err := s.client.RawRequestWithContext(context.Background(), req) //nolint:staticcheck
  60. Expect(err).ToNot(HaveOccurred())
  61. req = s.client.NewRequest(http.MethodPost, fmt.Sprintf("/v1/secret_v1/%s", key))
  62. req.BodyBytes = []byte(val.Value)
  63. _, err = s.client.RawRequestWithContext(context.Background(), req) //nolint:staticcheck
  64. Expect(err).ToNot(HaveOccurred())
  65. }
  66. func (s *vaultProvider) DeleteSecret(key string) {
  67. req := s.client.NewRequest(http.MethodDelete, fmt.Sprintf("/v1/secret/data/%s", key))
  68. _, err := s.client.RawRequestWithContext(context.Background(), req) //nolint:staticcheck
  69. Expect(err).ToNot(HaveOccurred())
  70. req = s.client.NewRequest(http.MethodDelete, fmt.Sprintf("/v1/secret_v1/%s", key))
  71. _, err = s.client.RawRequestWithContext(context.Background(), req) //nolint:staticcheck
  72. Expect(err).ToNot(HaveOccurred())
  73. }
  74. func (s *vaultProvider) BeforeEach() {
  75. ns := s.framework.Namespace.Name
  76. v := addon.NewVault(ns)
  77. s.framework.Install(v)
  78. s.client = v.VaultClient
  79. s.url = v.VaultURL
  80. s.CreateCertStore(v, ns)
  81. s.CreateTokenStore(v, ns)
  82. s.CreateAppRoleStore(v, ns)
  83. s.CreateV1Store(v, ns)
  84. s.CreateJWTStore(v, ns)
  85. s.CreateJWTK8sStore(v, ns)
  86. s.CreateKubernetesAuthStore(v, ns)
  87. s.CreateReferentTokenStore(v, ns)
  88. }
  89. func makeStore(name, ns string, v *addon.Vault) *esv1beta1.SecretStore {
  90. return &esv1beta1.SecretStore{
  91. ObjectMeta: metav1.ObjectMeta{
  92. Name: name,
  93. Namespace: ns,
  94. },
  95. Spec: esv1beta1.SecretStoreSpec{
  96. Provider: &esv1beta1.SecretStoreProvider{
  97. Vault: &esv1beta1.VaultProvider{
  98. Version: esv1beta1.VaultKVStoreV2,
  99. Path: &secretStorePath,
  100. Server: v.VaultURL,
  101. CABundle: v.VaultServerCA,
  102. },
  103. },
  104. },
  105. }
  106. }
  107. func makeClusterStore(name, ns string, v *addon.Vault) *esv1beta1.ClusterSecretStore {
  108. store := makeStore(name, ns, v)
  109. return &esv1beta1.ClusterSecretStore{
  110. ObjectMeta: store.ObjectMeta,
  111. Spec: store.Spec,
  112. }
  113. }
  114. func (s *vaultProvider) CreateCertStore(v *addon.Vault, ns string) {
  115. By("creating a vault secret")
  116. clientCert := v.ClientCert
  117. clientKey := v.ClientKey
  118. vaultCreds := &v1.Secret{
  119. ObjectMeta: metav1.ObjectMeta{
  120. Name: certAuthProviderName,
  121. Namespace: ns,
  122. },
  123. Data: map[string][]byte{
  124. "token": []byte(v.RootToken),
  125. "client_cert": clientCert,
  126. "client_key": clientKey,
  127. },
  128. }
  129. err := s.framework.CRClient.Create(context.Background(), vaultCreds)
  130. Expect(err).ToNot(HaveOccurred())
  131. By("creating an secret store for vault")
  132. secretStore := makeStore(certAuthProviderName, ns, v)
  133. secretStore.Spec.Provider.Vault.Auth = esv1beta1.VaultAuth{
  134. Cert: &esv1beta1.VaultCertAuth{
  135. ClientCert: esmeta.SecretKeySelector{
  136. Name: certAuthProviderName,
  137. Key: "client_cert",
  138. },
  139. SecretRef: esmeta.SecretKeySelector{
  140. Name: certAuthProviderName,
  141. Key: "client_key",
  142. },
  143. },
  144. }
  145. err = s.framework.CRClient.Create(context.Background(), secretStore)
  146. Expect(err).ToNot(HaveOccurred())
  147. }
  148. func (s vaultProvider) CreateTokenStore(v *addon.Vault, ns string) {
  149. vaultCreds := &v1.Secret{
  150. ObjectMeta: metav1.ObjectMeta{
  151. Name: "token-provider",
  152. Namespace: ns,
  153. },
  154. Data: map[string][]byte{
  155. "token": []byte(v.RootToken),
  156. },
  157. }
  158. err := s.framework.CRClient.Create(context.Background(), vaultCreds)
  159. Expect(err).ToNot(HaveOccurred())
  160. secretStore := makeStore(s.framework.Namespace.Name, ns, v)
  161. secretStore.Spec.Provider.Vault.Auth = esv1beta1.VaultAuth{
  162. TokenSecretRef: &esmeta.SecretKeySelector{
  163. Name: "token-provider",
  164. Key: "token",
  165. },
  166. }
  167. err = s.framework.CRClient.Create(context.Background(), secretStore)
  168. Expect(err).ToNot(HaveOccurred())
  169. }
  170. // CreateReferentTokenStore creates a secret in the ExternalSecrets
  171. // namespace and creates a ClusterSecretStore with an empty namespace
  172. // that can be used to test the referent namespace feature.
  173. func (s vaultProvider) CreateReferentTokenStore(v *addon.Vault, ns string) {
  174. referentSecret := &v1.Secret{
  175. ObjectMeta: metav1.ObjectMeta{
  176. Name: referentSecretName,
  177. Namespace: s.framework.Namespace.Name,
  178. },
  179. Data: map[string][]byte{
  180. referentKey: []byte(v.RootToken),
  181. },
  182. }
  183. _, err := s.framework.KubeClientSet.CoreV1().Secrets(s.framework.Namespace.Name).Create(context.Background(), referentSecret, metav1.CreateOptions{})
  184. Expect(err).ToNot(HaveOccurred())
  185. secretStore := makeClusterStore(referentSecretStoreName(s.framework), ns, v)
  186. secretStore.Spec.Provider.Vault.Auth = esv1beta1.VaultAuth{
  187. TokenSecretRef: &esmeta.SecretKeySelector{
  188. Name: referentSecretName,
  189. Key: referentKey,
  190. },
  191. }
  192. err = s.framework.CRClient.Create(context.Background(), secretStore)
  193. Expect(err).ToNot(HaveOccurred())
  194. }
  195. func (s vaultProvider) CreateAppRoleStore(v *addon.Vault, ns string) {
  196. By("creating a vault secret")
  197. vaultCreds := &v1.Secret{
  198. ObjectMeta: metav1.ObjectMeta{
  199. Name: appRoleAuthProviderName,
  200. Namespace: ns,
  201. },
  202. Data: map[string][]byte{
  203. "approle_secret": []byte(v.AppRoleSecret),
  204. },
  205. }
  206. err := s.framework.CRClient.Create(context.Background(), vaultCreds)
  207. Expect(err).ToNot(HaveOccurred())
  208. By("creating an secret store for vault")
  209. secretStore := makeStore(appRoleAuthProviderName, ns, v)
  210. secretStore.Spec.Provider.Vault.Auth = esv1beta1.VaultAuth{
  211. AppRole: &esv1beta1.VaultAppRole{
  212. Path: v.AppRolePath,
  213. RoleID: v.AppRoleID,
  214. SecretRef: esmeta.SecretKeySelector{
  215. Name: appRoleAuthProviderName,
  216. Key: "approle_secret",
  217. },
  218. },
  219. }
  220. err = s.framework.CRClient.Create(context.Background(), secretStore)
  221. Expect(err).ToNot(HaveOccurred())
  222. }
  223. func (s vaultProvider) CreateV1Store(v *addon.Vault, ns string) {
  224. vaultCreds := &v1.Secret{
  225. ObjectMeta: metav1.ObjectMeta{
  226. Name: "v1-provider",
  227. Namespace: ns,
  228. },
  229. Data: map[string][]byte{
  230. "token": []byte(v.RootToken),
  231. },
  232. }
  233. err := s.framework.CRClient.Create(context.Background(), vaultCreds)
  234. Expect(err).ToNot(HaveOccurred())
  235. secretStore := makeStore(kvv1ProviderName, ns, v)
  236. secretV1StorePath := "secret_v1"
  237. secretStore.Spec.Provider.Vault.Version = esv1beta1.VaultKVStoreV1
  238. secretStore.Spec.Provider.Vault.Path = &secretV1StorePath
  239. secretStore.Spec.Provider.Vault.Auth = esv1beta1.VaultAuth{
  240. TokenSecretRef: &esmeta.SecretKeySelector{
  241. Name: "v1-provider",
  242. Key: "token",
  243. },
  244. }
  245. err = s.framework.CRClient.Create(context.Background(), secretStore)
  246. Expect(err).ToNot(HaveOccurred())
  247. }
  248. func (s vaultProvider) CreateJWTStore(v *addon.Vault, ns string) {
  249. vaultCreds := &v1.Secret{
  250. ObjectMeta: metav1.ObjectMeta{
  251. Name: jwtProviderSecretName,
  252. Namespace: ns,
  253. },
  254. Data: map[string][]byte{
  255. "jwt": []byte(v.JWTToken),
  256. },
  257. }
  258. err := s.framework.CRClient.Create(context.Background(), vaultCreds)
  259. Expect(err).ToNot(HaveOccurred())
  260. secretStore := makeStore(jwtProviderName, ns, v)
  261. secretStore.Spec.Provider.Vault.Auth = esv1beta1.VaultAuth{
  262. Jwt: &esv1beta1.VaultJwtAuth{
  263. Path: v.JWTPath,
  264. Role: v.JWTRole,
  265. SecretRef: &esmeta.SecretKeySelector{
  266. Name: jwtProviderSecretName,
  267. Key: "jwt",
  268. },
  269. },
  270. }
  271. err = s.framework.CRClient.Create(context.Background(), secretStore)
  272. Expect(err).ToNot(HaveOccurred())
  273. }
  274. func (s vaultProvider) CreateJWTK8sStore(v *addon.Vault, ns string) {
  275. secretStore := makeStore(jwtK8sProviderName, ns, v)
  276. secretStore.Spec.Provider.Vault.Auth = esv1beta1.VaultAuth{
  277. Jwt: &esv1beta1.VaultJwtAuth{
  278. Path: v.JWTK8sPath,
  279. Role: v.JWTRole,
  280. KubernetesServiceAccountToken: &esv1beta1.VaultKubernetesServiceAccountTokenAuth{
  281. ServiceAccountRef: esmeta.ServiceAccountSelector{
  282. Name: "default",
  283. },
  284. Audiences: &[]string{
  285. "vault.client",
  286. },
  287. },
  288. },
  289. }
  290. err := s.framework.CRClient.Create(context.Background(), secretStore)
  291. Expect(err).ToNot(HaveOccurred())
  292. }
  293. func (s vaultProvider) CreateKubernetesAuthStore(v *addon.Vault, ns string) {
  294. secretStore := makeStore(kubernetesProviderName, ns, v)
  295. secretStore.Spec.Provider.Vault.Auth = esv1beta1.VaultAuth{
  296. Kubernetes: &esv1beta1.VaultKubernetesAuth{
  297. Path: v.KubernetesAuthPath,
  298. Role: v.KubernetesAuthRole,
  299. ServiceAccountRef: &esmeta.ServiceAccountSelector{
  300. Name: "default",
  301. },
  302. },
  303. }
  304. err := s.framework.CRClient.Create(context.Background(), secretStore)
  305. Expect(err).ToNot(HaveOccurred())
  306. }
  307. func referentSecretStoreName(f *framework.Framework) string {
  308. return "referent-provider-" + f.Namespace.Name
  309. }