manager_test.go 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924
  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 clientmanager
  14. import (
  15. "context"
  16. "crypto/rand"
  17. "crypto/rsa"
  18. "crypto/tls"
  19. "crypto/x509"
  20. "crypto/x509/pkix"
  21. "encoding/pem"
  22. "math/big"
  23. "net"
  24. "strings"
  25. "sync"
  26. "testing"
  27. "time"
  28. "github.com/go-logr/logr"
  29. "github.com/stretchr/testify/assert"
  30. "github.com/stretchr/testify/require"
  31. "google.golang.org/grpc"
  32. "google.golang.org/grpc/credentials"
  33. corev1 "k8s.io/api/core/v1"
  34. apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
  35. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  36. "k8s.io/apimachinery/pkg/runtime"
  37. "k8s.io/apimachinery/pkg/types"
  38. utilruntime "k8s.io/apimachinery/pkg/util/runtime"
  39. clientgoscheme "k8s.io/client-go/kubernetes/scheme"
  40. "sigs.k8s.io/controller-runtime/pkg/client"
  41. fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
  42. "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
  43. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  44. esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
  45. pb "github.com/external-secrets/external-secrets/proto/provider"
  46. )
  47. func TestManagerGet(t *testing.T) {
  48. scheme := runtime.NewScheme()
  49. // add kubernetes schemes
  50. utilruntime.Must(clientgoscheme.AddToScheme(scheme))
  51. utilruntime.Must(apiextensionsv1.AddToScheme(scheme))
  52. // add external-secrets schemes
  53. utilruntime.Must(esv1.AddToScheme(scheme))
  54. // We have a test provider to control
  55. // the behavior of the NewClient func.
  56. fakeProvider := &WrapProvider{}
  57. esv1.ForceRegister(fakeProvider, &esv1.SecretStoreProvider{
  58. AWS: &esv1.AWSProvider{},
  59. }, esv1.MaintenanceStatusMaintained)
  60. // fake clients are re-used to compare the
  61. // in-memory reference
  62. clientA := &MockFakeClient{id: "1"}
  63. clientB := &MockFakeClient{id: "2"}
  64. const testNamespace = "foo"
  65. readyStatus := esv1.SecretStoreStatus{
  66. Conditions: []esv1.SecretStoreStatusCondition{
  67. {
  68. Type: esv1.SecretStoreReady,
  69. Status: corev1.ConditionTrue,
  70. },
  71. },
  72. }
  73. fakeSpec := esv1.SecretStoreSpec{
  74. Provider: &esv1.SecretStoreProvider{
  75. AWS: &esv1.AWSProvider{},
  76. },
  77. }
  78. defaultStore := &esv1.SecretStore{
  79. TypeMeta: metav1.TypeMeta{Kind: esv1.SecretStoreKind},
  80. ObjectMeta: metav1.ObjectMeta{
  81. Name: "foo",
  82. Namespace: testNamespace,
  83. },
  84. Spec: fakeSpec,
  85. Status: readyStatus,
  86. }
  87. otherStore := &esv1.SecretStore{
  88. TypeMeta: metav1.TypeMeta{Kind: esv1.SecretStoreKind},
  89. ObjectMeta: metav1.ObjectMeta{
  90. Name: "other",
  91. Namespace: testNamespace,
  92. },
  93. Spec: fakeSpec,
  94. Status: readyStatus,
  95. }
  96. var mgr *Manager
  97. provKey := storeKey(fakeProvider)
  98. type fields struct {
  99. client client.Client
  100. clientMap map[clientKey]*clientVal
  101. }
  102. type args struct {
  103. storeRef esv1.SecretStoreRef
  104. namespace string
  105. sourceRef *esv1.StoreGeneratorSourceRef
  106. }
  107. tests := []struct {
  108. name string
  109. fields fields
  110. args args
  111. clientConstructor func(
  112. ctx context.Context,
  113. store esv1.GenericStore,
  114. kube client.Client,
  115. namespace string) (esv1.SecretsClient, error)
  116. verify func(esv1.SecretsClient)
  117. afterClose func()
  118. want esv1.SecretsClient
  119. wantErr bool
  120. }{
  121. {
  122. name: "creates a new client from storeRef and stores it",
  123. wantErr: false,
  124. fields: fields{
  125. client: fakeclient.NewClientBuilder().
  126. WithScheme(scheme).
  127. WithObjects(defaultStore).
  128. Build(),
  129. clientMap: make(map[clientKey]*clientVal),
  130. },
  131. args: args{
  132. storeRef: esv1.SecretStoreRef{
  133. Name: defaultStore.Name,
  134. Kind: esv1.SecretStoreKind,
  135. },
  136. namespace: defaultStore.Namespace,
  137. sourceRef: nil,
  138. },
  139. clientConstructor: func(_ context.Context, _ esv1.GenericStore, _ client.Client, _ string) (esv1.SecretsClient, error) {
  140. return clientA, nil
  141. },
  142. verify: func(sc esv1.SecretsClient) {
  143. // we now must have this provider in the clientMap
  144. // and it mustbe the client defined in clientConstructor
  145. assert.NotNil(t, sc)
  146. c, ok := mgr.clientMap[provKey]
  147. require.True(t, ok)
  148. assert.Same(t, c.client, clientA)
  149. },
  150. afterClose: func() {
  151. v, ok := mgr.clientMap[provKey]
  152. assert.False(t, ok)
  153. assert.Nil(t, v)
  154. },
  155. },
  156. {
  157. name: "creates a new client using both storeRef and sourceRef",
  158. wantErr: false,
  159. fields: fields{
  160. client: fakeclient.NewClientBuilder().
  161. WithScheme(scheme).
  162. WithObjects(otherStore).
  163. Build(),
  164. clientMap: make(map[clientKey]*clientVal),
  165. },
  166. args: args{
  167. storeRef: esv1.SecretStoreRef{
  168. Name: defaultStore.Name,
  169. Kind: esv1.SecretStoreKind,
  170. },
  171. // this should take precedence
  172. sourceRef: &esv1.StoreGeneratorSourceRef{
  173. SecretStoreRef: &esv1.SecretStoreRef{
  174. Name: otherStore.Name,
  175. Kind: esv1.SecretStoreKind,
  176. },
  177. },
  178. namespace: defaultStore.Namespace,
  179. },
  180. clientConstructor: func(_ context.Context, _ esv1.GenericStore, _ client.Client, _ string) (esv1.SecretsClient, error) {
  181. return clientB, nil
  182. },
  183. verify: func(sc esv1.SecretsClient) {
  184. // we now must have this provider in the clientMap
  185. // and it mustbe the client defined in clientConstructor
  186. assert.NotNil(t, sc)
  187. c, ok := mgr.clientMap[provKey]
  188. assert.True(t, ok)
  189. assert.Same(t, c.client, clientB)
  190. },
  191. afterClose: func() {
  192. v, ok := mgr.clientMap[provKey]
  193. assert.False(t, ok)
  194. assert.True(t, clientB.closeCalled)
  195. assert.Nil(t, v)
  196. },
  197. },
  198. {
  199. name: "retrieve cached client when store matches",
  200. wantErr: false,
  201. fields: fields{
  202. client: fakeclient.NewClientBuilder().
  203. WithScheme(scheme).
  204. WithObjects(defaultStore).
  205. Build(),
  206. clientMap: map[clientKey]*clientVal{
  207. provKey: {
  208. client: clientA,
  209. store: defaultStore,
  210. },
  211. },
  212. },
  213. args: args{
  214. storeRef: esv1.SecretStoreRef{
  215. Name: defaultStore.Name,
  216. Kind: esv1.SecretStoreKind,
  217. },
  218. namespace: defaultStore.Namespace,
  219. sourceRef: nil,
  220. },
  221. clientConstructor: func(_ context.Context, _ esv1.GenericStore, _ client.Client, _ string) (esv1.SecretsClient, error) {
  222. // constructor should not be called,
  223. // the client from the cache should be returned instead
  224. t.Fail()
  225. return nil, nil
  226. },
  227. verify: func(sc esv1.SecretsClient) {
  228. // verify that the secretsClient is the one from cache
  229. assert.NotNil(t, sc)
  230. c, ok := mgr.clientMap[provKey]
  231. assert.True(t, ok)
  232. assert.Same(t, c.client, clientA)
  233. assert.Same(t, sc, clientA)
  234. },
  235. afterClose: func() {
  236. v, ok := mgr.clientMap[provKey]
  237. assert.False(t, ok)
  238. assert.True(t, clientA.closeCalled)
  239. assert.Nil(t, v)
  240. },
  241. },
  242. {
  243. name: "create new client when store doesn't match",
  244. wantErr: false,
  245. fields: fields{
  246. client: fakeclient.NewClientBuilder().
  247. WithScheme(scheme).
  248. WithObjects(otherStore).
  249. Build(),
  250. clientMap: map[clientKey]*clientVal{
  251. provKey: {
  252. // we have clientA in cache pointing at defaultStore
  253. client: clientA,
  254. store: defaultStore,
  255. },
  256. },
  257. },
  258. args: args{
  259. storeRef: esv1.SecretStoreRef{
  260. Name: otherStore.Name,
  261. Kind: esv1.SecretStoreKind,
  262. },
  263. namespace: otherStore.Namespace,
  264. sourceRef: nil,
  265. },
  266. clientConstructor: func(_ context.Context, _ esv1.GenericStore, _ client.Client, _ string) (esv1.SecretsClient, error) {
  267. // because there is a store mismatch
  268. // we create a new client
  269. return clientB, nil
  270. },
  271. verify: func(sc esv1.SecretsClient) {
  272. // verify that SecretsClient is NOT the one from cache
  273. assert.NotNil(t, sc)
  274. c, ok := mgr.clientMap[provKey]
  275. assert.True(t, ok)
  276. assert.Same(t, c.client, clientB)
  277. assert.Same(t, sc, clientB)
  278. assert.True(t, clientA.closeCalled)
  279. },
  280. afterClose: func() {
  281. v, ok := mgr.clientMap[provKey]
  282. assert.False(t, ok)
  283. assert.True(t, clientB.closeCalled)
  284. assert.Nil(t, v)
  285. },
  286. },
  287. }
  288. for _, tt := range tests {
  289. t.Run(tt.name, func(t *testing.T) {
  290. mgr = &Manager{
  291. log: logr.Discard(),
  292. client: tt.fields.client,
  293. enableFloodgate: true,
  294. clientMap: tt.fields.clientMap,
  295. }
  296. fakeProvider.newClientFunc = tt.clientConstructor
  297. clientA.closeCalled = false
  298. clientB.closeCalled = false
  299. got, err := mgr.Get(context.Background(), tt.args.storeRef, tt.args.namespace, tt.args.sourceRef)
  300. if (err != nil) != tt.wantErr {
  301. t.Errorf("Manager.Get() error = %v, wantErr %v", err, tt.wantErr)
  302. return
  303. }
  304. tt.verify(got)
  305. mgr.Close(context.Background())
  306. tt.afterClose()
  307. })
  308. }
  309. }
  310. func TestShouldProcessSecret(t *testing.T) {
  311. scheme := runtime.NewScheme()
  312. // add kubernetes schemes
  313. utilruntime.Must(clientgoscheme.AddToScheme(scheme))
  314. utilruntime.Must(apiextensionsv1.AddToScheme(scheme))
  315. // add external-secrets schemes
  316. utilruntime.Must(esv1.AddToScheme(scheme))
  317. testNamespace := "test-a"
  318. testCases := []struct {
  319. name string
  320. conditions []esv1.ClusterSecretStoreCondition
  321. namespace *corev1.Namespace
  322. wantErr string
  323. want bool
  324. }{
  325. {
  326. name: "processes a regex condition",
  327. conditions: []esv1.ClusterSecretStoreCondition{
  328. {
  329. NamespaceRegexes: []string{`test-*`},
  330. },
  331. },
  332. namespace: &corev1.Namespace{
  333. ObjectMeta: metav1.ObjectMeta{
  334. Name: testNamespace,
  335. },
  336. },
  337. want: true,
  338. },
  339. {
  340. name: "process multiple regexes",
  341. conditions: []esv1.ClusterSecretStoreCondition{
  342. {
  343. NamespaceRegexes: []string{`nope`, `test-*`},
  344. },
  345. },
  346. namespace: &corev1.Namespace{
  347. ObjectMeta: metav1.ObjectMeta{
  348. Name: testNamespace,
  349. },
  350. },
  351. want: true,
  352. },
  353. {
  354. name: "shouldn't process if nothing matches",
  355. conditions: []esv1.ClusterSecretStoreCondition{
  356. {
  357. NamespaceRegexes: []string{`nope`},
  358. },
  359. },
  360. namespace: &corev1.Namespace{
  361. ObjectMeta: metav1.ObjectMeta{
  362. Name: testNamespace,
  363. },
  364. },
  365. want: false,
  366. },
  367. }
  368. for _, tt := range testCases {
  369. t.Run(tt.name, func(t *testing.T) {
  370. fakeSpec := esv1.SecretStoreSpec{
  371. Conditions: tt.conditions,
  372. }
  373. defaultStore := &esv1.ClusterSecretStore{
  374. TypeMeta: metav1.TypeMeta{Kind: esv1.ClusterSecretStoreKind},
  375. ObjectMeta: metav1.ObjectMeta{
  376. Name: "foo",
  377. Namespace: tt.namespace.Name,
  378. },
  379. Spec: fakeSpec,
  380. }
  381. client := fakeclient.NewClientBuilder().WithScheme(scheme).WithObjects(defaultStore, tt.namespace).Build()
  382. clientMap := make(map[clientKey]*clientVal)
  383. mgr := &Manager{
  384. log: logr.Discard(),
  385. client: client,
  386. enableFloodgate: true,
  387. clientMap: clientMap,
  388. }
  389. got, err := mgr.shouldProcessSecret(defaultStore, tt.namespace.Name)
  390. require.NoError(t, err)
  391. assert.Equal(t, tt.want, got)
  392. })
  393. }
  394. }
  395. func TestBuildCompatibilityStoreSerializesStore(t *testing.T) {
  396. store := &esv1.SecretStore{
  397. TypeMeta: metav1.TypeMeta{Kind: esv1.SecretStoreKind},
  398. ObjectMeta: metav1.ObjectMeta{
  399. Name: "aws-prod",
  400. Namespace: "team-a",
  401. UID: types.UID("uid-1"),
  402. Generation: 7,
  403. },
  404. Spec: esv1.SecretStoreSpec{
  405. RuntimeRef: &esv1.StoreRuntimeRef{Kind: "ClusterProviderClass", Name: "aws"},
  406. Provider: &esv1.SecretStoreProvider{
  407. Fake: &esv1.FakeProvider{Data: []esv1.FakeProviderData{{Key: "db", Value: "s3cr3t"}}},
  408. },
  409. },
  410. }
  411. out, err := buildCompatibilityStore(store)
  412. if err != nil {
  413. t.Fatalf("buildCompatibilityStore() error = %v", err)
  414. }
  415. if out.StoreName != "aws-prod" || out.StoreNamespace != "team-a" || out.StoreKind != esv1.SecretStoreKind {
  416. t.Fatalf("unexpected compatibility store metadata: %#v", out)
  417. }
  418. if out.StoreGeneration != 7 || out.StoreUid != "uid-1" {
  419. t.Fatalf("unexpected compatibility store identity: %#v", out)
  420. }
  421. if !strings.Contains(string(out.StoreSpecJson), "\"runtimeRef\"") || !strings.Contains(string(out.StoreSpecJson), "\"fake\"") {
  422. t.Fatalf("expected serialized store spec, got %s", string(out.StoreSpecJson))
  423. }
  424. }
  425. func TestGetFromStoreReturnsErrorWhenRuntimeClassMissing(t *testing.T) {
  426. scheme := runtime.NewScheme()
  427. utilruntime.Must(clientgoscheme.AddToScheme(scheme))
  428. utilruntime.Must(esv1.AddToScheme(scheme))
  429. utilruntime.Must(esv1alpha1.AddToScheme(scheme))
  430. store := &esv1.SecretStore{
  431. TypeMeta: metav1.TypeMeta{Kind: esv1.SecretStoreKind},
  432. ObjectMeta: metav1.ObjectMeta{
  433. Name: "aws-prod",
  434. Namespace: "team-a",
  435. UID: types.UID("uid-1"),
  436. Generation: 7,
  437. },
  438. Spec: esv1.SecretStoreSpec{
  439. RuntimeRef: &esv1.StoreRuntimeRef{Kind: "ClusterProviderClass", Name: "aws"},
  440. Provider: &esv1.SecretStoreProvider{
  441. Fake: &esv1.FakeProvider{Data: []esv1.FakeProviderData{{Key: "db", Value: "s3cr3t"}}},
  442. },
  443. },
  444. }
  445. kubeClient := fakeclient.NewClientBuilder().
  446. WithScheme(scheme).
  447. WithObjects(store).
  448. Build()
  449. mgr := NewManager(kubeClient, "", false)
  450. _, err := mgr.GetFromStore(context.Background(), store, "team-a")
  451. if err == nil || !strings.Contains(err.Error(), "ClusterProviderClass") {
  452. t.Fatalf("expected missing ClusterProviderClass error, got %v", err)
  453. }
  454. }
  455. func TestGetFromStoreWithRuntimeRefReturnsClientThatValidates(t *testing.T) {
  456. resetGlobalV2ConnectionPoolForTest(t)
  457. scheme := runtime.NewScheme()
  458. utilruntime.Must(clientgoscheme.AddToScheme(scheme))
  459. utilruntime.Must(apiextensionsv1.AddToScheme(scheme))
  460. utilruntime.Must(esv1.AddToScheme(scheme))
  461. utilruntime.Must(esv1alpha1.AddToScheme(scheme))
  462. server, address, tlsSecret := newRecordingProviderServer(t)
  463. store := &esv1.SecretStore{
  464. TypeMeta: metav1.TypeMeta{Kind: esv1.SecretStoreKind},
  465. ObjectMeta: metav1.ObjectMeta{
  466. Name: "aws-prod",
  467. Namespace: "team-a",
  468. UID: types.UID("uid-1"),
  469. Generation: 7,
  470. },
  471. Spec: esv1.SecretStoreSpec{
  472. RuntimeRef: &esv1.StoreRuntimeRef{Kind: "ClusterProviderClass", Name: "aws-runtime"},
  473. Provider: &esv1.SecretStoreProvider{
  474. Fake: &esv1.FakeProvider{Data: []esv1.FakeProviderData{{Key: "db", Value: "s3cr3t"}}},
  475. },
  476. },
  477. }
  478. runtimeClass := &esv1alpha1.ClusterProviderClass{
  479. ObjectMeta: metav1.ObjectMeta{
  480. Name: "aws-runtime",
  481. },
  482. Spec: esv1alpha1.ClusterProviderClassSpec{
  483. Address: address,
  484. },
  485. }
  486. kubeClient := fakeclient.NewClientBuilder().
  487. WithScheme(scheme).
  488. WithObjects(
  489. store,
  490. runtimeClass,
  491. &corev1.Secret{
  492. ObjectMeta: metav1.ObjectMeta{
  493. Name: "external-secrets-provider-tls",
  494. Namespace: "",
  495. },
  496. Data: tlsSecret,
  497. },
  498. ).
  499. Build()
  500. mgr := NewManager(kubeClient, "", false)
  501. client, err := mgr.GetFromStore(context.Background(), store, "team-a")
  502. require.NoError(t, err)
  503. result, err := client.Validate()
  504. require.NoError(t, err)
  505. assert.Equal(t, esv1.ValidationResultReady, result)
  506. assert.Equal(t, 1, server.ValidateCallCount())
  507. require.Len(t, mgr.v2PooledConnections, 0)
  508. }
  509. func TestGetFromStoreWithRuntimeRefReusesCachedClient(t *testing.T) {
  510. resetGlobalV2ConnectionPoolForTest(t)
  511. scheme := runtime.NewScheme()
  512. utilruntime.Must(clientgoscheme.AddToScheme(scheme))
  513. utilruntime.Must(apiextensionsv1.AddToScheme(scheme))
  514. utilruntime.Must(esv1.AddToScheme(scheme))
  515. utilruntime.Must(esv1alpha1.AddToScheme(scheme))
  516. server, address, tlsSecret := newRecordingProviderServer(t)
  517. store := &esv1.SecretStore{
  518. TypeMeta: metav1.TypeMeta{Kind: esv1.SecretStoreKind},
  519. ObjectMeta: metav1.ObjectMeta{
  520. Name: "aws-prod",
  521. Namespace: "team-a",
  522. UID: types.UID("uid-1"),
  523. Generation: 7,
  524. },
  525. Spec: esv1.SecretStoreSpec{
  526. RuntimeRef: &esv1.StoreRuntimeRef{Kind: "ClusterProviderClass", Name: "aws-runtime"},
  527. Provider: &esv1.SecretStoreProvider{
  528. Fake: &esv1.FakeProvider{Data: []esv1.FakeProviderData{{Key: "db", Value: "s3cr3t"}}},
  529. },
  530. },
  531. }
  532. runtimeClass := &esv1alpha1.ClusterProviderClass{
  533. ObjectMeta: metav1.ObjectMeta{Name: "aws-runtime"},
  534. Spec: esv1alpha1.ClusterProviderClassSpec{Address: address},
  535. }
  536. kubeClient := fakeclient.NewClientBuilder().
  537. WithScheme(scheme).
  538. WithObjects(
  539. store,
  540. runtimeClass,
  541. &corev1.Secret{
  542. ObjectMeta: metav1.ObjectMeta{
  543. Name: "external-secrets-provider-tls",
  544. Namespace: "",
  545. },
  546. Data: tlsSecret,
  547. },
  548. ).
  549. Build()
  550. mgr := NewManager(kubeClient, "", false)
  551. firstClient, err := mgr.GetFromStore(context.Background(), store, "team-a")
  552. require.NoError(t, err)
  553. secondClient, err := mgr.GetFromStore(context.Background(), store, "team-a")
  554. require.NoError(t, err)
  555. assert.Same(t, firstClient, secondClient)
  556. require.Len(t, mgr.v2PooledConnections, 0)
  557. result, err := firstClient.Validate()
  558. require.NoError(t, err)
  559. assert.Equal(t, esv1.ValidationResultReady, result)
  560. assert.Equal(t, 1, server.ValidateCallCount())
  561. }
  562. func TestGetFromStoreWithRuntimeRefDoesNotReuseClientAcrossSourceNamespaces(t *testing.T) {
  563. resetGlobalV2ConnectionPoolForTest(t)
  564. scheme := runtime.NewScheme()
  565. utilruntime.Must(clientgoscheme.AddToScheme(scheme))
  566. utilruntime.Must(apiextensionsv1.AddToScheme(scheme))
  567. utilruntime.Must(esv1.AddToScheme(scheme))
  568. utilruntime.Must(esv1alpha1.AddToScheme(scheme))
  569. server, address, tlsSecret := newRecordingProviderServer(t)
  570. store := &esv1.ClusterSecretStore{
  571. TypeMeta: metav1.TypeMeta{Kind: esv1.ClusterSecretStoreKind},
  572. ObjectMeta: metav1.ObjectMeta{
  573. Name: "aws-prod",
  574. UID: types.UID("uid-1"),
  575. Generation: 7,
  576. },
  577. Spec: esv1.SecretStoreSpec{
  578. RuntimeRef: &esv1.StoreRuntimeRef{Kind: "ClusterProviderClass", Name: "aws-runtime"},
  579. Provider: &esv1.SecretStoreProvider{
  580. Fake: &esv1.FakeProvider{Data: []esv1.FakeProviderData{{Key: "db", Value: "s3cr3t"}}},
  581. },
  582. },
  583. }
  584. runtimeClass := &esv1alpha1.ClusterProviderClass{
  585. ObjectMeta: metav1.ObjectMeta{Name: "aws-runtime"},
  586. Spec: esv1alpha1.ClusterProviderClassSpec{Address: address},
  587. }
  588. kubeClient := fakeclient.NewClientBuilder().
  589. WithScheme(scheme).
  590. WithObjects(
  591. store,
  592. runtimeClass,
  593. &corev1.Secret{
  594. ObjectMeta: metav1.ObjectMeta{
  595. Name: "external-secrets-provider-tls",
  596. Namespace: "",
  597. },
  598. Data: tlsSecret,
  599. },
  600. ).
  601. Build()
  602. mgr := NewManager(kubeClient, "", false)
  603. firstClient, err := mgr.GetFromStore(context.Background(), store, "team-a")
  604. require.NoError(t, err)
  605. secondClient, err := mgr.GetFromStore(context.Background(), store, "team-b")
  606. require.NoError(t, err)
  607. assert.NotSame(t, firstClient, secondClient)
  608. result, err := firstClient.Validate()
  609. require.NoError(t, err)
  610. assert.Equal(t, esv1.ValidationResultReady, result)
  611. result, err = secondClient.Validate()
  612. require.NoError(t, err)
  613. assert.Equal(t, esv1.ValidationResultReady, result)
  614. assert.Equal(t, []string{"team-a", "team-b"}, server.ValidateSourceNamespaces())
  615. }
  616. type WrapProvider struct {
  617. newClientFunc func(
  618. context.Context,
  619. esv1.GenericStore,
  620. client.Client,
  621. string) (esv1.SecretsClient, error)
  622. }
  623. // NewClient constructs a SecretsManager Provider.
  624. func (f *WrapProvider) NewClient(
  625. ctx context.Context,
  626. store esv1.GenericStore,
  627. kube client.Client,
  628. namespace string) (esv1.SecretsClient, error) {
  629. return f.newClientFunc(ctx, store, kube, namespace)
  630. }
  631. func (f *WrapProvider) Capabilities() esv1.SecretStoreCapabilities {
  632. return esv1.SecretStoreReadOnly
  633. }
  634. // ValidateStore checks if the provided store is valid.
  635. func (f *WrapProvider) ValidateStore(_ esv1.GenericStore) (admission.Warnings, error) {
  636. return nil, nil
  637. }
  638. type MockFakeClient struct {
  639. id string
  640. closeCalled bool
  641. }
  642. func (c *MockFakeClient) PushSecret(_ context.Context, _ *corev1.Secret, _ esv1.PushSecretData) error {
  643. return nil
  644. }
  645. func (c *MockFakeClient) DeleteSecret(_ context.Context, _ esv1.PushSecretRemoteRef) error {
  646. return nil
  647. }
  648. func (c *MockFakeClient) SecretExists(_ context.Context, _ esv1.PushSecretRemoteRef) (bool, error) {
  649. return false, nil
  650. }
  651. func (c *MockFakeClient) GetSecret(_ context.Context, _ esv1.ExternalSecretDataRemoteRef) ([]byte, error) {
  652. return nil, nil
  653. }
  654. func (c *MockFakeClient) Validate() (esv1.ValidationResult, error) {
  655. return esv1.ValidationResultReady, nil
  656. }
  657. // GetSecretMap returns multiple k/v pairs from the provider.
  658. func (c *MockFakeClient) GetSecretMap(_ context.Context, _ esv1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
  659. return nil, nil
  660. }
  661. // GetAllSecrets returns multiple k/v pairs from the provider.
  662. func (c *MockFakeClient) GetAllSecrets(_ context.Context, _ esv1.ExternalSecretFind) (map[string][]byte, error) {
  663. return nil, nil
  664. }
  665. func (c *MockFakeClient) Close(_ context.Context) error {
  666. c.closeCalled = true
  667. return nil
  668. }
  669. func newManagerTestScheme(t *testing.T) *runtime.Scheme {
  670. t.Helper()
  671. scheme := runtime.NewScheme()
  672. utilruntime.Must(clientgoscheme.AddToScheme(scheme))
  673. utilruntime.Must(apiextensionsv1.AddToScheme(scheme))
  674. utilruntime.Must(esv1.AddToScheme(scheme))
  675. return scheme
  676. }
  677. func resetGlobalV2ConnectionPoolForTest(t *testing.T) {
  678. t.Helper()
  679. if globalV2ConnectionPool != nil {
  680. _ = globalV2ConnectionPool.Close()
  681. }
  682. globalV2ConnectionPool = nil
  683. globalV2ConnectionPoolOnce = sync.Once{}
  684. t.Cleanup(func() {
  685. if globalV2ConnectionPool != nil {
  686. _ = globalV2ConnectionPool.Close()
  687. }
  688. globalV2ConnectionPool = nil
  689. globalV2ConnectionPoolOnce = sync.Once{}
  690. })
  691. }
  692. type recordingProviderServer struct {
  693. pb.UnimplementedSecretStoreProviderServer
  694. mu sync.Mutex
  695. validateRequests []*pb.ValidateRequest
  696. }
  697. func newRecordingProviderServer(t *testing.T) (*recordingProviderServer, string, map[string][]byte) {
  698. t.Helper()
  699. serverCert, serverKey, clientCert, clientKey, caCert := newMutualTLSArtifacts(t, "127.0.0.1")
  700. caPool := x509.NewCertPool()
  701. require.True(t, caPool.AppendCertsFromPEM(caCert))
  702. tlsCert, err := tls.X509KeyPair(serverCert, serverKey)
  703. require.NoError(t, err)
  704. lis, err := net.Listen("tcp", "127.0.0.1:0")
  705. require.NoError(t, err)
  706. recorder := &recordingProviderServer{}
  707. grpcServer := grpc.NewServer(grpc.Creds(credentials.NewTLS(&tls.Config{
  708. MinVersion: tls.VersionTLS12,
  709. Certificates: []tls.Certificate{tlsCert},
  710. ClientCAs: caPool,
  711. ClientAuth: tls.RequireAndVerifyClientCert,
  712. })))
  713. pb.RegisterSecretStoreProviderServer(grpcServer, recorder)
  714. go func() {
  715. _ = grpcServer.Serve(lis)
  716. }()
  717. t.Cleanup(func() {
  718. grpcServer.Stop()
  719. _ = lis.Close()
  720. })
  721. return recorder, lis.Addr().String(), map[string][]byte{
  722. "ca.crt": caCert,
  723. "client.crt": clientCert,
  724. "client.key": clientKey,
  725. }
  726. }
  727. func (s *recordingProviderServer) Validate(_ context.Context, req *pb.ValidateRequest) (*pb.ValidateResponse, error) {
  728. s.mu.Lock()
  729. defer s.mu.Unlock()
  730. s.validateRequests = append(s.validateRequests, req)
  731. return &pb.ValidateResponse{Valid: true}, nil
  732. }
  733. func (s *recordingProviderServer) LastValidateRequest() *pb.ValidateRequest {
  734. s.mu.Lock()
  735. defer s.mu.Unlock()
  736. if len(s.validateRequests) == 0 {
  737. return nil
  738. }
  739. return s.validateRequests[len(s.validateRequests)-1]
  740. }
  741. func (s *recordingProviderServer) ValidateCallCount() int {
  742. s.mu.Lock()
  743. defer s.mu.Unlock()
  744. return len(s.validateRequests)
  745. }
  746. func (s *recordingProviderServer) ValidateSourceNamespaces() []string {
  747. s.mu.Lock()
  748. defer s.mu.Unlock()
  749. out := make([]string, 0, len(s.validateRequests))
  750. for _, req := range s.validateRequests {
  751. out = append(out, req.GetSourceNamespace())
  752. }
  753. return out
  754. }
  755. func newMutualTLSArtifacts(t *testing.T, host string) (serverCertPEM, serverKeyPEM, clientCertPEM, clientKeyPEM, caCertPEM []byte) {
  756. t.Helper()
  757. caKey, err := rsa.GenerateKey(rand.Reader, 2048)
  758. require.NoError(t, err)
  759. caTemplate := &x509.Certificate{
  760. SerialNumber: big.NewInt(1),
  761. Subject: pkix.Name{
  762. CommonName: "clientmanager-test-ca",
  763. },
  764. NotBefore: time.Now().Add(-time.Hour),
  765. NotAfter: time.Now().Add(24 * time.Hour),
  766. KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
  767. BasicConstraintsValid: true,
  768. IsCA: true,
  769. }
  770. caDER, err := x509.CreateCertificate(rand.Reader, caTemplate, caTemplate, &caKey.PublicKey, caKey)
  771. require.NoError(t, err)
  772. caCert, err := x509.ParseCertificate(caDER)
  773. require.NoError(t, err)
  774. serverCertPEM, serverKeyPEM = newSignedCertificateForTest(t, caCert, caKey, 2, host, []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth})
  775. clientCertPEM, clientKeyPEM = newSignedCertificateForTest(t, caCert, caKey, 3, "client", []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth})
  776. caCertPEM = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: caDER})
  777. return serverCertPEM, serverKeyPEM, clientCertPEM, clientKeyPEM, caCertPEM
  778. }
  779. func newSignedCertificateForTest(t *testing.T, caCert *x509.Certificate, caKey *rsa.PrivateKey, serial int64, host string, usages []x509.ExtKeyUsage) ([]byte, []byte) {
  780. t.Helper()
  781. key, err := rsa.GenerateKey(rand.Reader, 2048)
  782. require.NoError(t, err)
  783. template := &x509.Certificate{
  784. SerialNumber: big.NewInt(serial),
  785. Subject: pkix.Name{
  786. CommonName: host,
  787. },
  788. NotBefore: time.Now().Add(-time.Hour),
  789. NotAfter: time.Now().Add(24 * time.Hour),
  790. KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
  791. ExtKeyUsage: usages,
  792. }
  793. if ip := net.ParseIP(host); ip != nil {
  794. template.IPAddresses = []net.IP{ip}
  795. } else {
  796. template.DNSNames = []string{host}
  797. }
  798. der, err := x509.CreateCertificate(rand.Reader, template, caCert, &key.PublicKey, caKey)
  799. require.NoError(t, err)
  800. certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der})
  801. keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})
  802. return certPEM, keyPEM
  803. }