manager_test.go 28 KB

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