provider.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  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. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  27. esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
  28. )
  29. type vaultProvider struct {
  30. url string
  31. mtlsUrl string
  32. client *vault.Client
  33. framework *framework.Framework
  34. }
  35. type StoreCustomizer = func(provider *vaultProvider, secret *v1.Secret, secretStore *metav1.ObjectMeta, secretStoreSpec *esv1.SecretStoreSpec, isClusterStore bool)
  36. const (
  37. clientTlsCertName = "vault-client-tls"
  38. certAuthProviderName = "cert-auth-provider"
  39. appRoleAuthProviderName = "app-role-provider"
  40. kvv1ProviderName = "kv-v1-provider"
  41. jwtProviderName = "jwt-provider"
  42. jwtProviderSecretName = "jwt-provider-credentials"
  43. jwtK8sProviderName = "jwt-k8s-provider"
  44. kubernetesProviderName = "kubernetes-provider"
  45. referentSecretName = "referent-secret"
  46. referentKey = "referent-secret-key"
  47. )
  48. var (
  49. secretStorePath = "secret"
  50. mtlsSuffix = "-mtls"
  51. invalidMtlSuffix = "-invalid-mtls"
  52. )
  53. func newVaultProvider(f *framework.Framework) *vaultProvider {
  54. prov := &vaultProvider{
  55. framework: f,
  56. }
  57. BeforeEach(prov.BeforeEach)
  58. AfterEach(prov.AfterEach)
  59. return prov
  60. }
  61. // CreateSecret creates a secret in both kv v1 and v2 provider.
  62. func (s *vaultProvider) CreateSecret(key string, val framework.SecretEntry) {
  63. req := s.client.NewRequest(http.MethodPost, fmt.Sprintf("/v1/secret/data/%s", key))
  64. req.BodyBytes = []byte(fmt.Sprintf(`{"data": %s}`, val.Value))
  65. _, err := s.client.RawRequestWithContext(context.Background(), req) //nolint:staticcheck
  66. Expect(err).ToNot(HaveOccurred())
  67. req = s.client.NewRequest(http.MethodPost, fmt.Sprintf("/v1/secret_v1/%s", key))
  68. req.BodyBytes = []byte(val.Value)
  69. _, err = s.client.RawRequestWithContext(context.Background(), req) //nolint:staticcheck
  70. Expect(err).ToNot(HaveOccurred())
  71. }
  72. func (s *vaultProvider) DeleteSecret(key string) {
  73. req := s.client.NewRequest(http.MethodDelete, fmt.Sprintf("/v1/secret/data/%s", key))
  74. _, err := s.client.RawRequestWithContext(context.Background(), req) //nolint:staticcheck
  75. Expect(err).ToNot(HaveOccurred())
  76. req = s.client.NewRequest(http.MethodDelete, fmt.Sprintf("/v1/secret_v1/%s", key))
  77. _, err = s.client.RawRequestWithContext(context.Background(), req) //nolint:staticcheck
  78. Expect(err).ToNot(HaveOccurred())
  79. }
  80. func (s *vaultProvider) BeforeEach() {
  81. ns := s.framework.Namespace.Name
  82. v := addon.NewVault(ns)
  83. s.framework.Install(v)
  84. s.client = v.VaultClient
  85. s.url = v.VaultURL
  86. s.mtlsUrl = v.VaultMtlsURL
  87. mtlsCustomizer := func(provider *vaultProvider, secret *v1.Secret, secretStore *metav1.ObjectMeta, secretStoreSpec *esv1.SecretStoreSpec, isClusterStore bool) {
  88. secret.Name = secret.Name + mtlsSuffix
  89. secretStore.Name = secretStore.Name + mtlsSuffix
  90. secretStoreSpec.Provider.Vault.Server = provider.mtlsUrl
  91. secretStoreSpec.Provider.Vault.ClientTLS = esv1.VaultClientTLS{
  92. CertSecretRef: &esmeta.SecretKeySelector{
  93. Name: clientTlsCertName,
  94. },
  95. KeySecretRef: &esmeta.SecretKeySelector{
  96. Name: clientTlsCertName,
  97. },
  98. }
  99. if isClusterStore {
  100. secretStoreSpec.Provider.Vault.ClientTLS.CertSecretRef.Namespace = &provider.framework.Namespace.Name
  101. secretStoreSpec.Provider.Vault.ClientTLS.KeySecretRef.Namespace = &provider.framework.Namespace.Name
  102. }
  103. }
  104. invalidMtlsCustomizer := func(provider *vaultProvider, secret *v1.Secret, secretStore *metav1.ObjectMeta, secretStoreSpec *esv1.SecretStoreSpec, isClusterStore bool) {
  105. secret.Name = secret.Name + invalidMtlSuffix
  106. secretStore.Name = secretStore.Name + invalidMtlSuffix
  107. secretStoreSpec.Provider.Vault.Server = provider.mtlsUrl
  108. }
  109. s.CreateClientTlsCert(v, ns)
  110. s.CreateCertStore(v, ns)
  111. s.CreateTokenStore(v, ns)
  112. s.CreateAppRoleStore(v, ns)
  113. s.CreateV1Store(v, ns)
  114. s.CreateJWTStore(v, ns)
  115. s.CreateJWTK8sStore(v, ns)
  116. s.CreateKubernetesAuthStore(v, ns)
  117. s.CreateReferentTokenStore(v, ns)
  118. s.CreateTokenStore(v, ns, mtlsCustomizer)
  119. s.CreateReferentTokenStore(v, ns, mtlsCustomizer)
  120. s.CreateTokenStore(v, ns, invalidMtlsCustomizer)
  121. }
  122. func (s *vaultProvider) AfterEach() {
  123. s.DeleteClusterSecretStore(referentSecretStoreName(s.framework))
  124. s.DeleteClusterSecretStore(referentSecretStoreName(s.framework) + mtlsSuffix)
  125. }
  126. func makeStore(name, ns string, v *addon.Vault) *esv1.SecretStore {
  127. return &esv1.SecretStore{
  128. ObjectMeta: metav1.ObjectMeta{
  129. Name: name,
  130. Namespace: ns,
  131. },
  132. Spec: esv1.SecretStoreSpec{
  133. Provider: &esv1.SecretStoreProvider{
  134. Vault: &esv1.VaultProvider{
  135. Version: esv1.VaultKVStoreV2,
  136. Path: &secretStorePath,
  137. Server: v.VaultURL,
  138. CABundle: v.VaultServerCA,
  139. },
  140. },
  141. },
  142. }
  143. }
  144. func makeClusterStore(name, ns string, v *addon.Vault) *esv1.ClusterSecretStore {
  145. store := makeStore(name, ns, v)
  146. return &esv1.ClusterSecretStore{
  147. ObjectMeta: store.ObjectMeta,
  148. Spec: store.Spec,
  149. }
  150. }
  151. func (s *vaultProvider) CreateClientTlsCert(v *addon.Vault, ns string) {
  152. By("creating a secret containing the Vault TLS client certificate")
  153. clientCert := v.ClientCert
  154. clientKey := v.ClientKey
  155. vaultClientCert := &v1.Secret{
  156. ObjectMeta: metav1.ObjectMeta{
  157. Name: clientTlsCertName,
  158. Namespace: ns,
  159. },
  160. Data: map[string][]byte{
  161. "tls.crt": clientCert,
  162. "tls.key": clientKey,
  163. },
  164. }
  165. err := s.framework.CRClient.Create(context.Background(), vaultClientCert)
  166. Expect(err).ToNot(HaveOccurred())
  167. }
  168. func (s *vaultProvider) CreateCertStore(v *addon.Vault, ns string) {
  169. By("creating a vault secret")
  170. clientCert := v.ClientCert
  171. clientKey := v.ClientKey
  172. vaultCreds := &v1.Secret{
  173. ObjectMeta: metav1.ObjectMeta{
  174. Name: certAuthProviderName,
  175. Namespace: ns,
  176. },
  177. Data: map[string][]byte{
  178. "token": []byte(v.RootToken),
  179. "client_cert": clientCert,
  180. "client_key": clientKey,
  181. },
  182. }
  183. err := s.framework.CRClient.Create(context.Background(), vaultCreds)
  184. Expect(err).ToNot(HaveOccurred())
  185. By("creating an secret store for vault")
  186. secretStore := makeStore(certAuthProviderName, ns, v)
  187. secretStore.Spec.Provider.Vault.Auth = &esv1.VaultAuth{
  188. Cert: &esv1.VaultCertAuth{
  189. ClientCert: esmeta.SecretKeySelector{
  190. Name: certAuthProviderName,
  191. Key: "client_cert",
  192. },
  193. SecretRef: esmeta.SecretKeySelector{
  194. Name: certAuthProviderName,
  195. Key: "client_key",
  196. },
  197. },
  198. }
  199. err = s.framework.CRClient.Create(context.Background(), secretStore)
  200. Expect(err).ToNot(HaveOccurred())
  201. }
  202. func (s vaultProvider) CreateTokenStore(v *addon.Vault, ns string, customizers ...StoreCustomizer) {
  203. vaultCreds := &v1.Secret{
  204. ObjectMeta: metav1.ObjectMeta{
  205. Name: "token-provider",
  206. Namespace: ns,
  207. },
  208. Data: map[string][]byte{
  209. "token": []byte(v.RootToken),
  210. },
  211. }
  212. secretStore := makeStore(s.framework.Namespace.Name, ns, v)
  213. secretStore.Spec.Provider.Vault.Auth = &esv1.VaultAuth{
  214. TokenSecretRef: &esmeta.SecretKeySelector{
  215. Name: vaultCreds.Name,
  216. Key: "token",
  217. },
  218. }
  219. for _, customizer := range customizers {
  220. customizer(&s, vaultCreds, &secretStore.ObjectMeta, &secretStore.Spec, false)
  221. }
  222. secretStore.Spec.Provider.Vault.Auth.TokenSecretRef.Name = vaultCreds.Name
  223. err := s.framework.CRClient.Create(context.Background(), vaultCreds)
  224. Expect(err).ToNot(HaveOccurred())
  225. err = s.framework.CRClient.Create(context.Background(), secretStore)
  226. Expect(err).ToNot(HaveOccurred())
  227. }
  228. // CreateReferentTokenStore creates a secret in the ExternalSecrets
  229. // namespace and creates a ClusterSecretStore with an empty namespace
  230. // that can be used to test the referent namespace feature.
  231. func (s vaultProvider) CreateReferentTokenStore(v *addon.Vault, ns string, customizers ...StoreCustomizer) {
  232. referentSecret := &v1.Secret{
  233. ObjectMeta: metav1.ObjectMeta{
  234. Name: referentSecretName,
  235. Namespace: s.framework.Namespace.Name,
  236. },
  237. Data: map[string][]byte{
  238. referentKey: []byte(v.RootToken),
  239. },
  240. }
  241. secretStore := makeClusterStore(referentSecretStoreName(s.framework), ns, v)
  242. secretStore.Spec.Provider.Vault.Auth = &esv1.VaultAuth{
  243. TokenSecretRef: &esmeta.SecretKeySelector{
  244. Name: referentSecret.Name,
  245. Key: referentKey,
  246. },
  247. }
  248. for _, customizer := range customizers {
  249. customizer(&s, referentSecret, &secretStore.ObjectMeta, &secretStore.Spec, true)
  250. }
  251. secretStore.Spec.Provider.Vault.Auth.TokenSecretRef.Name = referentSecret.Name
  252. _, err := s.framework.KubeClientSet.CoreV1().Secrets(s.framework.Namespace.Name).Create(context.Background(), referentSecret, metav1.CreateOptions{})
  253. Expect(err).ToNot(HaveOccurred())
  254. err = s.framework.CRClient.Create(context.Background(), secretStore)
  255. Expect(err).ToNot(HaveOccurred())
  256. }
  257. func (s *vaultProvider) DeleteClusterSecretStore(name string) {
  258. err := s.framework.CRClient.Delete(context.Background(), &esv1.ClusterSecretStore{
  259. ObjectMeta: metav1.ObjectMeta{
  260. Name: name,
  261. },
  262. })
  263. Expect(err).ToNot(HaveOccurred())
  264. }
  265. func (s vaultProvider) CreateAppRoleStore(v *addon.Vault, ns string) {
  266. By("creating a vault secret")
  267. vaultCreds := &v1.Secret{
  268. ObjectMeta: metav1.ObjectMeta{
  269. Name: appRoleAuthProviderName,
  270. Namespace: ns,
  271. },
  272. Data: map[string][]byte{
  273. "approle_secret": []byte(v.AppRoleSecret),
  274. },
  275. }
  276. err := s.framework.CRClient.Create(context.Background(), vaultCreds)
  277. Expect(err).ToNot(HaveOccurred())
  278. By("creating an secret store for vault")
  279. secretStore := makeStore(appRoleAuthProviderName, ns, v)
  280. secretStore.Spec.Provider.Vault.Auth = &esv1.VaultAuth{
  281. AppRole: &esv1.VaultAppRole{
  282. Path: v.AppRolePath,
  283. RoleID: v.AppRoleID,
  284. SecretRef: esmeta.SecretKeySelector{
  285. Name: appRoleAuthProviderName,
  286. Key: "approle_secret",
  287. },
  288. },
  289. }
  290. err = s.framework.CRClient.Create(context.Background(), secretStore)
  291. Expect(err).ToNot(HaveOccurred())
  292. }
  293. func (s vaultProvider) CreateV1Store(v *addon.Vault, ns string) {
  294. vaultCreds := &v1.Secret{
  295. ObjectMeta: metav1.ObjectMeta{
  296. Name: "v1-provider",
  297. Namespace: ns,
  298. },
  299. Data: map[string][]byte{
  300. "token": []byte(v.RootToken),
  301. },
  302. }
  303. err := s.framework.CRClient.Create(context.Background(), vaultCreds)
  304. Expect(err).ToNot(HaveOccurred())
  305. secretStore := makeStore(kvv1ProviderName, ns, v)
  306. secretV1StorePath := "secret_v1"
  307. secretStore.Spec.Provider.Vault.Version = esv1.VaultKVStoreV1
  308. secretStore.Spec.Provider.Vault.Path = &secretV1StorePath
  309. secretStore.Spec.Provider.Vault.Auth = &esv1.VaultAuth{
  310. TokenSecretRef: &esmeta.SecretKeySelector{
  311. Name: "v1-provider",
  312. Key: "token",
  313. },
  314. }
  315. err = s.framework.CRClient.Create(context.Background(), secretStore)
  316. Expect(err).ToNot(HaveOccurred())
  317. }
  318. func (s vaultProvider) CreateJWTStore(v *addon.Vault, ns string) {
  319. vaultCreds := &v1.Secret{
  320. ObjectMeta: metav1.ObjectMeta{
  321. Name: jwtProviderSecretName,
  322. Namespace: ns,
  323. },
  324. Data: map[string][]byte{
  325. "jwt": []byte(v.JWTToken),
  326. },
  327. }
  328. err := s.framework.CRClient.Create(context.Background(), vaultCreds)
  329. Expect(err).ToNot(HaveOccurred())
  330. secretStore := makeStore(jwtProviderName, ns, v)
  331. secretStore.Spec.Provider.Vault.Auth = &esv1.VaultAuth{
  332. Jwt: &esv1.VaultJwtAuth{
  333. Path: v.JWTPath,
  334. Role: v.JWTRole,
  335. SecretRef: &esmeta.SecretKeySelector{
  336. Name: jwtProviderSecretName,
  337. Key: "jwt",
  338. },
  339. },
  340. }
  341. err = s.framework.CRClient.Create(context.Background(), secretStore)
  342. Expect(err).ToNot(HaveOccurred())
  343. }
  344. func (s vaultProvider) CreateJWTK8sStore(v *addon.Vault, ns string) {
  345. secretStore := makeStore(jwtK8sProviderName, ns, v)
  346. secretStore.Spec.Provider.Vault.Auth = &esv1.VaultAuth{
  347. Jwt: &esv1.VaultJwtAuth{
  348. Path: v.JWTK8sPath,
  349. Role: v.JWTRole,
  350. KubernetesServiceAccountToken: &esv1.VaultKubernetesServiceAccountTokenAuth{
  351. ServiceAccountRef: esmeta.ServiceAccountSelector{
  352. Name: "default",
  353. },
  354. Audiences: &[]string{
  355. "vault.client",
  356. },
  357. },
  358. },
  359. }
  360. err := s.framework.CRClient.Create(context.Background(), secretStore)
  361. Expect(err).ToNot(HaveOccurred())
  362. }
  363. func (s vaultProvider) CreateKubernetesAuthStore(v *addon.Vault, ns string) {
  364. secretStore := makeStore(kubernetesProviderName, ns, v)
  365. secretStore.Spec.Provider.Vault.Auth = &esv1.VaultAuth{
  366. Kubernetes: &esv1.VaultKubernetesAuth{
  367. Path: v.KubernetesAuthPath,
  368. Role: v.KubernetesAuthRole,
  369. ServiceAccountRef: &esmeta.ServiceAccountSelector{
  370. Name: "default",
  371. },
  372. },
  373. }
  374. err := s.framework.CRClient.Create(context.Background(), secretStore)
  375. Expect(err).ToNot(HaveOccurred())
  376. }
  377. func referentSecretStoreName(f *framework.Framework) string {
  378. return "referent-provider-" + f.Namespace.Name
  379. }