vault_test.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676
  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. "bytes"
  15. "context"
  16. "encoding/json"
  17. "errors"
  18. "fmt"
  19. "io/ioutil"
  20. "net/http"
  21. "testing"
  22. "github.com/crossplane/crossplane-runtime/pkg/test"
  23. "github.com/google/go-cmp/cmp"
  24. vault "github.com/hashicorp/vault/api"
  25. corev1 "k8s.io/api/core/v1"
  26. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  27. kclient "sigs.k8s.io/controller-runtime/pkg/client"
  28. esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
  29. esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
  30. "github.com/external-secrets/external-secrets/pkg/provider/vault/fake"
  31. )
  32. const (
  33. tokenSecretName = "example-secret-token"
  34. secretDataString = "some-creds"
  35. )
  36. func makeValidSecretStoreWithVersion(v esv1alpha1.VaultKVStoreVersion) *esv1alpha1.SecretStore {
  37. return &esv1alpha1.SecretStore{
  38. ObjectMeta: metav1.ObjectMeta{
  39. Name: "vault-store",
  40. Namespace: "default",
  41. },
  42. Spec: esv1alpha1.SecretStoreSpec{
  43. Provider: &esv1alpha1.SecretStoreProvider{
  44. Vault: &esv1alpha1.VaultProvider{
  45. Server: "vault.example.com",
  46. Path: "secret",
  47. Version: v,
  48. Auth: esv1alpha1.VaultAuth{
  49. Kubernetes: &esv1alpha1.VaultKubernetesAuth{
  50. Path: "kubernetes",
  51. Role: "kubernetes-auth-role",
  52. ServiceAccountRef: &esmeta.ServiceAccountSelector{
  53. Name: "example-sa",
  54. },
  55. },
  56. },
  57. },
  58. },
  59. },
  60. }
  61. }
  62. func makeValidSecretStore() *esv1alpha1.SecretStore {
  63. return makeValidSecretStoreWithVersion(esv1alpha1.VaultKVStoreV2)
  64. }
  65. func makeValidSecretStoreWithCerts() *esv1alpha1.SecretStore {
  66. return &esv1alpha1.SecretStore{
  67. ObjectMeta: metav1.ObjectMeta{
  68. Name: "vault-store",
  69. Namespace: "default",
  70. },
  71. Spec: esv1alpha1.SecretStoreSpec{
  72. Provider: &esv1alpha1.SecretStoreProvider{
  73. Vault: &esv1alpha1.VaultProvider{
  74. Server: "vault.example.com",
  75. Path: "secret",
  76. Version: esv1alpha1.VaultKVStoreV2,
  77. Auth: esv1alpha1.VaultAuth{
  78. Cert: &esv1alpha1.VaultCertAuth{
  79. ClientCert: esmeta.SecretKeySelector{
  80. Name: "tls-auth-certs",
  81. Key: "tls.crt",
  82. },
  83. SecretRef: esmeta.SecretKeySelector{
  84. Name: "tls-auth-certs",
  85. Key: "tls.key",
  86. },
  87. },
  88. },
  89. },
  90. },
  91. },
  92. }
  93. }
  94. func makeValidSecretStoreWithK8sCerts(isSecret bool) *esv1alpha1.SecretStore {
  95. store := makeSecretStore()
  96. caProvider := &esv1alpha1.CAProvider{
  97. Name: "vault-cert",
  98. Key: "cert",
  99. }
  100. if isSecret {
  101. caProvider.Type = "Secret"
  102. } else {
  103. caProvider.Type = "ConfigMap"
  104. }
  105. store.Spec.Provider.Vault.CAProvider = caProvider
  106. return store
  107. }
  108. func makeInvalidClusterSecretStoreWithK8sCerts() *esv1alpha1.ClusterSecretStore {
  109. return &esv1alpha1.ClusterSecretStore{
  110. TypeMeta: metav1.TypeMeta{
  111. Kind: "ClusterSecretStore",
  112. },
  113. ObjectMeta: metav1.ObjectMeta{
  114. Name: "vault-store",
  115. Namespace: "default",
  116. },
  117. Spec: esv1alpha1.SecretStoreSpec{
  118. Provider: &esv1alpha1.SecretStoreProvider{
  119. Vault: &esv1alpha1.VaultProvider{
  120. Server: "vault.example.com",
  121. Path: "secret",
  122. Version: "v2",
  123. Auth: esv1alpha1.VaultAuth{
  124. Kubernetes: &esv1alpha1.VaultKubernetesAuth{
  125. Path: "kubernetes",
  126. Role: "kubernetes-auth-role",
  127. ServiceAccountRef: &esmeta.ServiceAccountSelector{
  128. Name: "example-sa",
  129. },
  130. },
  131. },
  132. CAProvider: &esv1alpha1.CAProvider{
  133. Name: "vault-cert",
  134. Key: "cert",
  135. Type: "Secret",
  136. },
  137. },
  138. },
  139. },
  140. }
  141. }
  142. type secretStoreTweakFn func(s *esv1alpha1.SecretStore)
  143. func makeSecretStore(tweaks ...secretStoreTweakFn) *esv1alpha1.SecretStore {
  144. store := makeValidSecretStore()
  145. for _, fn := range tweaks {
  146. fn(store)
  147. }
  148. return store
  149. }
  150. func newVaultResponse(data *vault.Secret) *vault.Response {
  151. jsonData, _ := json.Marshal(data)
  152. return &vault.Response{
  153. Response: &http.Response{
  154. Body: ioutil.NopCloser(bytes.NewReader(jsonData)),
  155. },
  156. }
  157. }
  158. func newVaultResponseWithData(data map[string]interface{}) *vault.Response {
  159. return newVaultResponse(&vault.Secret{
  160. Data: data,
  161. })
  162. }
  163. func newVaultTokenIDResponse(token string) *vault.Response {
  164. return newVaultResponseWithData(map[string]interface{}{
  165. "id": token,
  166. })
  167. }
  168. type args struct {
  169. newClientFunc func(c *vault.Config) (Client, error)
  170. store esv1alpha1.GenericStore
  171. kube kclient.Client
  172. ns string
  173. }
  174. type want struct {
  175. err error
  176. }
  177. type testCase struct {
  178. reason string
  179. args args
  180. want want
  181. }
  182. func clientWithLoginMock(c *vault.Config) (Client, error) {
  183. return &fake.VaultClient{
  184. MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}),
  185. MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(
  186. newVaultTokenIDResponse("test-token"), nil, func(got *vault.Request) error { return nil }),
  187. MockSetToken: fake.NewSetTokenFn(),
  188. }, nil
  189. }
  190. func kubeMockWithSecretTokenAndServiceAcc(obj kclient.Object) error {
  191. if o, ok := obj.(*corev1.ServiceAccount); ok {
  192. o.Secrets = []corev1.ObjectReference{
  193. {
  194. Name: tokenSecretName,
  195. },
  196. }
  197. return nil
  198. }
  199. if o, ok := obj.(*corev1.Secret); ok {
  200. o.Data = map[string][]byte{
  201. "token": []byte(secretDataString),
  202. }
  203. return nil
  204. }
  205. return nil
  206. }
  207. func TestNewVault(t *testing.T) {
  208. errBoom := errors.New("boom")
  209. secretClientKey := []byte(`-----BEGIN RSA PRIVATE KEY-----
  210. MIIEpAIBAAKCAQEArfZ4HV1obFVlVNiA24tX/UOakqRnEtWXpIvaOsMaPGvvODgGe4XnyJGO32idPv85sIr7vDH9p+OhactVlJV1fu5SZoZ7pg4jTCLqVDCb3IRD++yik2Sw58YayNe3HiaCTsJQWeMXLzfaqOeyk6bEpBCJo09+3QxUWxijgJ7YZCb+Gi8pf3ZWeSZG+rGNNvXHmTs1Yu1H849SYXu+uJOd/R3ZSTw8CxFe4eTLgbCnPf6tgA8Sg2hc+CAZxunPP2JLZWbiJXxjNRoypso6MAJ1FRkx5sTJiLg6UoLvd95/S/lCVOR2PDlM1hg7ox8VEd4QHky7tLx7gji/5hHQKJQSTwIDAQABAoIBAQCYPICQ8hVX+MNcpLrfZenycR7sBYNOMC0silbH5cUn6yzFfgHuRxi3pOnrCJnTb3cE0BvMbdMVAVdYReD2znSsR9NEdZvvjZ/GGSgH1SIQsI7t//+mDQ/jRLJb4KsXb4vJcLLwdpLrd22bMmhMXjzndrF8gSz8NLX9omozPM8RlLxjzPzYOdlX/Zw8V68qQH2Ic04KbtnCwyAUIgAJxYtn/uYB8lzILBkyzQqwhQKkDDZQ0wbZT0hP6z+HgsdifwQvHG1GZAgCuzzyXrL/4TgDaDhYdMVoBA4+HPmzqm5MkBvjH4oqroxjRofUroVix0OGXZJMI1OJ0z/ubzmwCq5BAoGBANqbwzAydUJs0P+GFL94K/Y6tXULKA2c9N0crbxoxheobRpuJvhpW1ZE/9UGpaYX1Rw3nW4x+Jwvt83YkgHAlR4LgEwDvdJPZobybfqifQDiraUO0t62Crn8mSxOsFCugtRIFniwnX67w3uKxiSdCZYbJGs9JEDTpxRG/PSWq3QlAoGBAMu3zOv1PJAhOky7VcxFxWQPEMY+t2PA/sneD01/qgGuhlTwL4QlpywmBqXcI070dcvcBkP0flnWI7y5cnuE1+55twmsrvfaS8s1+AYje0b35DsaF2vtKuJrXC0AGKP+/eiycd9cbvVW2GWOxE7Ui76Mj95MARK8ZNjt0wJagQhjAoGASm9dD80uhhadN1RFPkjB1054OMk6sx/tdFhug8e9I5MSyzwUguME2aQW5EcmIh7dToVVUo8rUqsgz7NdS8FyRM+vuLJRcQneJDbp4bxwCdwlOh2JCZI8psVutlp4yJATNgrxs9iXV+7BChDflNnvyK+nP+iKrpQiwNHHEdU3vg0CgYEAvEpwD4+loJn1psJn9NxwK6F5IaMKIhtZ4/9pKXpcCh3jb1JouL2MnFOxRVAJGor87aW57Mlol2RDt8W4OM56PqMlOL3xIokUEQka66GT6e5pdu8QwuJ9BrWwhq9WFw4yZQe6FHb836qbbJLegvYVC9QjjZW2UDjtBUwcAkrghH0CgYBUMmMOCwIfMEtMaWxZRGdxRabazLhn7TXhBpVTuv7WouPaXYd7ZGjCTMKAuVa/E4afBlxgemnqBuX90gHpK/dDmn9l+lp8GZey0grJ7G0x5HEMiKziaX5PrgAcKbQ70m9ZNZ1deYhsC05X8rHNexZB6ns7Yms9L7qnlAy51ZH2zw==
  211. -----END RSA PRIVATE KEY-----`)
  212. clientCrt := []byte(`-----BEGIN CERTIFICATE-----
  213. MIICsTCCAZkCFEJJ4daz5sxkFlzq9n1djLEuG7bmMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNVBAMMCHZhdWx0LWNhMB4XDTIxMDcyMDA4MTQxM1oXDTIyMDcyMDA4MTQxM1owFzEVMBMGA1UEAwwMdmF1bHQtY2xpZW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArfZ4HV1obFVlVNiA24tX/UOakqRnEtWXpIvaOsMaPGvvODgGe4XnyJGO32idPv85sIr7vDH9p+OhactVlJV1fu5SZoZ7pg4jTCLqVDCb3IRD++yik2Sw58YayNe3HiaCTsJQWeMXLzfaqOeyk6bEpBCJo09+3QxUWxijgJ7YZCb+Gi8pf3ZWeSZG+rGNNvXHmTs1Yu1H849SYXu+uJOd/R3ZSTw8CxFe4eTLgbCnPf6tgA8Sg2hc+CAZxunPP2JLZWbiJXxjNRoypso6MAJ1FRkx5sTJiLg6UoLvd95/S/lCVOR2PDlM1hg7ox8VEd4QHky7tLx7gji/5hHQKJQSTwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAsDYKtzScIA7bqIOmqF8rr+oLSjRhPt5OfT+KGNdXk8G3VAy1ED2tyCHaRNC7dPLq4EvcxbIXQnXPy1iZMofriGbFPAcQ2fyWUesAD6bYSpI+bYxwz6Ebb93hU5nc/FyXg8yh0kgiGbY3MrACPjxqP2+z5kcOC3u3hx3SZylgW7TeOXDTdqSbNfH1b+1rR/bVNgQQshjhU9d+c4Yv/t0u07uykBhHLWZDSnYiAeOZ8+mWuOSDkcZHE1zznx74fWgtN0zRDtr0L0w9evT9R2CnNSZGxXcEQxAlQ7SL/Jyw82TFCGEw0L4jj7jjvx0N5J8KX/DulUDE9vuVyQEJ88Epe
  214. -----END CERTIFICATE-----
  215. `)
  216. secretData := []byte(secretDataString)
  217. cases := map[string]testCase{
  218. "InvalidVaultStore": {
  219. reason: "Should return error if given an invalid vault store.",
  220. args: args{
  221. store: &esv1alpha1.SecretStore{},
  222. },
  223. want: want{
  224. err: errors.New(errVaultStore),
  225. },
  226. },
  227. "AddVaultStoreCertsError": {
  228. reason: "Should return error if given an invalid CA certificate.",
  229. args: args{
  230. store: makeSecretStore(func(s *esv1alpha1.SecretStore) {
  231. s.Spec.Provider.Vault.CABundle = []byte("badcertdata")
  232. }),
  233. },
  234. want: want{
  235. err: errors.New(errVaultCert),
  236. },
  237. },
  238. "VaultAuthFormatError": {
  239. reason: "Should return error if no valid authentication method is given.",
  240. args: args{
  241. store: makeSecretStore(func(s *esv1alpha1.SecretStore) {
  242. s.Spec.Provider.Vault.Auth = esv1alpha1.VaultAuth{}
  243. }),
  244. },
  245. want: want{
  246. err: errors.New(errAuthFormat),
  247. },
  248. },
  249. "GetKubeServiceAccountError": {
  250. reason: "Should return error if fetching kubernetes secret fails.",
  251. args: args{
  252. store: makeSecretStore(),
  253. kube: &test.MockClient{
  254. MockGet: test.NewMockGetFn(errBoom),
  255. },
  256. },
  257. want: want{
  258. err: fmt.Errorf(errGetKubeSA, "example-sa", errBoom),
  259. },
  260. },
  261. "GetKubeSecretError": {
  262. reason: "Should return error if fetching kubernetes secret fails.",
  263. args: args{
  264. store: makeSecretStore(func(s *esv1alpha1.SecretStore) {
  265. s.Spec.Provider.Vault.Auth.Kubernetes.ServiceAccountRef = nil
  266. s.Spec.Provider.Vault.Auth.Kubernetes.SecretRef = &esmeta.SecretKeySelector{
  267. Name: "vault-secret",
  268. Key: "key",
  269. }
  270. }),
  271. kube: &test.MockClient{
  272. MockGet: test.NewMockGetFn(errBoom),
  273. },
  274. },
  275. want: want{
  276. err: fmt.Errorf(errGetKubeSecret, "vault-secret", errBoom),
  277. },
  278. },
  279. "SuccessfulVaultStore": {
  280. reason: "Should return a Vault provider successfully",
  281. args: args{
  282. store: makeSecretStore(),
  283. kube: &test.MockClient{
  284. MockGet: test.NewMockGetFn(nil, kubeMockWithSecretTokenAndServiceAcc),
  285. },
  286. newClientFunc: func(c *vault.Config) (Client, error) {
  287. return &fake.VaultClient{
  288. MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}),
  289. MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(
  290. newVaultTokenIDResponse("test-token"), nil, func(got *vault.Request) error {
  291. kubeRole := makeValidSecretStore().Spec.Provider.Vault.Auth.Kubernetes.Role
  292. want := kubeParameters(kubeRole, string(secretData))
  293. if diff := cmp.Diff(want, got.Obj); diff != "" {
  294. t.Errorf("RawRequestWithContext(...): -want, +got:\n%s", diff)
  295. }
  296. return nil
  297. }),
  298. MockSetToken: fake.NewSetTokenFn(),
  299. MockToken: fake.NewTokenFn(""),
  300. MockClearToken: fake.NewClearTokenFn(),
  301. }, nil
  302. },
  303. },
  304. want: want{
  305. err: nil,
  306. },
  307. },
  308. "SuccessfulVaultStoreWithCertAuth": {
  309. reason: "Should return a Vault provider successfully",
  310. args: args{
  311. store: makeValidSecretStoreWithCerts(),
  312. kube: &test.MockClient{
  313. MockGet: test.NewMockGetFn(nil, func(obj kclient.Object) error {
  314. if o, ok := obj.(*corev1.Secret); ok {
  315. o.Data = map[string][]byte{
  316. "tls.key": secretClientKey,
  317. "tls.crt": clientCrt,
  318. }
  319. return nil
  320. }
  321. return nil
  322. }),
  323. },
  324. newClientFunc: clientWithLoginMock,
  325. },
  326. want: want{
  327. err: nil,
  328. },
  329. },
  330. "SuccessfulVaultStoreWithK8sCertSecret": {
  331. reason: "Should return a Vault prodvider with the cert from k8s",
  332. args: args{
  333. store: makeValidSecretStoreWithK8sCerts(true),
  334. kube: &test.MockClient{
  335. MockGet: test.NewMockGetFn(nil, func(obj kclient.Object) error {
  336. if o, ok := obj.(*corev1.Secret); ok {
  337. o.Data = map[string][]byte{
  338. "cert": clientCrt,
  339. "token": secretData,
  340. }
  341. return nil
  342. }
  343. if o, ok := obj.(*corev1.ServiceAccount); ok {
  344. o.Secrets = []corev1.ObjectReference{
  345. {
  346. Name: tokenSecretName,
  347. },
  348. }
  349. return nil
  350. }
  351. return nil
  352. }),
  353. },
  354. newClientFunc: clientWithLoginMock,
  355. },
  356. want: want{
  357. err: nil,
  358. },
  359. },
  360. "GetCertNamespaceMissingError": {
  361. reason: "Should return an error if namespace is missing and is a ClusterSecretStore",
  362. args: args{
  363. store: makeInvalidClusterSecretStoreWithK8sCerts(),
  364. kube: &test.MockClient{
  365. MockGet: test.NewMockGetFn(nil, kubeMockWithSecretTokenAndServiceAcc),
  366. },
  367. },
  368. want: want{
  369. err: errors.New(errCANamespace),
  370. },
  371. },
  372. "GetCertSecretKeyMissingError": {
  373. reason: "Should return an error if the secret key is missing",
  374. args: args{
  375. store: makeValidSecretStoreWithK8sCerts(true),
  376. kube: &test.MockClient{
  377. MockGet: test.NewMockGetFn(nil, kubeMockWithSecretTokenAndServiceAcc),
  378. },
  379. newClientFunc: clientWithLoginMock,
  380. },
  381. want: want{
  382. err: fmt.Errorf(errVaultCert, errors.New(`cannot find secret data for key: "cert"`)),
  383. },
  384. },
  385. "SuccessfulVaultStoreWithK8sCertConfigMap": {
  386. reason: "Should return a Vault prodvider with the cert from k8s",
  387. args: args{
  388. store: makeValidSecretStoreWithK8sCerts(false),
  389. kube: &test.MockClient{
  390. MockGet: test.NewMockGetFn(nil, func(obj kclient.Object) error {
  391. if o, ok := obj.(*corev1.ConfigMap); ok {
  392. o.Data = map[string]string{
  393. "cert": string(clientCrt),
  394. }
  395. return nil
  396. }
  397. if o, ok := obj.(*corev1.ServiceAccount); ok {
  398. o.Secrets = []corev1.ObjectReference{
  399. {
  400. Name: tokenSecretName,
  401. },
  402. }
  403. return nil
  404. }
  405. if o, ok := obj.(*corev1.Secret); ok {
  406. o.Data = map[string][]byte{
  407. "token": secretData,
  408. }
  409. return nil
  410. }
  411. return nil
  412. }),
  413. },
  414. newClientFunc: clientWithLoginMock,
  415. },
  416. want: want{
  417. err: nil,
  418. },
  419. },
  420. "GetCertConfigMapMissingError": {
  421. reason: "Should return an error if the config map key is missing",
  422. args: args{
  423. store: makeValidSecretStoreWithK8sCerts(false),
  424. kube: &test.MockClient{
  425. MockGet: test.NewMockGetFn(nil, func(obj kclient.Object) error {
  426. if o, ok := obj.(*corev1.ServiceAccount); ok {
  427. o.Secrets = []corev1.ObjectReference{
  428. {
  429. Name: tokenSecretName,
  430. },
  431. }
  432. return nil
  433. }
  434. if o, ok := obj.(*corev1.Secret); ok {
  435. o.Data = map[string][]byte{
  436. "token": secretData,
  437. }
  438. return nil
  439. }
  440. return nil
  441. }),
  442. },
  443. newClientFunc: clientWithLoginMock,
  444. },
  445. want: want{
  446. err: fmt.Errorf(errConfigMapFmt, "cert"),
  447. },
  448. },
  449. "GetCertificateFormatError": {
  450. reason: "Should return error if client certificate is in wrong format.",
  451. args: args{
  452. store: makeValidSecretStoreWithCerts(),
  453. kube: &test.MockClient{
  454. MockGet: test.NewMockGetFn(nil, func(obj kclient.Object) error {
  455. if o, ok := obj.(*corev1.Secret); ok {
  456. o.Data = map[string][]byte{
  457. "tls.key": secretClientKey,
  458. "tls.crt": []byte("cert with mistak"),
  459. }
  460. return nil
  461. }
  462. return nil
  463. }),
  464. },
  465. newClientFunc: clientWithLoginMock,
  466. },
  467. want: want{
  468. err: fmt.Errorf(errClientTLSAuth, "tls: failed to find any PEM data in certificate input"),
  469. },
  470. },
  471. "GetKeyFormatError": {
  472. reason: "Should return error if client key is in wrong format.",
  473. args: args{
  474. store: makeValidSecretStoreWithCerts(),
  475. kube: &test.MockClient{
  476. MockGet: test.NewMockGetFn(nil, func(obj kclient.Object) error {
  477. if o, ok := obj.(*corev1.Secret); ok {
  478. o.Data = map[string][]byte{
  479. "tls.key": []byte("key with mistake"),
  480. "tls.crt": clientCrt,
  481. }
  482. return nil
  483. }
  484. return nil
  485. }),
  486. },
  487. newClientFunc: clientWithLoginMock,
  488. },
  489. want: want{
  490. err: fmt.Errorf(errClientTLSAuth, "tls: failed to find any PEM data in key input"),
  491. },
  492. },
  493. }
  494. for name, tc := range cases {
  495. t.Run(name, func(t *testing.T) {
  496. vaultTest(t, name, tc)
  497. })
  498. }
  499. }
  500. func vaultTest(t *testing.T, name string, tc testCase) {
  501. conn := &connector{
  502. newVaultClient: tc.args.newClientFunc,
  503. }
  504. if tc.args.newClientFunc == nil {
  505. conn.newVaultClient = newVaultClient
  506. }
  507. _, err := conn.NewClient(context.Background(), tc.args.store, tc.args.kube, tc.args.ns)
  508. if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
  509. t.Errorf("\n%s\nvault.New(...): -want error, +got error:\n%s", tc.reason, diff)
  510. }
  511. }
  512. func TestGetSecretMap(t *testing.T) {
  513. errBoom := errors.New("boom")
  514. secret := map[string]interface{}{
  515. "access_key": "access_key",
  516. "access_secret": "access_secret",
  517. }
  518. secretWithNilVal := map[string]interface{}{
  519. "access_key": "access_key",
  520. "access_secret": "access_secret",
  521. "token": nil,
  522. }
  523. type args struct {
  524. store *esv1alpha1.VaultProvider
  525. kube kclient.Client
  526. vClient Client
  527. ns string
  528. data esv1alpha1.ExternalSecretDataRemoteRef
  529. }
  530. type want struct {
  531. err error
  532. }
  533. cases := map[string]struct {
  534. reason string
  535. args args
  536. want want
  537. }{
  538. "ReadSecretKV1": {
  539. reason: "Should map the secret even if it has a nil value",
  540. args: args{
  541. store: makeValidSecretStoreWithVersion(esv1alpha1.VaultKVStoreV1).Spec.Provider.Vault,
  542. vClient: &fake.VaultClient{
  543. MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}),
  544. MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(
  545. newVaultResponseWithData(secret), nil,
  546. ),
  547. },
  548. },
  549. want: want{
  550. err: nil,
  551. },
  552. },
  553. "ReadSecretKV2": {
  554. reason: "Should map the secret even if it has a nil value",
  555. args: args{
  556. store: makeValidSecretStoreWithVersion(esv1alpha1.VaultKVStoreV2).Spec.Provider.Vault,
  557. vClient: &fake.VaultClient{
  558. MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}),
  559. MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(
  560. newVaultResponseWithData(
  561. map[string]interface{}{
  562. "data": secret,
  563. },
  564. ), nil,
  565. ),
  566. },
  567. },
  568. want: want{
  569. err: nil,
  570. },
  571. },
  572. "ReadSecretWithNilValueKV1": {
  573. reason: "Should map the secret even if it has a nil value",
  574. args: args{
  575. store: makeValidSecretStoreWithVersion(esv1alpha1.VaultKVStoreV1).Spec.Provider.Vault,
  576. vClient: &fake.VaultClient{
  577. MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}),
  578. MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(
  579. newVaultResponseWithData(secretWithNilVal), nil,
  580. ),
  581. },
  582. },
  583. want: want{
  584. err: nil,
  585. },
  586. },
  587. "ReadSecretWithNilValueKV2": {
  588. reason: "Should map the secret even if it has a nil value",
  589. args: args{
  590. store: makeValidSecretStoreWithVersion(esv1alpha1.VaultKVStoreV2).Spec.Provider.Vault,
  591. vClient: &fake.VaultClient{
  592. MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}),
  593. MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(
  594. newVaultResponseWithData(
  595. map[string]interface{}{
  596. "data": secretWithNilVal,
  597. },
  598. ), nil,
  599. ),
  600. },
  601. },
  602. want: want{
  603. err: nil,
  604. },
  605. },
  606. "ReadSecretError": {
  607. reason: "Should return error if vault client fails to read secret.",
  608. args: args{
  609. store: makeSecretStore().Spec.Provider.Vault,
  610. vClient: &fake.VaultClient{
  611. MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}),
  612. MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(nil, errBoom),
  613. },
  614. },
  615. want: want{
  616. err: fmt.Errorf(errReadSecret, errBoom),
  617. },
  618. },
  619. }
  620. for name, tc := range cases {
  621. t.Run(name, func(t *testing.T) {
  622. vStore := &client{
  623. kube: tc.args.kube,
  624. client: tc.args.vClient,
  625. store: tc.args.store,
  626. namespace: tc.args.ns,
  627. }
  628. _, err := vStore.GetSecretMap(context.Background(), tc.args.data)
  629. if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" {
  630. t.Errorf("\n%s\nvault.GetSecretMap(...): -want error, +got error:\n%s", tc.reason, diff)
  631. }
  632. })
  633. }
  634. }