provider_test.go 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019
  1. /*
  2. Copyright © The ESO Authors
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. https://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package vault
  14. import (
  15. "context"
  16. "errors"
  17. "fmt"
  18. "testing"
  19. "time"
  20. "github.com/google/go-cmp/cmp"
  21. vault "github.com/hashicorp/vault/api"
  22. "github.com/spf13/pflag"
  23. corev1 "k8s.io/api/core/v1"
  24. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  25. typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
  26. kclient "sigs.k8s.io/controller-runtime/pkg/client"
  27. clientfake "sigs.k8s.io/controller-runtime/pkg/client/fake"
  28. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  29. esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
  30. "github.com/external-secrets/external-secrets/providers/v1/vault/fake"
  31. vaultutil "github.com/external-secrets/external-secrets/providers/v1/vault/util"
  32. utilfake "github.com/external-secrets/external-secrets/runtime/util/fake"
  33. )
  34. const (
  35. tokenSecretName = "example-secret-token"
  36. secretDataString = "some-creds"
  37. tlsAuthCerts = "tls-auth-certs"
  38. tlsKey = "tls.key"
  39. tlsCrt = "tls.crt"
  40. vaultCert = "vault-cert"
  41. )
  42. var (
  43. secretStorePath = "secret"
  44. )
  45. func makeValidSecretStoreWithVersion(v esv1.VaultKVStoreVersion) *esv1.SecretStore {
  46. return &esv1.SecretStore{
  47. ObjectMeta: metav1.ObjectMeta{
  48. Name: "vault-store",
  49. Namespace: "default",
  50. },
  51. Spec: esv1.SecretStoreSpec{
  52. Provider: &esv1.SecretStoreProvider{
  53. Vault: &esv1.VaultProvider{
  54. Server: "vault.example.com",
  55. Path: &secretStorePath,
  56. Version: v,
  57. Auth: &esv1.VaultAuth{
  58. Kubernetes: &esv1.VaultKubernetesAuth{
  59. Path: "kubernetes",
  60. Role: "kubernetes-auth-role",
  61. ServiceAccountRef: &esmeta.ServiceAccountSelector{
  62. Name: "example-sa",
  63. },
  64. },
  65. },
  66. },
  67. },
  68. },
  69. }
  70. }
  71. func makeValidSecretStore() *esv1.SecretStore {
  72. return makeValidSecretStoreWithVersion(esv1.VaultKVStoreV2)
  73. }
  74. func makeValidSecretStoreWithCerts() *esv1.SecretStore {
  75. return &esv1.SecretStore{
  76. ObjectMeta: metav1.ObjectMeta{
  77. Name: "vault-store",
  78. Namespace: "default",
  79. },
  80. Spec: esv1.SecretStoreSpec{
  81. Provider: &esv1.SecretStoreProvider{
  82. Vault: &esv1.VaultProvider{
  83. Server: "vault.example.com",
  84. Path: &secretStorePath,
  85. Version: esv1.VaultKVStoreV2,
  86. Auth: &esv1.VaultAuth{
  87. Cert: &esv1.VaultCertAuth{
  88. ClientCert: esmeta.SecretKeySelector{
  89. Name: tlsAuthCerts,
  90. Key: tlsCrt,
  91. },
  92. SecretRef: esmeta.SecretKeySelector{
  93. Name: tlsAuthCerts,
  94. Key: tlsKey,
  95. },
  96. },
  97. },
  98. },
  99. },
  100. },
  101. }
  102. }
  103. func makeValidSecretStoreWithK8sCerts(isSecret bool) *esv1.SecretStore {
  104. store := makeSecretStore()
  105. caProvider := &esv1.CAProvider{
  106. Name: vaultCert,
  107. Key: "cert",
  108. }
  109. if isSecret {
  110. caProvider.Type = "Secret"
  111. } else {
  112. caProvider.Type = "ConfigMap"
  113. }
  114. store.Spec.Provider.Vault.CAProvider = caProvider
  115. return store
  116. }
  117. func makeInvalidClusterSecretStoreWithK8sCerts() *esv1.ClusterSecretStore {
  118. return &esv1.ClusterSecretStore{
  119. TypeMeta: metav1.TypeMeta{
  120. Kind: "ClusterSecretStore",
  121. },
  122. ObjectMeta: metav1.ObjectMeta{
  123. Name: "vault-store",
  124. Namespace: "default",
  125. },
  126. Spec: esv1.SecretStoreSpec{
  127. Provider: &esv1.SecretStoreProvider{
  128. Vault: &esv1.VaultProvider{
  129. Server: "vault.example.com",
  130. Path: &secretStorePath,
  131. Version: "v2",
  132. Auth: &esv1.VaultAuth{
  133. Kubernetes: &esv1.VaultKubernetesAuth{
  134. Path: "kubernetes",
  135. Role: "kubernetes-auth-role",
  136. ServiceAccountRef: &esmeta.ServiceAccountSelector{
  137. Name: "example-sa",
  138. },
  139. },
  140. },
  141. CAProvider: &esv1.CAProvider{
  142. Name: vaultCert,
  143. Key: "cert",
  144. Type: "Secret",
  145. },
  146. },
  147. },
  148. },
  149. }
  150. }
  151. func makeValidSecretStoreWithIamAuthSecret() *esv1.SecretStore {
  152. return &esv1.SecretStore{
  153. ObjectMeta: metav1.ObjectMeta{
  154. Name: "vault-store",
  155. Namespace: "default",
  156. },
  157. Spec: esv1.SecretStoreSpec{
  158. Provider: &esv1.SecretStoreProvider{
  159. Vault: &esv1.VaultProvider{
  160. Server: "https://vault.example.com:8200",
  161. Path: &secretStorePath,
  162. Version: esv1.VaultKVStoreV2,
  163. Auth: &esv1.VaultAuth{
  164. Iam: &esv1.VaultIamAuth{
  165. Path: "aws",
  166. Region: "us-east-1",
  167. Role: "vault-role",
  168. SecretRef: &esv1.VaultAwsAuthSecretRef{
  169. AccessKeyID: esmeta.SecretKeySelector{
  170. Name: "vault-iam-creds-secret",
  171. Key: "access-key",
  172. },
  173. SecretAccessKey: esmeta.SecretKeySelector{
  174. Name: "vault-iam-creds-secret",
  175. Key: "secret-access-key",
  176. },
  177. SessionToken: &esmeta.SecretKeySelector{
  178. Name: "vault-iam-creds-secret",
  179. Key: "secret-session-token",
  180. },
  181. },
  182. },
  183. },
  184. },
  185. },
  186. },
  187. }
  188. }
  189. func makeValidSecretStoreWithIamAuthControllerPod() *esv1.SecretStore {
  190. return &esv1.SecretStore{
  191. ObjectMeta: metav1.ObjectMeta{
  192. Name: "vault-store",
  193. Namespace: "default",
  194. },
  195. Spec: esv1.SecretStoreSpec{
  196. Provider: &esv1.SecretStoreProvider{
  197. Vault: &esv1.VaultProvider{
  198. Server: "https://vault.example.com:8200",
  199. Path: &secretStorePath,
  200. Version: esv1.VaultKVStoreV2,
  201. Auth: &esv1.VaultAuth{
  202. Iam: &esv1.VaultIamAuth{
  203. Path: "aws",
  204. Region: "us-east-1",
  205. Role: "vault-role",
  206. // No JWTAuth or SecretRef - will use controller pod identity
  207. },
  208. },
  209. },
  210. },
  211. },
  212. }
  213. }
  214. type secretStoreTweakFn func(s *esv1.SecretStore)
  215. func makeSecretStore(tweaks ...secretStoreTweakFn) *esv1.SecretStore {
  216. store := makeValidSecretStore()
  217. for _, fn := range tweaks {
  218. fn(store)
  219. }
  220. return store
  221. }
  222. func makeClusterSecretStore(tweaks ...secretStoreTweakFn) *esv1.ClusterSecretStore {
  223. store := makeValidSecretStore()
  224. for _, fn := range tweaks {
  225. fn(store)
  226. }
  227. return &esv1.ClusterSecretStore{
  228. TypeMeta: metav1.TypeMeta{
  229. Kind: esv1.ClusterSecretStoreKind,
  230. },
  231. ObjectMeta: store.ObjectMeta,
  232. Spec: store.Spec,
  233. }
  234. }
  235. type args struct {
  236. newClientFunc func(c *vault.Config) (vaultutil.Client, error)
  237. store esv1.GenericStore
  238. kube kclient.Client
  239. corev1 typedcorev1.CoreV1Interface
  240. ns string
  241. }
  242. type want struct {
  243. err error
  244. }
  245. type testCase struct {
  246. reason string
  247. args args
  248. want want
  249. }
  250. func TestNewVault(t *testing.T) {
  251. errBoom := errors.New("boom")
  252. secretClientKey := []byte(`-----BEGIN PRIVATE KEY-----
  253. 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
  254. -----END PRIVATE KEY-----`)
  255. clientCrt := []byte(`-----BEGIN CERTIFICATE-----
  256. MIIFkTCCA3mgAwIBAgIUBEUg3m/WqAsWHG4Q/II3IePFfuowDQYJKoZIhvcNAQELBQAwWDELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDERMA8GA1UEAwwIdmF1bHQtY2EwHhcNMjIwNzI5MjEyMjE4WhcNMzkwMTAxMjEyMjE4WjBYMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMREwDwYDVQQDDAh2YXVsdC1jYTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKLhwbYLEd6M5dpZbReuLg9tbK5qKe6sJm5Qs8sLSMmwkXTOSrvvw7smV5bOHtCGpUEooiFbos1RVL22YYd8cLu59oev1i1p3NWSIPNAq0DQKBxyj3wJ9ftvOfmlmf/J+dtE39pPLFPnffkMWEdzT8UImfZD/kf1S1fotHcffNtP0ijfZPQHo4BQooH/e7PEAQIXDBIrxSoZR57wkoYGtTXRdEAwbE5zUE5ZikIdZlEfYVEYiBL5vBgc5dSM+/oUfurH7AIzOXGp30sa+JzYKkUTQGISw4siJj+oSqjO93Zo4tWEE+NP/0tNe9FnBf+oDuHhgiyerk3yU3jK2vJvXc3191FWDKZXKQaRoCaHnMDgMiDjxQtbM590/BFDWRWrZNGaPciVwmw2foyAclNtGq8J0kzIDn0VRkL35Ad8LfTuxXZpY+hVPC5TIPek4W7grQtX44ohVDw5M6oClucv104tXHjAVVgTfaVMLi1F3FRTwwqI8ShPX0D5ssw+tkeepcIhHMvDB97AgKBM1g24MdX3c6aokrghdwXxQbTS/6lHD66apc3E2STTHeCt9fnDSwEZlvK6iNgedBDHrBl9SXeYTMnbOWYIHX/YYq1efq0gOZHHN4nZuBopOMb+4K/04a2nVUuafdv/eoL6F7h+/+UaH/N3+LkkxWAxcz0w9GVZAgMBAAGjUzBRMB0GA1UdDgQWBBQuIVwmjMZvkq+jf6ViTelH5KDBVDAfBgNVHSMEGDAWgBQuIVwmjMZvkq+jf6ViTelH5KDBVDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQAk4kNyFzmiKnREmi5PPj7xGAtv2aJIdMEfcZJ9e+H0Nb2aCvMvZsDodduXu6G5+1opd45v0AeTjLBkXDO6/8vnyM32VZEEKCAwMCLcOLD1z0+r+gaurDYMOGU5qr8hQadHKFsxEDYnR/9KdHhBg6A8qE2cOQa1ryu34DnWQ3m0CBApClf1YBRp/4T8BmHumfH6odD96H30HVzINrd9WM2hR9GRE3xqQyfwlvqmGn9S6snSVa+mcJ6w2wNE2LPGx0kOtBeOIUdfSsEgvSRjbowSHz9lohFZ0LxJYyizCA5vnMmYyhhkfJqm7YtjHkGWgXmqpH9BFt0D3gfORlIh787nuWfxtZ+554rDyQmPjYQG/qF4+Awehr4RxiGWTox1C67G/RzA6TOXX09xuFY+3U1ich90/KffvhoHvRVfhzxx+HUUY2qSU3HqQDzgieQQBaMuOhd1i6pua+/kPSXkuXqnIs8daao/goR5iU/lPLs7M8Dy7xZ9adzbIPuNuzHir2UuvtPlW+x/sSvOnVL9r/7TrAuWhdScglQ70EInPDVX7BgDWKrZUh86N4d7fu2f/T+6VoUSGEjq8obCj3BQ61mNEoftKVECUO4MMUdat6pY/4Xh6Dwc+FnbvR2+sX7IzI7FtgOrfO6abT+LCAR0R+UXyvnqZcjK2zkHz4DfXFbCQg==
  257. -----END CERTIFICATE-----`)
  258. secretData := []byte(secretDataString)
  259. cases := map[string]testCase{
  260. "InvalidVaultStore": {
  261. reason: "Should return error if given an invalid vault store.",
  262. args: args{
  263. store: &esv1.SecretStore{},
  264. },
  265. want: want{
  266. err: errors.New(errVaultStore),
  267. },
  268. },
  269. "InvalidRetrySettings": {
  270. reason: "Should return error if given an invalid Retry Interval.",
  271. args: args{
  272. store: makeSecretStore(func(s *esv1.SecretStore) {
  273. s.Spec.RetrySettings = &esv1.SecretStoreRetrySettings{
  274. MaxRetries: new(int32(3)),
  275. RetryInterval: new("not-an-interval"),
  276. }
  277. }),
  278. },
  279. want: want{
  280. err: func() error { _, err := time.ParseDuration("not-an-interval"); return err }(),
  281. },
  282. },
  283. "ValidRetrySettings": {
  284. reason: "Should return a Vault provider with custom retry settings",
  285. args: args{
  286. store: makeSecretStore(func(s *esv1.SecretStore) {
  287. s.Spec.RetrySettings = &esv1.SecretStoreRetrySettings{
  288. MaxRetries: new(int32(3)),
  289. RetryInterval: new("10m"),
  290. }
  291. }),
  292. ns: "default",
  293. kube: clientfake.NewClientBuilder().Build(),
  294. corev1: utilfake.NewCreateTokenMock().WithToken("ok"),
  295. newClientFunc: fake.ClientWithLoginMock,
  296. },
  297. want: want{
  298. err: nil,
  299. },
  300. },
  301. "AddVaultStoreCertsError": {
  302. reason: "Should return error if given an invalid CA certificate.",
  303. args: args{
  304. store: makeSecretStore(func(s *esv1.SecretStore) {
  305. s.Spec.Provider.Vault.CABundle = []byte("badcertdata")
  306. }),
  307. },
  308. want: want{
  309. err: fmt.Errorf("failed to decode ca bundle: %w", errors.New("failed to parse the new certificate, not valid pem data")),
  310. },
  311. },
  312. "VaultAuthFormatError": {
  313. reason: "Should return error if no valid authentication method is given.",
  314. args: args{
  315. store: makeSecretStore(func(s *esv1.SecretStore) {
  316. s.Spec.Provider.Vault.Auth = &esv1.VaultAuth{}
  317. }),
  318. },
  319. want: want{
  320. err: errors.New(errAuthFormat),
  321. },
  322. },
  323. "GetKubeServiceAccountError": {
  324. reason: "Should return error if fetching kubernetes secret fails.",
  325. args: args{
  326. newClientFunc: fake.ClientWithLoginMock,
  327. ns: "default",
  328. kube: clientfake.NewClientBuilder().Build(),
  329. store: makeSecretStore(),
  330. corev1: utilfake.NewCreateTokenMock().WithError(errBoom),
  331. },
  332. want: want{
  333. err: fmt.Errorf(errGetKubeSATokenRequest, "example-sa", fmt.Errorf(errGetKubeSA, "example-sa", fmt.Errorf(errServiceAccountNotFound, "example-sa"))),
  334. },
  335. },
  336. "GetKubeSecretError": {
  337. reason: "Should return error if fetching kubernetes secret fails.",
  338. args: args{
  339. ns: "default",
  340. store: makeSecretStore(func(s *esv1.SecretStore) {
  341. s.Spec.Provider.Vault.Auth.Kubernetes.ServiceAccountRef = nil
  342. s.Spec.Provider.Vault.Auth.Kubernetes.SecretRef = &esmeta.SecretKeySelector{
  343. Name: "vault-secret",
  344. Key: "key",
  345. }
  346. }),
  347. kube: clientfake.NewClientBuilder().Build(),
  348. },
  349. want: want{
  350. err: fmt.Errorf(`cannot get Kubernetes secret "vault-secret" from namespace "default": %w`, errors.New(`secrets "vault-secret" not found`)),
  351. },
  352. },
  353. "SuccessfulVaultStoreWithCertAuth": {
  354. reason: "Should return a Vault provider successfully",
  355. args: args{
  356. store: makeValidSecretStoreWithCerts(),
  357. ns: "default",
  358. kube: clientfake.NewClientBuilder().WithObjects(&corev1.Secret{
  359. ObjectMeta: metav1.ObjectMeta{
  360. Name: tlsAuthCerts,
  361. Namespace: "default",
  362. },
  363. Data: map[string][]byte{
  364. tlsKey: secretClientKey,
  365. tlsCrt: clientCrt,
  366. },
  367. }).Build(),
  368. newClientFunc: fake.ClientWithLoginMock,
  369. },
  370. want: want{
  371. err: nil,
  372. },
  373. },
  374. "SuccessfulVaultStoreWithK8sCertSecret": {
  375. reason: "Should return a Vault provider with the cert from k8s",
  376. args: args{
  377. store: makeValidSecretStoreWithK8sCerts(true),
  378. ns: "default",
  379. kube: clientfake.NewClientBuilder().WithObjects(&corev1.Secret{
  380. ObjectMeta: metav1.ObjectMeta{
  381. Name: vaultCert,
  382. Namespace: "default",
  383. },
  384. Data: map[string][]byte{
  385. "cert": clientCrt,
  386. "token": secretData,
  387. },
  388. }).Build(),
  389. corev1: utilfake.NewCreateTokenMock().WithToken("ok"),
  390. newClientFunc: fake.ClientWithLoginMock,
  391. },
  392. want: want{
  393. err: nil,
  394. },
  395. },
  396. "GetCertNamespaceMissingError": {
  397. reason: "Should return an error if namespace is missing and is a ClusterSecretStore",
  398. args: args{
  399. store: makeInvalidClusterSecretStoreWithK8sCerts(),
  400. ns: "default",
  401. kube: clientfake.NewClientBuilder().Build(),
  402. },
  403. want: want{
  404. err: errors.New(errCANamespace),
  405. },
  406. },
  407. "GetCertSecretKeyMissingError": {
  408. reason: "Should return an error if the secret key is missing",
  409. args: args{
  410. store: makeValidSecretStoreWithK8sCerts(true),
  411. ns: "default",
  412. kube: clientfake.NewClientBuilder().WithObjects(&corev1.Secret{
  413. ObjectMeta: metav1.ObjectMeta{
  414. Name: vaultCert,
  415. Namespace: "default",
  416. },
  417. Data: map[string][]byte{},
  418. }).Build(),
  419. newClientFunc: fake.ClientWithLoginMock,
  420. },
  421. want: want{
  422. err: fmt.Errorf("failed to get cert from secret: %w", fmt.Errorf("failed to resolve secret key ref: %w", errors.New("cannot find secret data for key: \"cert\""))),
  423. },
  424. },
  425. "SuccessfulVaultStoreWithIamAuthSecret": {
  426. reason: "Should return a Vault provider successfully",
  427. args: args{
  428. store: makeValidSecretStoreWithIamAuthSecret(),
  429. ns: "default",
  430. kube: clientfake.NewClientBuilder().WithObjects(&corev1.Secret{
  431. ObjectMeta: metav1.ObjectMeta{
  432. Name: "vault-iam-creds-secret",
  433. Namespace: "default",
  434. },
  435. Data: map[string][]byte{
  436. "access-key": []byte("TESTING"),
  437. "secret-access-key": []byte("ABCDEF"),
  438. "secret-session-token": []byte("c2VjcmV0LXNlc3Npb24tdG9rZW4K"),
  439. },
  440. }).Build(),
  441. corev1: utilfake.NewCreateTokenMock().WithToken("ok"),
  442. newClientFunc: fake.ClientWithLoginMock,
  443. },
  444. want: want{
  445. err: nil,
  446. },
  447. },
  448. "SuccessfulVaultStoreWithK8sCertConfigMap": {
  449. reason: "Should return a Vault prodvider with the cert from k8s",
  450. args: args{
  451. store: makeValidSecretStoreWithK8sCerts(false),
  452. ns: "default",
  453. kube: clientfake.NewClientBuilder().WithObjects(&corev1.ConfigMap{
  454. ObjectMeta: metav1.ObjectMeta{
  455. Name: vaultCert,
  456. Namespace: "default",
  457. },
  458. Data: map[string]string{
  459. "cert": string(clientCrt),
  460. },
  461. }).Build(),
  462. corev1: utilfake.NewCreateTokenMock().WithToken("ok"),
  463. newClientFunc: fake.ClientWithLoginMock,
  464. },
  465. want: want{
  466. err: nil,
  467. },
  468. },
  469. "GetCertConfigMapMissingError": {
  470. reason: "Should return an error if the config map key is missing",
  471. args: args{
  472. store: makeValidSecretStoreWithK8sCerts(false),
  473. ns: "default",
  474. kube: clientfake.NewClientBuilder().WithObjects(&corev1.ServiceAccount{
  475. ObjectMeta: metav1.ObjectMeta{
  476. Name: "example-sa",
  477. Namespace: "default",
  478. },
  479. Secrets: []corev1.ObjectReference{
  480. {
  481. Name: tokenSecretName,
  482. },
  483. },
  484. }, &corev1.ConfigMap{
  485. ObjectMeta: metav1.ObjectMeta{
  486. Name: vaultCert,
  487. Namespace: "default",
  488. },
  489. Data: map[string]string{},
  490. }).Build(),
  491. newClientFunc: fake.ClientWithLoginMock,
  492. },
  493. want: want{
  494. err: fmt.Errorf("failed to get cert from configmap: %w", errors.New("failed to get caProvider configMap vault-cert -> cert")),
  495. },
  496. },
  497. "GetCertificateFormatError": {
  498. reason: "Should return error if client certificate is in wrong format.",
  499. args: args{
  500. store: makeValidSecretStoreWithCerts(),
  501. ns: "default",
  502. kube: clientfake.NewClientBuilder().WithObjects(&corev1.Secret{
  503. ObjectMeta: metav1.ObjectMeta{
  504. Name: tlsAuthCerts,
  505. Namespace: "default",
  506. },
  507. Data: map[string][]byte{
  508. tlsKey: secretClientKey,
  509. tlsCrt: []byte("cert with mistake"),
  510. },
  511. }).Build(),
  512. newClientFunc: fake.ClientWithLoginMock,
  513. },
  514. want: want{
  515. err: fmt.Errorf(errClientTLSAuth, "tls: failed to find any PEM data in certificate input"),
  516. },
  517. },
  518. "GetKeyFormatError": {
  519. reason: "Should return error if client key is in wrong format.",
  520. args: args{
  521. store: makeValidSecretStoreWithCerts(),
  522. ns: "default",
  523. kube: clientfake.NewClientBuilder().WithObjects(&corev1.Secret{
  524. ObjectMeta: metav1.ObjectMeta{
  525. Name: tlsAuthCerts,
  526. Namespace: "default",
  527. },
  528. Data: map[string][]byte{
  529. tlsKey: []byte("key with mistake"),
  530. tlsCrt: clientCrt,
  531. },
  532. }).Build(),
  533. newClientFunc: fake.ClientWithLoginMock,
  534. },
  535. want: want{
  536. err: fmt.Errorf(errClientTLSAuth, "tls: failed to find any PEM data in key input"),
  537. },
  538. },
  539. "ClientTlsInvalidCertificatesError": {
  540. reason: "Should return error if client key is in wrong format.",
  541. args: args{
  542. store: makeSecretStore(func(s *esv1.SecretStore) {
  543. s.Spec.Provider.Vault.ClientTLS = esv1.VaultClientTLS{
  544. CertSecretRef: &esmeta.SecretKeySelector{
  545. Name: tlsAuthCerts,
  546. },
  547. KeySecretRef: &esmeta.SecretKeySelector{
  548. Name: tlsAuthCerts,
  549. },
  550. }
  551. }),
  552. ns: "default",
  553. kube: clientfake.NewClientBuilder().WithObjects(&corev1.Secret{
  554. ObjectMeta: metav1.ObjectMeta{
  555. Name: tlsAuthCerts,
  556. Namespace: "default",
  557. },
  558. Data: map[string][]byte{
  559. tlsKey: []byte("key with mistake"),
  560. tlsCrt: clientCrt,
  561. },
  562. }).Build(),
  563. corev1: utilfake.NewCreateTokenMock().WithToken("ok"),
  564. newClientFunc: fake.ClientWithLoginMock,
  565. },
  566. want: want{
  567. err: fmt.Errorf(errClientTLSAuth, "tls: failed to find any PEM data in key input"),
  568. },
  569. },
  570. "SuccessfulVaultStoreValidClientTls": {
  571. reason: "Should return a Vault provider with the cert from k8s",
  572. args: args{
  573. store: makeSecretStore(func(s *esv1.SecretStore) {
  574. s.Spec.Provider.Vault.ClientTLS = esv1.VaultClientTLS{
  575. CertSecretRef: &esmeta.SecretKeySelector{
  576. Name: tlsAuthCerts,
  577. },
  578. KeySecretRef: &esmeta.SecretKeySelector{
  579. Name: tlsAuthCerts,
  580. },
  581. }
  582. }),
  583. ns: "default",
  584. kube: clientfake.NewClientBuilder().WithObjects(&corev1.Secret{
  585. ObjectMeta: metav1.ObjectMeta{
  586. Name: tlsAuthCerts,
  587. Namespace: "default",
  588. },
  589. Data: map[string][]byte{
  590. tlsKey: secretClientKey,
  591. tlsCrt: clientCrt,
  592. },
  593. }).Build(),
  594. corev1: utilfake.NewCreateTokenMock().WithToken("ok"),
  595. newClientFunc: fake.ClientWithLoginMock,
  596. },
  597. want: want{
  598. err: nil,
  599. },
  600. },
  601. "SuccessfulVaultStoreWithSecretRef": {
  602. reason: "Should return a Vault provider with secret ref auth",
  603. args: args{
  604. store: makeClusterSecretStore(func(s *esv1.SecretStore) {
  605. s.Spec.Provider.Vault.Auth.Kubernetes = nil
  606. s.Spec.Provider.Vault.Auth.TokenSecretRef = &esmeta.SecretKeySelector{
  607. Name: "vault-token",
  608. Namespace: new("default"),
  609. Key: "token",
  610. }
  611. }),
  612. kube: clientfake.NewClientBuilder().WithObjects(&corev1.Secret{
  613. ObjectMeta: metav1.ObjectMeta{
  614. Name: "vault-token",
  615. Namespace: "default",
  616. },
  617. Data: map[string][]byte{
  618. "token": []byte("token"),
  619. },
  620. }).Build(),
  621. // no need to mock the secret as it is not used
  622. newClientFunc: fake.ClientWithLoginMock,
  623. },
  624. want: want{},
  625. },
  626. "SuccessfulVaultStoreWithApproleRef": {
  627. reason: "Should return a Vault provider with approle auth",
  628. args: args{
  629. store: makeSecretStore(func(s *esv1.SecretStore) {
  630. s.Spec.Provider.Vault.Auth.Kubernetes = nil
  631. s.Spec.Provider.Vault.Auth.AppRole = &esv1.VaultAppRole{
  632. SecretRef: esmeta.SecretKeySelector{
  633. Name: "vault-secret-id",
  634. Key: "secret-id",
  635. },
  636. RoleRef: &esmeta.SecretKeySelector{
  637. Name: "vault-secret-id",
  638. Key: "approle",
  639. },
  640. }
  641. }),
  642. ns: "default",
  643. kube: clientfake.NewClientBuilder().WithObjects(&corev1.Secret{
  644. ObjectMeta: metav1.ObjectMeta{
  645. Name: "vault-secret-id",
  646. Namespace: "default",
  647. },
  648. Data: map[string][]byte{
  649. "secret-id": []byte("myid"),
  650. "approle": []byte("myrole"),
  651. },
  652. }).Build(),
  653. // no need to mock the secret as it is not used
  654. newClientFunc: fake.ClientWithLoginMock,
  655. },
  656. want: want{},
  657. },
  658. "SuccessfulVaultStoreWithSecretRefAndReferentSpec": {
  659. reason: "Should return a Vault provider with secret ref auth",
  660. args: args{
  661. store: makeClusterSecretStore(func(s *esv1.SecretStore) {
  662. s.Spec.Provider.Vault.Auth.TokenSecretRef = &esmeta.SecretKeySelector{
  663. Name: "vault-token",
  664. Key: "token",
  665. }
  666. }),
  667. // no need to mock the secret as it is not used
  668. newClientFunc: fake.ClientWithLoginMock,
  669. },
  670. want: want{},
  671. },
  672. "SuccessfulVaultStoreWithJwtAuthAndReferentSpec": {
  673. reason: "Should return a Vault provider with jwt auth",
  674. args: args{
  675. store: makeClusterSecretStore(func(s *esv1.SecretStore) {
  676. s.Spec.Provider.Vault.Auth.Kubernetes = nil
  677. s.Spec.Provider.Vault.Auth.Jwt = &esv1.VaultJwtAuth{
  678. Role: "test-role",
  679. SecretRef: &esmeta.SecretKeySelector{
  680. Name: "vault-token",
  681. },
  682. }
  683. }),
  684. // no need to mock the secret as it is not used
  685. newClientFunc: fake.ClientWithLoginMock,
  686. },
  687. want: want{},
  688. },
  689. "IamAuthControllerPodNoEnvVars": {
  690. reason: "Should return error when IAM controller pod auth has no AWS environment variables",
  691. args: args{
  692. store: makeValidSecretStoreWithIamAuthControllerPod(),
  693. ns: "default",
  694. kube: clientfake.NewClientBuilder().Build(),
  695. corev1: utilfake.NewCreateTokenMock().WithToken("ok"),
  696. newClientFunc: fake.ClientWithLoginMock,
  697. },
  698. want: want{
  699. err: errors.New(errNoAWSAuthMethodFound),
  700. },
  701. },
  702. }
  703. for name, tc := range cases {
  704. t.Run(name, func(t *testing.T) {
  705. vaultTest(t, name, tc)
  706. })
  707. }
  708. }
  709. func vaultTest(t *testing.T, _ string, tc testCase) {
  710. prov := &Provider{
  711. NewVaultClient: tc.args.newClientFunc,
  712. }
  713. if tc.args.newClientFunc == nil {
  714. prov.NewVaultClient = NewVaultClient
  715. }
  716. _, err := prov.newClient(context.Background(), tc.args.store, tc.args.kube, tc.args.corev1, tc.args.ns)
  717. if tc.want.err == nil {
  718. if err != nil {
  719. t.Errorf("newClient() unexpected error = %v", err)
  720. }
  721. return
  722. }
  723. if err == nil {
  724. t.Errorf("newClient() error = nil, wantErr %v", tc.want.err)
  725. return
  726. }
  727. if tc.want.err.Error() != err.Error() {
  728. t.Errorf("newClient() error = %v, wantErr %v", err, tc.want.err)
  729. }
  730. }
  731. func TestGetControllerPodCredentials(t *testing.T) {
  732. client := &client{storeKind: esv1.SecretStoreKind}
  733. ctx := context.Background()
  734. region := "us-east-1"
  735. kube := clientfake.NewClientBuilder().Build()
  736. t.Run("PodIdentityEnvVars", func(t *testing.T) {
  737. t.Setenv("AWS_CONTAINER_CREDENTIALS_FULL_URI", "http://169.254.170.23/v1/credentials")
  738. creds, err := client.getControllerPodCredentials(ctx, region, kube, nil)
  739. // Should succeed and return nil (indicating AWS SDK should handle it)
  740. if err != nil {
  741. t.Errorf("Expected no error, got: %v", err)
  742. }
  743. if creds != nil {
  744. t.Errorf("Expected nil credentials for Pod Identity, got: %v", creds)
  745. }
  746. })
  747. t.Run("NoEnvVars", func(t *testing.T) {
  748. // Pod Identity URI is not set.
  749. _, err := client.getControllerPodCredentials(ctx, region, kube, nil)
  750. expectedErr := fmt.Errorf(errNoAWSAuthMethodFound)
  751. if diff := cmp.Diff(expectedErr, err, EquateErrors()); diff != "" {
  752. t.Errorf("TestGetControllerPodCredentials/NoEnvVars: -want error, +got error:\n%s", diff)
  753. }
  754. })
  755. }
  756. func TestCache(t *testing.T) {
  757. t.Cleanup(resetCache)
  758. enableCache = true
  759. initCache(defaultCacheSize)
  760. prov := &Provider{
  761. NewVaultClient: fake.ClientWithLoginMock,
  762. }
  763. namespace := "default"
  764. store := makeClusterSecretStore(func(s *esv1.SecretStore) {
  765. s.Spec.Provider.Vault.Auth.Kubernetes.ServiceAccountRef = &esmeta.ServiceAccountSelector{
  766. Name: "vault-sa",
  767. Namespace: &namespace, // fixed namespace!
  768. }
  769. })
  770. // first request creates a new client:
  771. c1, err := getVaultClient(prov, store, nil, namespace)
  772. if err != nil {
  773. t.Fatal(err)
  774. }
  775. // seconds request should retrieve cached client instance:
  776. c2, err := getVaultClient(prov, store, nil, namespace)
  777. if err != nil {
  778. t.Fatal(err)
  779. }
  780. if c1 != c2 {
  781. t.Fatal("Expected a cached client instance")
  782. }
  783. // third request should retrieve cached client instance even when using a different namespace,
  784. // because the ClusterSecretStore references a ServiceAccount of a specific namespace:
  785. c3, err := getVaultClient(prov, store, nil, "another-namespace")
  786. if err != nil {
  787. t.Fatal(err)
  788. }
  789. if c3 != c1 {
  790. t.Fatal("Expected a cached client instance")
  791. }
  792. }
  793. func TestCacheWithReferentSpec(t *testing.T) {
  794. t.Cleanup(resetCache)
  795. enableCache = true
  796. initCache(defaultCacheSize)
  797. prov := &Provider{
  798. NewVaultClient: fake.ClientWithLoginMock,
  799. }
  800. store := makeClusterSecretStore(func(s *esv1.SecretStore) {
  801. s.Spec.Provider.Vault.Auth.Kubernetes.ServiceAccountRef = &esmeta.ServiceAccountSelector{
  802. Name: "vault-sa",
  803. // No fixed namespace!
  804. }
  805. })
  806. // first request creates a new client:
  807. c1, err := getVaultClient(prov, store, nil, "default")
  808. if err != nil {
  809. t.Fatal(err)
  810. }
  811. // seconds request should retrieve cached client instance:
  812. c2, err := getVaultClient(prov, store, nil, "default")
  813. if err != nil {
  814. t.Fatal(err)
  815. }
  816. if c1 != c2 {
  817. t.Fatal("Expected a cached client instance")
  818. }
  819. // third request should retrieve a new client instance,
  820. // because the ServiceAccount namespace depends on the namespace of the referent:
  821. c3, err := getVaultClient(prov, store, nil, "another-namespace")
  822. if err != nil {
  823. t.Fatal(err)
  824. }
  825. if c3 == c1 {
  826. t.Fatal("Expected a new client instance")
  827. }
  828. }
  829. func resetCache() {
  830. enableCache = false
  831. clientCache = nil
  832. }
  833. func TestValidateTokenExpiry(t *testing.T) {
  834. t.Run("skip checkToken when token expiry is in the future", func(t *testing.T) {
  835. futureExpiry := time.Now().Add(1 * time.Hour)
  836. c := &client{
  837. store: makeValidSecretStore().Spec.Provider.Vault,
  838. storeKind: esv1.SecretStoreKind,
  839. tokenExpiryTime: &futureExpiry,
  840. }
  841. result, err := c.Validate()
  842. if err != nil {
  843. t.Fatalf("unexpected error: %v", err)
  844. }
  845. if result != esv1.ValidationResultReady {
  846. t.Fatalf("expected ValidationResultReady, got %v", result)
  847. }
  848. })
  849. t.Run("call checkToken when token expiry is in the past", func(t *testing.T) {
  850. pastExpiry := time.Now().Add(-1 * time.Hour)
  851. c := &client{
  852. store: makeValidSecretStore().Spec.Provider.Vault,
  853. storeKind: esv1.SecretStoreKind,
  854. tokenExpiryTime: &pastExpiry,
  855. token: fake.Token{
  856. LookupSelfWithContextFn: func(ctx context.Context) (*vault.Secret, error) {
  857. return nil, errors.New("token expired")
  858. },
  859. },
  860. }
  861. result, err := c.Validate()
  862. if err == nil {
  863. t.Fatal("expected error, got nil")
  864. }
  865. if result != esv1.ValidationResultError {
  866. t.Fatalf("expected ValidationResultError, got %v", result)
  867. }
  868. })
  869. t.Run("call checkToken when token expiry is nil", func(t *testing.T) {
  870. c := &client{
  871. store: makeValidSecretStore().Spec.Provider.Vault,
  872. storeKind: esv1.SecretStoreKind,
  873. tokenExpiryTime: nil,
  874. token: fake.Token{
  875. LookupSelfWithContextFn: func(ctx context.Context) (*vault.Secret, error) {
  876. return nil, errors.New("lookup failed")
  877. },
  878. },
  879. }
  880. result, err := c.Validate()
  881. if err == nil {
  882. t.Fatal("expected error, got nil")
  883. }
  884. if result != esv1.ValidationResultError {
  885. t.Fatalf("expected ValidationResultError, got %v", result)
  886. }
  887. })
  888. }
  889. func TestRegisterVaultCacheFlagsDefaults(t *testing.T) {
  890. _, flags := registerVaultCacheFlags(pflag.ContinueOnError)
  891. // The deprecated size flag shares the non-zero default of the flag that
  892. // replaces it. That is why resolveVaultCacheConfig has to look at
  893. // fs.Changed instead of the value.
  894. if flags.size != defaultCacheSize {
  895. t.Errorf("vault-token-cache-size default = %d, want %d", flags.size, defaultCacheSize)
  896. }
  897. if flags.expSize != defaultCacheSize {
  898. t.Errorf("experimental-vault-token-cache-size default = %d, want %d", flags.expSize, defaultCacheSize)
  899. }
  900. if flags.enable || flags.expEnable {
  901. t.Errorf("cache enable flags should default to false, got enable=%v expEnable=%v", flags.enable, flags.expEnable)
  902. }
  903. }
  904. func TestResolveVaultCacheConfig(t *testing.T) {
  905. cases := map[string]struct {
  906. reason string
  907. args []string
  908. wantEnable bool
  909. wantSize int
  910. }{
  911. "NoFlagSet": {
  912. reason: "Should keep the supported defaults when no flag is passed",
  913. args: []string{},
  914. wantEnable: false,
  915. wantSize: defaultCacheSize,
  916. },
  917. "SupportedFlagsOnly": {
  918. reason: "Should honor --vault-token-cache-size, which the deprecated default used to overwrite",
  919. args: []string{"--enable-vault-token-cache", "--vault-token-cache-size=4096"},
  920. wantEnable: true,
  921. wantSize: 4096,
  922. },
  923. "DeprecatedFlagsOnly": {
  924. reason: "Should apply the deprecated flags when they are explicitly passed",
  925. args: []string{"--experimental-enable-vault-token-cache", "--experimental-vault-token-cache-size=8192"},
  926. wantEnable: true,
  927. wantSize: 8192,
  928. },
  929. "BothSizeFlagsSet": {
  930. reason: "Should let the deprecated size win, which is the existing precedence",
  931. args: []string{"--vault-token-cache-size=4096", "--experimental-vault-token-cache-size=8192"},
  932. wantEnable: false,
  933. wantSize: 8192,
  934. },
  935. "DeprecatedEnableExplicitlyFalse": {
  936. reason: "Should honor an explicit false instead of forcing the cache on",
  937. args: []string{"--enable-vault-token-cache", "--experimental-enable-vault-token-cache=false"},
  938. wantEnable: false,
  939. wantSize: defaultCacheSize,
  940. },
  941. }
  942. for name, tc := range cases {
  943. t.Run(name, func(t *testing.T) {
  944. fs, flags := registerVaultCacheFlags(pflag.ContinueOnError)
  945. if err := fs.Parse(tc.args); err != nil {
  946. t.Fatalf("%s: unexpected parse error: %v", tc.reason, err)
  947. }
  948. gotEnable, gotSize := resolveVaultCacheConfig(fs, flags)
  949. if gotEnable != tc.wantEnable {
  950. t.Errorf("%s: enable = %v, want %v", tc.reason, gotEnable, tc.wantEnable)
  951. }
  952. if gotSize != tc.wantSize {
  953. t.Errorf("%s: size = %d, want %d", tc.reason, gotSize, tc.wantSize)
  954. }
  955. })
  956. }
  957. }