pushsecret_controller_v2_test.go 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403
  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 pushsecret
  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. "testing"
  25. "time"
  26. "github.com/go-logr/logr"
  27. "google.golang.org/grpc"
  28. "google.golang.org/grpc/credentials"
  29. corev1 "k8s.io/api/core/v1"
  30. apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
  31. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  32. "k8s.io/apimachinery/pkg/runtime"
  33. utilruntime "k8s.io/apimachinery/pkg/util/runtime"
  34. clientgoscheme "k8s.io/client-go/kubernetes/scheme"
  35. fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
  36. esv1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1"
  37. esapi "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
  38. pb "github.com/external-secrets/external-secrets/proto/provider"
  39. "github.com/external-secrets/external-secrets/runtime/clientmanager"
  40. )
  41. type pushsecretRecordingProviderServer struct {
  42. pb.UnimplementedSecretStoreProviderServer
  43. pushRequest *pb.PushSecretRequest
  44. deleteRequest *pb.DeleteSecretRequest
  45. }
  46. const (
  47. pushSecretManifestNamespace = "tenant-a"
  48. pushSecretRemoteKey = "remote/path"
  49. pushSecretProperty = "property"
  50. pushSecretSecretKey = "token"
  51. )
  52. func (s *pushsecretRecordingProviderServer) PushSecret(_ context.Context, req *pb.PushSecretRequest) (*pb.PushSecretResponse, error) {
  53. s.pushRequest = req
  54. return &pb.PushSecretResponse{}, nil
  55. }
  56. func (s *pushsecretRecordingProviderServer) DeleteSecret(_ context.Context, req *pb.DeleteSecretRequest) (*pb.DeleteSecretResponse, error) {
  57. s.deleteRequest = req
  58. return &pb.DeleteSecretResponse{}, nil
  59. }
  60. func (s *pushsecretRecordingProviderServer) SecretExists(_ context.Context, _ *pb.SecretExistsRequest) (*pb.SecretExistsResponse, error) {
  61. return &pb.SecretExistsResponse{Exists: false}, nil
  62. }
  63. func TestResolvedStoreInfoSupportsProviderKinds(t *testing.T) {
  64. providerInfo, ok := resolvedStoreInfo(esapi.PushSecretStoreRef{
  65. Name: "provider",
  66. Kind: esv1.ProviderKindStr,
  67. }, &esv1.Provider{
  68. ObjectMeta: metav1.ObjectMeta{
  69. Name: "provider",
  70. Labels: map[string]string{"team": "a"},
  71. },
  72. })
  73. if !ok {
  74. t.Fatal("expected provider store info to resolve")
  75. }
  76. if providerInfo.Name != "provider" || providerInfo.Kind != esv1.ProviderKindStr || providerInfo.Labels["team"] != "a" {
  77. t.Fatalf("unexpected provider info: %#v", providerInfo)
  78. }
  79. clusterProviderInfo, ok := resolvedStoreInfo(esapi.PushSecretStoreRef{
  80. Name: "cluster-provider",
  81. Kind: esv1.ClusterProviderKindStr,
  82. }, &esv1.ClusterProvider{
  83. ObjectMeta: metav1.ObjectMeta{
  84. Name: "cluster-provider",
  85. Labels: map[string]string{"scope": "cluster"},
  86. },
  87. })
  88. if !ok {
  89. t.Fatal("expected cluster provider store info to resolve")
  90. }
  91. if clusterProviderInfo.Name != "cluster-provider" || clusterProviderInfo.Kind != esv1.ClusterProviderKindStr || clusterProviderInfo.Labels["scope"] != "cluster" {
  92. t.Fatalf("unexpected cluster provider info: %#v", clusterProviderInfo)
  93. }
  94. }
  95. func TestResolvedStoreInfoInfersOmittedProviderKinds(t *testing.T) {
  96. providerInfo, ok := resolvedStoreInfo(esapi.PushSecretStoreRef{
  97. Name: "provider",
  98. }, &esv1.Provider{
  99. ObjectMeta: metav1.ObjectMeta{
  100. Name: "provider",
  101. Labels: map[string]string{"team": "a"},
  102. },
  103. })
  104. if !ok {
  105. t.Fatal("expected provider store info to resolve")
  106. }
  107. if providerInfo.Kind != esv1.ProviderKindStr {
  108. t.Fatalf("expected kind %q, got %#v", esv1.ProviderKindStr, providerInfo)
  109. }
  110. clusterProviderInfo, ok := resolvedStoreInfo(esapi.PushSecretStoreRef{
  111. Name: "cluster-provider",
  112. }, &esv1.ClusterProvider{
  113. ObjectMeta: metav1.ObjectMeta{
  114. Name: "cluster-provider",
  115. Labels: map[string]string{"scope": "cluster"},
  116. },
  117. })
  118. if !ok {
  119. t.Fatal("expected cluster provider store info to resolve")
  120. }
  121. if clusterProviderInfo.Kind != esv1.ClusterProviderKindStr {
  122. t.Fatalf("expected kind %q, got %#v", esv1.ClusterProviderKindStr, clusterProviderInfo)
  123. }
  124. }
  125. func TestValidateDataToMatchesResolvedStoresSupportsProviderKinds(t *testing.T) {
  126. err := validateDataToMatchesResolvedStores([]esapi.PushSecretDataTo{
  127. {
  128. StoreRef: &esapi.PushSecretStoreRef{
  129. Kind: esv1.ProviderKindStr,
  130. LabelSelector: &metav1.LabelSelector{
  131. MatchLabels: map[string]string{"team": "a"},
  132. },
  133. },
  134. RemoteKey: "bundle",
  135. },
  136. }, []storeInfo{
  137. {Name: "provider", Kind: esv1.ProviderKindStr, Labels: map[string]string{"team": "a"}},
  138. })
  139. if err != nil {
  140. t.Fatalf("expected provider label selector to match, got %v", err)
  141. }
  142. err = validateDataToMatchesResolvedStores([]esapi.PushSecretDataTo{
  143. {
  144. StoreRef: &esapi.PushSecretStoreRef{
  145. Kind: esv1.ClusterProviderKindStr,
  146. LabelSelector: &metav1.LabelSelector{
  147. MatchLabels: map[string]string{"scope": "missing"},
  148. },
  149. },
  150. RemoteKey: "bundle",
  151. },
  152. }, []storeInfo{
  153. {Name: "cluster-provider", Kind: esv1.ClusterProviderKindStr, Labels: map[string]string{"scope": "cluster"}},
  154. })
  155. if err == nil || err.Error() != "dataTo[0]: labelSelector does not match any store in secretStoreRefs" {
  156. t.Fatalf("unexpected error: %v", err)
  157. }
  158. }
  159. func TestPushSecretToProvidersV2UsesProviderPath(t *testing.T) {
  160. previous := clientmanager.V2ProvidersEnabled()
  161. clientmanager.SetV2ProvidersEnabled(true)
  162. t.Cleanup(func() {
  163. clientmanager.SetV2ProvidersEnabled(previous)
  164. })
  165. scheme := newPushSecretTestScheme(t)
  166. server, address, tlsSecret := newPushSecretProviderServer(t)
  167. provider := &esv1.Provider{
  168. ObjectMeta: metav1.ObjectMeta{
  169. Name: "provider",
  170. Namespace: "tenant-a",
  171. Labels: map[string]string{"team": "a"},
  172. },
  173. Spec: esv1.ProviderSpec{
  174. Config: esv1.ProviderConfig{
  175. Address: address,
  176. ProviderRef: esv1.ProviderReference{
  177. APIVersion: "provider.external-secrets.io/v2alpha1",
  178. Kind: "Kubernetes",
  179. Name: "backend",
  180. },
  181. },
  182. },
  183. }
  184. kubeClient := fakeclient.NewClientBuilder().
  185. WithScheme(scheme).
  186. WithObjects(provider, &corev1.Secret{
  187. ObjectMeta: metav1.ObjectMeta{
  188. Name: "external-secrets-provider-tls",
  189. Namespace: "tenant-a",
  190. },
  191. Data: tlsSecret,
  192. }).
  193. Build()
  194. r := &Reconciler{Client: kubeClient, Log: logr.Discard()}
  195. mgr := clientmanager.NewManager(kubeClient, "", false)
  196. defer func() {
  197. _ = mgr.Close(context.Background())
  198. }()
  199. ps := esapi.PushSecret{
  200. ObjectMeta: metav1.ObjectMeta{
  201. Name: "pushsecret",
  202. Namespace: "tenant-a",
  203. },
  204. Spec: esapi.PushSecretSpec{
  205. SecretStoreRefs: []esapi.PushSecretStoreRef{{
  206. Name: "provider",
  207. Kind: esv1.ProviderKindStr,
  208. }},
  209. Data: []esapi.PushSecretData{{
  210. Match: esapi.PushSecretMatch{
  211. SecretKey: pushSecretSecretKey,
  212. RemoteRef: esapi.PushSecretRemoteRef{
  213. RemoteKey: pushSecretRemoteKey,
  214. Property: pushSecretProperty,
  215. },
  216. },
  217. Metadata: &apiextensionsv1.JSON{Raw: []byte(`{"owner":"eso"}`)},
  218. }},
  219. },
  220. }
  221. secret := &corev1.Secret{
  222. Data: map[string][]byte{pushSecretSecretKey: []byte("value")},
  223. }
  224. synced, err := r.PushSecretToProvidersV2(context.Background(), map[esapi.PushSecretStoreRef]any{
  225. {Name: "provider", Kind: esv1.ProviderKindStr}: provider,
  226. }, ps, secret, mgr)
  227. if err != nil {
  228. t.Fatalf("PushSecretToProvidersV2() error = %v", err)
  229. }
  230. if server.pushRequest == nil {
  231. t.Fatal("expected push request to be recorded")
  232. }
  233. if server.pushRequest.SourceNamespace != pushSecretManifestNamespace {
  234. t.Fatalf("unexpected source namespace: %q", server.pushRequest.SourceNamespace)
  235. }
  236. if server.pushRequest.ProviderRef == nil || server.pushRequest.ProviderRef.Name != "backend" {
  237. t.Fatalf("unexpected provider ref: %#v", server.pushRequest.ProviderRef)
  238. }
  239. if string(server.pushRequest.SecretData[pushSecretSecretKey]) != "value" {
  240. t.Fatalf("unexpected secret data: %#v", server.pushRequest.SecretData)
  241. }
  242. if server.pushRequest.PushSecretData == nil || server.pushRequest.PushSecretData.RemoteKey != pushSecretRemoteKey || server.pushRequest.PushSecretData.Property != pushSecretProperty {
  243. t.Fatalf("unexpected push payload: %#v", server.pushRequest.PushSecretData)
  244. }
  245. if string(server.pushRequest.PushSecretData.Metadata) != `{"owner":"eso"}` {
  246. t.Fatalf("unexpected metadata: %q", string(server.pushRequest.PushSecretData.Metadata))
  247. }
  248. if synced["Provider/provider"]["remote/path/property"].Match.SecretKey != pushSecretSecretKey {
  249. t.Fatalf("unexpected synced map: %#v", synced)
  250. }
  251. }
  252. func TestPushSecretToProvidersV2UsesProviderPathWhenKindOmitted(t *testing.T) {
  253. previous := clientmanager.V2ProvidersEnabled()
  254. clientmanager.SetV2ProvidersEnabled(true)
  255. t.Cleanup(func() {
  256. clientmanager.SetV2ProvidersEnabled(previous)
  257. })
  258. scheme := newPushSecretTestScheme(t)
  259. server, address, tlsSecret := newPushSecretProviderServer(t)
  260. provider := &esv1.Provider{
  261. ObjectMeta: metav1.ObjectMeta{
  262. Name: "provider",
  263. Namespace: pushSecretManifestNamespace,
  264. Labels: map[string]string{"team": "a"},
  265. },
  266. Spec: esv1.ProviderSpec{
  267. Config: esv1.ProviderConfig{
  268. Address: address,
  269. ProviderRef: esv1.ProviderReference{
  270. APIVersion: "provider.external-secrets.io/v2alpha1",
  271. Kind: "Kubernetes",
  272. Name: "backend",
  273. },
  274. },
  275. },
  276. }
  277. kubeClient := fakeclient.NewClientBuilder().
  278. WithScheme(scheme).
  279. WithObjects(provider, &corev1.Secret{
  280. ObjectMeta: metav1.ObjectMeta{
  281. Name: "external-secrets-provider-tls",
  282. Namespace: pushSecretManifestNamespace,
  283. },
  284. Data: tlsSecret,
  285. }).
  286. Build()
  287. r := &Reconciler{Client: kubeClient, Log: logr.Discard()}
  288. mgr := clientmanager.NewManager(kubeClient, "", false)
  289. defer func() {
  290. _ = mgr.Close(context.Background())
  291. }()
  292. ps := esapi.PushSecret{
  293. ObjectMeta: metav1.ObjectMeta{
  294. Name: "pushsecret",
  295. Namespace: "tenant-a",
  296. },
  297. Spec: esapi.PushSecretSpec{
  298. SecretStoreRefs: []esapi.PushSecretStoreRef{{
  299. Name: "provider",
  300. }},
  301. Data: []esapi.PushSecretData{{
  302. Match: esapi.PushSecretMatch{
  303. SecretKey: "token",
  304. RemoteRef: esapi.PushSecretRemoteRef{
  305. RemoteKey: "remote/path",
  306. Property: "property",
  307. },
  308. },
  309. Metadata: &apiextensionsv1.JSON{Raw: []byte(`{"owner":"eso"}`)},
  310. }},
  311. },
  312. }
  313. secret := &corev1.Secret{
  314. Data: map[string][]byte{"token": []byte("value")},
  315. }
  316. synced, err := r.PushSecretToProvidersV2(context.Background(), map[esapi.PushSecretStoreRef]any{
  317. {Name: "provider"}: provider,
  318. }, ps, secret, mgr)
  319. if err != nil {
  320. t.Fatalf("PushSecretToProvidersV2() error = %v", err)
  321. }
  322. if server.pushRequest == nil {
  323. t.Fatal("expected push request to be recorded")
  324. }
  325. if synced["Provider/provider"]["remote/path/property"].Match.SecretKey != "token" {
  326. t.Fatalf("unexpected synced map: %#v", synced)
  327. }
  328. }
  329. func TestPushSecretToProvidersV2UsesProviderNamespaceAuthScope(t *testing.T) {
  330. previous := clientmanager.V2ProvidersEnabled()
  331. clientmanager.SetV2ProvidersEnabled(true)
  332. t.Cleanup(func() {
  333. clientmanager.SetV2ProvidersEnabled(previous)
  334. })
  335. scheme := newPushSecretTestScheme(t)
  336. server, address, tlsSecret := newPushSecretProviderServer(t)
  337. const manifestNamespace = "tenant-a"
  338. const providerNamespace = "provider-config-ns"
  339. clusterProvider := &esv1.ClusterProvider{
  340. ObjectMeta: metav1.ObjectMeta{
  341. Name: "cluster-provider",
  342. Labels: map[string]string{"scope": "cluster"},
  343. },
  344. Spec: esv1.ClusterProviderSpec{
  345. Config: esv1.ProviderConfig{
  346. Address: address,
  347. ProviderRef: esv1.ProviderReference{
  348. APIVersion: "provider.external-secrets.io/v2alpha1",
  349. Kind: "Kubernetes",
  350. Name: "backend",
  351. Namespace: providerNamespace,
  352. },
  353. },
  354. AuthenticationScope: esv1.AuthenticationScopeProviderNamespace,
  355. Conditions: []esv1.ClusterSecretStoreCondition{{
  356. Namespaces: []string{manifestNamespace},
  357. }},
  358. },
  359. }
  360. kubeClient := fakeclient.NewClientBuilder().
  361. WithScheme(scheme).
  362. WithObjects(
  363. clusterProvider,
  364. &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{
  365. Name: manifestNamespace,
  366. Labels: map[string]string{
  367. "kubernetes.io/metadata.name": manifestNamespace,
  368. },
  369. }},
  370. &corev1.Secret{
  371. ObjectMeta: metav1.ObjectMeta{
  372. Name: "external-secrets-provider-tls",
  373. Namespace: providerNamespace,
  374. },
  375. Data: tlsSecret,
  376. },
  377. ).
  378. Build()
  379. r := &Reconciler{Client: kubeClient, Log: logr.Discard()}
  380. mgr := clientmanager.NewManager(kubeClient, "", false)
  381. defer func() {
  382. _ = mgr.Close(context.Background())
  383. }()
  384. ps := esapi.PushSecret{
  385. ObjectMeta: metav1.ObjectMeta{
  386. Name: "pushsecret",
  387. Namespace: manifestNamespace,
  388. },
  389. Spec: esapi.PushSecretSpec{
  390. SecretStoreRefs: []esapi.PushSecretStoreRef{{
  391. Name: "cluster-provider",
  392. Kind: esv1.ClusterProviderKindStr,
  393. }},
  394. Data: []esapi.PushSecretData{{
  395. Match: esapi.PushSecretMatch{
  396. SecretKey: "token",
  397. RemoteRef: esapi.PushSecretRemoteRef{
  398. RemoteKey: "remote/path",
  399. Property: "property",
  400. },
  401. },
  402. Metadata: &apiextensionsv1.JSON{Raw: []byte(`{"owner":"eso"}`)},
  403. }},
  404. },
  405. }
  406. secret := &corev1.Secret{
  407. Data: map[string][]byte{"token": []byte("value")},
  408. }
  409. synced, err := r.PushSecretToProvidersV2(context.Background(), map[esapi.PushSecretStoreRef]any{
  410. {Name: "cluster-provider", Kind: esv1.ClusterProviderKindStr}: clusterProvider,
  411. }, ps, secret, mgr)
  412. if err != nil {
  413. t.Fatalf("PushSecretToProvidersV2() error = %v", err)
  414. }
  415. if server.pushRequest == nil {
  416. t.Fatal("expected push request to be recorded")
  417. }
  418. if server.pushRequest.SourceNamespace != providerNamespace {
  419. t.Fatalf("unexpected source namespace: %q", server.pushRequest.SourceNamespace)
  420. }
  421. if server.pushRequest.ProviderRef == nil || server.pushRequest.ProviderRef.Name != "backend" || server.pushRequest.ProviderRef.Namespace != providerNamespace {
  422. t.Fatalf("unexpected provider ref: %#v", server.pushRequest.ProviderRef)
  423. }
  424. if synced["ClusterProvider/cluster-provider"]["remote/path/property"].Match.SecretKey != "token" {
  425. t.Fatalf("unexpected synced map: %#v", synced)
  426. }
  427. }
  428. func TestDeleteSecretFromProvidersV2UsesClusterProviderPath(t *testing.T) {
  429. previous := clientmanager.V2ProvidersEnabled()
  430. clientmanager.SetV2ProvidersEnabled(true)
  431. t.Cleanup(func() {
  432. clientmanager.SetV2ProvidersEnabled(previous)
  433. })
  434. scheme := newPushSecretTestScheme(t)
  435. server, address, tlsSecret := newPushSecretProviderServer(t)
  436. clusterProvider := &esv1.ClusterProvider{
  437. ObjectMeta: metav1.ObjectMeta{
  438. Name: "cluster-provider",
  439. Labels: map[string]string{"scope": "cluster"},
  440. },
  441. Spec: esv1.ClusterProviderSpec{
  442. Config: esv1.ProviderConfig{
  443. Address: address,
  444. ProviderRef: esv1.ProviderReference{
  445. APIVersion: "provider.external-secrets.io/v2alpha1",
  446. Kind: "Kubernetes",
  447. Name: "backend",
  448. },
  449. },
  450. AuthenticationScope: esv1.AuthenticationScopeManifestNamespace,
  451. Conditions: []esv1.ClusterSecretStoreCondition{{
  452. Namespaces: []string{"tenant-a"},
  453. }},
  454. },
  455. }
  456. kubeClient := fakeclient.NewClientBuilder().
  457. WithScheme(scheme).
  458. WithObjects(
  459. clusterProvider,
  460. &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{
  461. Name: "tenant-a",
  462. Labels: map[string]string{
  463. "kubernetes.io/metadata.name": "tenant-a",
  464. },
  465. }},
  466. &corev1.Secret{
  467. ObjectMeta: metav1.ObjectMeta{
  468. Name: "external-secrets-provider-tls",
  469. Namespace: "tenant-a",
  470. },
  471. Data: tlsSecret,
  472. },
  473. ).
  474. Build()
  475. r := &Reconciler{Client: kubeClient, Log: logr.Discard()}
  476. ps := &esapi.PushSecret{
  477. ObjectMeta: metav1.ObjectMeta{
  478. Name: "pushsecret",
  479. Namespace: "tenant-a",
  480. },
  481. Status: esapi.PushSecretStatus{
  482. SyncedPushSecrets: esapi.SyncedPushSecretsMap{
  483. "ClusterProvider/cluster-provider": {
  484. "remote/path": {
  485. Match: esapi.PushSecretMatch{
  486. SecretKey: "token",
  487. RemoteRef: esapi.PushSecretRemoteRef{
  488. RemoteKey: "remote/path",
  489. Property: "property",
  490. },
  491. },
  492. },
  493. },
  494. },
  495. },
  496. }
  497. result, err := r.DeleteSecretFromProvidersV2(context.Background(), ps, esapi.SyncedPushSecretsMap{}, map[esapi.PushSecretStoreRef]any{
  498. {Name: "cluster-provider", Kind: esv1.ClusterProviderKindStr}: clusterProvider,
  499. })
  500. if err != nil {
  501. t.Fatalf("DeleteSecretFromProvidersV2() error = %v", err)
  502. }
  503. if server.deleteRequest == nil {
  504. t.Fatal("expected delete request to be recorded")
  505. }
  506. if server.deleteRequest.SourceNamespace != pushSecretManifestNamespace {
  507. t.Fatalf("unexpected source namespace: %q", server.deleteRequest.SourceNamespace)
  508. }
  509. if server.deleteRequest.RemoteRef == nil || server.deleteRequest.RemoteRef.RemoteKey != pushSecretRemoteKey || server.deleteRequest.RemoteRef.Property != pushSecretProperty {
  510. t.Fatalf("unexpected delete ref: %#v", server.deleteRequest.RemoteRef)
  511. }
  512. if _, ok := result["ClusterProvider/cluster-provider"]; ok {
  513. t.Fatalf("expected synced state to be cleaned up, got %#v", result)
  514. }
  515. }
  516. func TestDeleteSecretFromProvidersV2UsesClusterProviderPathWhenKindOmitted(t *testing.T) {
  517. previous := clientmanager.V2ProvidersEnabled()
  518. clientmanager.SetV2ProvidersEnabled(true)
  519. t.Cleanup(func() {
  520. clientmanager.SetV2ProvidersEnabled(previous)
  521. })
  522. scheme := newPushSecretTestScheme(t)
  523. server, address, tlsSecret := newPushSecretProviderServer(t)
  524. clusterProvider := &esv1.ClusterProvider{
  525. ObjectMeta: metav1.ObjectMeta{
  526. Name: "cluster-provider",
  527. Labels: map[string]string{"scope": "cluster"},
  528. },
  529. Spec: esv1.ClusterProviderSpec{
  530. Config: esv1.ProviderConfig{
  531. Address: address,
  532. ProviderRef: esv1.ProviderReference{
  533. APIVersion: "provider.external-secrets.io/v2alpha1",
  534. Kind: "Kubernetes",
  535. Name: "backend",
  536. },
  537. },
  538. AuthenticationScope: esv1.AuthenticationScopeManifestNamespace,
  539. Conditions: []esv1.ClusterSecretStoreCondition{{
  540. Namespaces: []string{"tenant-a"},
  541. }},
  542. },
  543. }
  544. kubeClient := fakeclient.NewClientBuilder().
  545. WithScheme(scheme).
  546. WithObjects(
  547. clusterProvider,
  548. &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{
  549. Name: "tenant-a",
  550. Labels: map[string]string{
  551. "kubernetes.io/metadata.name": "tenant-a",
  552. },
  553. }},
  554. &corev1.Secret{
  555. ObjectMeta: metav1.ObjectMeta{
  556. Name: "external-secrets-provider-tls",
  557. Namespace: "tenant-a",
  558. },
  559. Data: tlsSecret,
  560. },
  561. ).
  562. Build()
  563. r := &Reconciler{Client: kubeClient, Log: logr.Discard()}
  564. ps := &esapi.PushSecret{
  565. ObjectMeta: metav1.ObjectMeta{
  566. Name: "pushsecret",
  567. Namespace: "tenant-a",
  568. },
  569. Status: esapi.PushSecretStatus{
  570. SyncedPushSecrets: esapi.SyncedPushSecretsMap{
  571. "ClusterProvider/cluster-provider": {
  572. "remote/path": {
  573. Match: esapi.PushSecretMatch{
  574. SecretKey: "token",
  575. RemoteRef: esapi.PushSecretRemoteRef{
  576. RemoteKey: "remote/path",
  577. Property: "property",
  578. },
  579. },
  580. },
  581. },
  582. },
  583. },
  584. }
  585. result, err := r.DeleteSecretFromProvidersV2(context.Background(), ps, esapi.SyncedPushSecretsMap{}, map[esapi.PushSecretStoreRef]any{
  586. {Name: "cluster-provider"}: clusterProvider,
  587. })
  588. if err != nil {
  589. t.Fatalf("DeleteSecretFromProvidersV2() error = %v", err)
  590. }
  591. if server.deleteRequest == nil {
  592. t.Fatal("expected delete request to be recorded")
  593. }
  594. if _, ok := result["ClusterProvider/cluster-provider"]; ok {
  595. t.Fatalf("expected synced state to be cleaned up, got %#v", result)
  596. }
  597. }
  598. func TestGetSecretStoresV2ResolvesClusterProviderWhenKindOmitted(t *testing.T) {
  599. previous := clientmanager.V2ProvidersEnabled()
  600. clientmanager.SetV2ProvidersEnabled(true)
  601. t.Cleanup(func() {
  602. clientmanager.SetV2ProvidersEnabled(previous)
  603. })
  604. scheme := newPushSecretTestScheme(t)
  605. clusterProvider := &esv1.ClusterProvider{
  606. ObjectMeta: metav1.ObjectMeta{
  607. Name: "cluster-provider",
  608. },
  609. }
  610. kubeClient := fakeclient.NewClientBuilder().
  611. WithScheme(scheme).
  612. WithObjects(clusterProvider).
  613. Build()
  614. r := &Reconciler{Client: kubeClient, Log: logr.Discard()}
  615. ps := esapi.PushSecret{
  616. ObjectMeta: metav1.ObjectMeta{
  617. Name: "pushsecret",
  618. Namespace: "tenant-a",
  619. },
  620. Spec: esapi.PushSecretSpec{
  621. SecretStoreRefs: []esapi.PushSecretStoreRef{{
  622. Name: "cluster-provider",
  623. }},
  624. },
  625. }
  626. stores, err := r.GetSecretStoresV2(context.Background(), ps)
  627. if err != nil {
  628. t.Fatalf("GetSecretStoresV2() error = %v", err)
  629. }
  630. store, ok := stores[esapi.PushSecretStoreRef{Name: "cluster-provider"}]
  631. if !ok {
  632. t.Fatalf("expected cluster provider store, got %#v", stores)
  633. }
  634. if _, ok := store.(*esv1.ClusterProvider); !ok {
  635. t.Fatalf("expected ClusterProvider, got %T", store)
  636. }
  637. }
  638. func TestGetSecretStoresV2PrefersSecretStoreWhenKindOmitted(t *testing.T) {
  639. previous := clientmanager.V2ProvidersEnabled()
  640. clientmanager.SetV2ProvidersEnabled(true)
  641. t.Cleanup(func() {
  642. clientmanager.SetV2ProvidersEnabled(previous)
  643. })
  644. scheme := newPushSecretTestScheme(t)
  645. secretStore := &esv1.SecretStore{
  646. ObjectMeta: metav1.ObjectMeta{
  647. Name: "shared-name",
  648. Namespace: "tenant-a",
  649. },
  650. }
  651. clusterProvider := &esv1.ClusterProvider{
  652. ObjectMeta: metav1.ObjectMeta{
  653. Name: "shared-name",
  654. },
  655. }
  656. kubeClient := fakeclient.NewClientBuilder().
  657. WithScheme(scheme).
  658. WithObjects(secretStore, clusterProvider).
  659. Build()
  660. r := &Reconciler{Client: kubeClient, Log: logr.Discard()}
  661. ps := esapi.PushSecret{
  662. ObjectMeta: metav1.ObjectMeta{
  663. Name: "pushsecret",
  664. Namespace: "tenant-a",
  665. },
  666. Spec: esapi.PushSecretSpec{
  667. SecretStoreRefs: []esapi.PushSecretStoreRef{{
  668. Name: "shared-name",
  669. }},
  670. },
  671. }
  672. stores, err := r.GetSecretStoresV2(context.Background(), ps)
  673. if err != nil {
  674. t.Fatalf("GetSecretStoresV2() error = %v", err)
  675. }
  676. store, ok := stores[esapi.PushSecretStoreRef{Name: "shared-name"}]
  677. if !ok {
  678. t.Fatalf("expected store to resolve, got %#v", stores)
  679. }
  680. if _, ok := store.(*esv1.SecretStore); !ok {
  681. t.Fatalf("expected SecretStore to take precedence, got %T", store)
  682. }
  683. }
  684. func TestGetSecretStoresV2SupportsSecretStoreLabelSelectors(t *testing.T) {
  685. scheme := newPushSecretTestScheme(t)
  686. selectedStore := &esv1.SecretStore{
  687. ObjectMeta: metav1.ObjectMeta{
  688. Name: "selected",
  689. Namespace: "tenant-a",
  690. Labels: map[string]string{"env": "test"},
  691. },
  692. }
  693. otherNamespaceStore := &esv1.SecretStore{
  694. ObjectMeta: metav1.ObjectMeta{
  695. Name: "other-namespace",
  696. Namespace: "tenant-b",
  697. Labels: map[string]string{"env": "test"},
  698. },
  699. }
  700. kubeClient := fakeclient.NewClientBuilder().
  701. WithScheme(scheme).
  702. WithObjects(selectedStore, otherNamespaceStore).
  703. Build()
  704. r := &Reconciler{Client: kubeClient, Log: logr.Discard()}
  705. ps := esapi.PushSecret{
  706. ObjectMeta: metav1.ObjectMeta{
  707. Name: "pushsecret",
  708. Namespace: "tenant-a",
  709. },
  710. Spec: esapi.PushSecretSpec{
  711. SecretStoreRefs: []esapi.PushSecretStoreRef{{
  712. Kind: esv1.SecretStoreKind,
  713. LabelSelector: &metav1.LabelSelector{
  714. MatchLabels: map[string]string{"env": "test"},
  715. },
  716. }},
  717. },
  718. }
  719. stores, err := r.GetSecretStoresV2(context.Background(), ps)
  720. if err != nil {
  721. t.Fatalf("GetSecretStoresV2() error = %v", err)
  722. }
  723. if len(stores) != 1 {
  724. t.Fatalf("expected one resolved store, got %#v", stores)
  725. }
  726. store, ok := stores[esapi.PushSecretStoreRef{Name: "selected", Kind: esv1.SecretStoreKind}]
  727. if !ok {
  728. t.Fatalf("expected selected store, got %#v", stores)
  729. }
  730. if _, ok := store.(*esv1.SecretStore); !ok {
  731. t.Fatalf("expected SecretStore, got %T", store)
  732. }
  733. }
  734. func TestRemoveUnmanagedStoresSupportsOmittedKindRefs(t *testing.T) {
  735. scheme := newPushSecretTestScheme(t)
  736. secretStore := &esv1.SecretStore{
  737. ObjectMeta: metav1.ObjectMeta{
  738. Name: "shared-name",
  739. Namespace: "tenant-a",
  740. },
  741. Spec: esv1.SecretStoreSpec{
  742. Controller: "eso",
  743. },
  744. }
  745. kubeClient := fakeclient.NewClientBuilder().
  746. WithScheme(scheme).
  747. WithObjects(secretStore).
  748. Build()
  749. r := &Reconciler{
  750. Client: kubeClient,
  751. ControllerClass: "eso",
  752. Log: logr.Discard(),
  753. }
  754. stores, err := removeUnmanagedStores(context.Background(), "tenant-a", r, map[esapi.PushSecretStoreRef]esv1.GenericStore{
  755. {Name: "shared-name"}: secretStore,
  756. })
  757. if err != nil {
  758. t.Fatalf("removeUnmanagedStores() error = %v", err)
  759. }
  760. if _, ok := stores[esapi.PushSecretStoreRef{Name: "shared-name"}]; !ok {
  761. t.Fatalf("expected omitted-kind store ref to be retained, got %#v", stores)
  762. }
  763. }
  764. func TestDeleteSecretFromProvidersV2DeletesRemovedStoreEvenWhenNoLongerReferenced(t *testing.T) {
  765. previous := clientmanager.V2ProvidersEnabled()
  766. clientmanager.SetV2ProvidersEnabled(true)
  767. t.Cleanup(func() {
  768. clientmanager.SetV2ProvidersEnabled(previous)
  769. })
  770. scheme := newPushSecretTestScheme(t)
  771. server, address, tlsSecret := newPushSecretProviderServer(t)
  772. clusterProvider := &esv1.ClusterProvider{
  773. ObjectMeta: metav1.ObjectMeta{
  774. Name: "cluster-provider",
  775. },
  776. Spec: esv1.ClusterProviderSpec{
  777. Config: esv1.ProviderConfig{
  778. Address: address,
  779. ProviderRef: esv1.ProviderReference{
  780. APIVersion: "provider.external-secrets.io/v2alpha1",
  781. Kind: "Kubernetes",
  782. Name: "backend",
  783. },
  784. },
  785. AuthenticationScope: esv1.AuthenticationScopeManifestNamespace,
  786. Conditions: []esv1.ClusterSecretStoreCondition{{
  787. Namespaces: []string{"tenant-a"},
  788. }},
  789. },
  790. }
  791. kubeClient := fakeclient.NewClientBuilder().
  792. WithScheme(scheme).
  793. WithObjects(
  794. clusterProvider,
  795. &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{
  796. Name: "tenant-a",
  797. Labels: map[string]string{
  798. "kubernetes.io/metadata.name": "tenant-a",
  799. },
  800. }},
  801. &corev1.Secret{
  802. ObjectMeta: metav1.ObjectMeta{
  803. Name: "external-secrets-provider-tls",
  804. Namespace: "tenant-a",
  805. },
  806. Data: tlsSecret,
  807. },
  808. ).
  809. Build()
  810. r := &Reconciler{Client: kubeClient, Log: logr.Discard()}
  811. ps := &esapi.PushSecret{
  812. ObjectMeta: metav1.ObjectMeta{
  813. Name: "pushsecret",
  814. Namespace: "tenant-a",
  815. },
  816. Status: esapi.PushSecretStatus{
  817. SyncedPushSecrets: esapi.SyncedPushSecretsMap{
  818. "ClusterProvider/cluster-provider": {
  819. "remote/path": {
  820. Match: esapi.PushSecretMatch{
  821. SecretKey: "token",
  822. RemoteRef: esapi.PushSecretRemoteRef{
  823. RemoteKey: "remote/path",
  824. Property: "property",
  825. },
  826. },
  827. },
  828. },
  829. },
  830. },
  831. }
  832. result, err := r.DeleteSecretFromProvidersV2(context.Background(), ps, esapi.SyncedPushSecretsMap{}, map[esapi.PushSecretStoreRef]any{})
  833. if err != nil {
  834. t.Fatalf("DeleteSecretFromProvidersV2() error = %v", err)
  835. }
  836. if server.deleteRequest == nil {
  837. t.Fatal("expected delete request to be recorded")
  838. }
  839. if server.deleteRequest.RemoteRef == nil || server.deleteRequest.RemoteRef.RemoteKey != "remote/path" {
  840. t.Fatalf("unexpected delete ref: %#v", server.deleteRequest.RemoteRef)
  841. }
  842. if _, ok := result["ClusterProvider/cluster-provider"]; ok {
  843. t.Fatalf("expected synced state to be cleaned up, got %#v", result)
  844. }
  845. }
  846. func TestDeleteSecretFromProvidersV2UsesProviderNamespaceAuthScope(t *testing.T) {
  847. previous := clientmanager.V2ProvidersEnabled()
  848. clientmanager.SetV2ProvidersEnabled(true)
  849. t.Cleanup(func() {
  850. clientmanager.SetV2ProvidersEnabled(previous)
  851. })
  852. scheme := newPushSecretTestScheme(t)
  853. server, address, tlsSecret := newPushSecretProviderServer(t)
  854. const manifestNamespace = "tenant-a"
  855. const providerNamespace = "provider-config-ns"
  856. clusterProvider := &esv1.ClusterProvider{
  857. ObjectMeta: metav1.ObjectMeta{
  858. Name: "cluster-provider",
  859. Labels: map[string]string{"scope": "cluster"},
  860. },
  861. Spec: esv1.ClusterProviderSpec{
  862. Config: esv1.ProviderConfig{
  863. Address: address,
  864. ProviderRef: esv1.ProviderReference{
  865. APIVersion: "provider.external-secrets.io/v2alpha1",
  866. Kind: "Kubernetes",
  867. Name: "backend",
  868. Namespace: providerNamespace,
  869. },
  870. },
  871. AuthenticationScope: esv1.AuthenticationScopeProviderNamespace,
  872. Conditions: []esv1.ClusterSecretStoreCondition{{
  873. Namespaces: []string{manifestNamespace},
  874. }},
  875. },
  876. }
  877. kubeClient := fakeclient.NewClientBuilder().
  878. WithScheme(scheme).
  879. WithObjects(
  880. clusterProvider,
  881. &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{
  882. Name: manifestNamespace,
  883. Labels: map[string]string{
  884. "kubernetes.io/metadata.name": manifestNamespace,
  885. },
  886. }},
  887. &corev1.Secret{
  888. ObjectMeta: metav1.ObjectMeta{
  889. Name: "external-secrets-provider-tls",
  890. Namespace: providerNamespace,
  891. },
  892. Data: tlsSecret,
  893. },
  894. ).
  895. Build()
  896. r := &Reconciler{Client: kubeClient, Log: logr.Discard()}
  897. ps := &esapi.PushSecret{
  898. ObjectMeta: metav1.ObjectMeta{
  899. Name: "pushsecret",
  900. Namespace: manifestNamespace,
  901. },
  902. Status: esapi.PushSecretStatus{
  903. SyncedPushSecrets: esapi.SyncedPushSecretsMap{
  904. "ClusterProvider/cluster-provider": {
  905. "remote/path": {
  906. Match: esapi.PushSecretMatch{
  907. SecretKey: "token",
  908. RemoteRef: esapi.PushSecretRemoteRef{
  909. RemoteKey: "remote/path",
  910. Property: "property",
  911. },
  912. },
  913. },
  914. },
  915. },
  916. },
  917. }
  918. result, err := r.DeleteSecretFromProvidersV2(context.Background(), ps, esapi.SyncedPushSecretsMap{}, map[esapi.PushSecretStoreRef]any{
  919. {Name: "cluster-provider", Kind: esv1.ClusterProviderKindStr}: clusterProvider,
  920. })
  921. if err != nil {
  922. t.Fatalf("DeleteSecretFromProvidersV2() error = %v", err)
  923. }
  924. if server.deleteRequest == nil {
  925. t.Fatal("expected delete request to be recorded")
  926. }
  927. if server.deleteRequest.SourceNamespace != providerNamespace {
  928. t.Fatalf("unexpected source namespace: %q", server.deleteRequest.SourceNamespace)
  929. }
  930. if server.deleteRequest.ProviderRef == nil || server.deleteRequest.ProviderRef.Namespace != providerNamespace {
  931. t.Fatalf("unexpected provider ref: %#v", server.deleteRequest.ProviderRef)
  932. }
  933. if _, ok := result["ClusterProvider/cluster-provider"]; ok {
  934. t.Fatalf("expected synced state to be cleaned up, got %#v", result)
  935. }
  936. }
  937. func TestDeleteSecretFromProvidersV2DeletesOnlyRemovedEntriesForManifestScope(t *testing.T) {
  938. previous := clientmanager.V2ProvidersEnabled()
  939. clientmanager.SetV2ProvidersEnabled(true)
  940. t.Cleanup(func() {
  941. clientmanager.SetV2ProvidersEnabled(previous)
  942. })
  943. scheme := newPushSecretTestScheme(t)
  944. server, address, tlsSecret := newPushSecretProviderServer(t)
  945. clusterProvider := &esv1.ClusterProvider{
  946. ObjectMeta: metav1.ObjectMeta{
  947. Name: "cluster-provider",
  948. },
  949. Spec: esv1.ClusterProviderSpec{
  950. Config: esv1.ProviderConfig{
  951. Address: address,
  952. ProviderRef: esv1.ProviderReference{
  953. APIVersion: "provider.external-secrets.io/v2alpha1",
  954. Kind: "Kubernetes",
  955. Name: "backend",
  956. },
  957. },
  958. AuthenticationScope: esv1.AuthenticationScopeManifestNamespace,
  959. Conditions: []esv1.ClusterSecretStoreCondition{{
  960. Namespaces: []string{"tenant-a"},
  961. }},
  962. },
  963. }
  964. kubeClient := fakeclient.NewClientBuilder().
  965. WithScheme(scheme).
  966. WithObjects(
  967. clusterProvider,
  968. &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{
  969. Name: "tenant-a",
  970. Labels: map[string]string{
  971. "kubernetes.io/metadata.name": "tenant-a",
  972. },
  973. }},
  974. &corev1.Secret{
  975. ObjectMeta: metav1.ObjectMeta{
  976. Name: "external-secrets-provider-tls",
  977. Namespace: "tenant-a",
  978. },
  979. Data: tlsSecret,
  980. },
  981. ).
  982. Build()
  983. r := &Reconciler{Client: kubeClient, Log: logr.Discard()}
  984. ps := &esapi.PushSecret{
  985. ObjectMeta: metav1.ObjectMeta{
  986. Name: "pushsecret",
  987. Namespace: "tenant-a",
  988. },
  989. Status: esapi.PushSecretStatus{
  990. SyncedPushSecrets: esapi.SyncedPushSecretsMap{
  991. "ClusterProvider/cluster-provider": {
  992. "remote/keep/property": {
  993. Match: esapi.PushSecretMatch{
  994. SecretKey: "keep",
  995. RemoteRef: esapi.PushSecretRemoteRef{
  996. RemoteKey: "remote/keep",
  997. Property: "property",
  998. },
  999. },
  1000. },
  1001. "remote/delete/property": {
  1002. Match: esapi.PushSecretMatch{
  1003. SecretKey: "delete",
  1004. RemoteRef: esapi.PushSecretRemoteRef{
  1005. RemoteKey: "remote/delete",
  1006. Property: "property",
  1007. },
  1008. },
  1009. },
  1010. },
  1011. },
  1012. },
  1013. }
  1014. newMap := esapi.SyncedPushSecretsMap{
  1015. "ClusterProvider/cluster-provider": {
  1016. "remote/keep/property": ps.Status.SyncedPushSecrets["ClusterProvider/cluster-provider"]["remote/keep/property"],
  1017. },
  1018. }
  1019. result, err := r.DeleteSecretFromProvidersV2(context.Background(), ps, newMap, map[esapi.PushSecretStoreRef]any{
  1020. {Name: "cluster-provider", Kind: esv1.ClusterProviderKindStr}: clusterProvider,
  1021. })
  1022. if err != nil {
  1023. t.Fatalf("DeleteSecretFromProvidersV2() error = %v", err)
  1024. }
  1025. if server.deleteRequest == nil {
  1026. t.Fatal("expected delete request to be recorded")
  1027. }
  1028. if server.deleteRequest.SourceNamespace != "tenant-a" {
  1029. t.Fatalf("unexpected source namespace: %q", server.deleteRequest.SourceNamespace)
  1030. }
  1031. if server.deleteRequest.RemoteRef == nil || server.deleteRequest.RemoteRef.RemoteKey != "remote/delete" || server.deleteRequest.RemoteRef.Property != "property" {
  1032. t.Fatalf("unexpected delete ref: %#v", server.deleteRequest.RemoteRef)
  1033. }
  1034. storeState, ok := result["ClusterProvider/cluster-provider"]
  1035. if !ok {
  1036. t.Fatalf("expected synced state for cluster provider, got %#v", result)
  1037. }
  1038. if len(storeState) != 1 {
  1039. t.Fatalf("expected one remaining synced entry, got %#v", storeState)
  1040. }
  1041. if _, ok := storeState["remote/keep/property"]; !ok {
  1042. t.Fatalf("expected keep entry to remain, got %#v", storeState)
  1043. }
  1044. if _, ok := storeState["remote/delete/property"]; ok {
  1045. t.Fatalf("expected delete entry to be removed, got %#v", storeState)
  1046. }
  1047. }
  1048. func TestDeleteSecretFromProvidersV2DeletesOnlyRemovedEntriesForProviderNamespaceScope(t *testing.T) {
  1049. previous := clientmanager.V2ProvidersEnabled()
  1050. clientmanager.SetV2ProvidersEnabled(true)
  1051. t.Cleanup(func() {
  1052. clientmanager.SetV2ProvidersEnabled(previous)
  1053. })
  1054. scheme := newPushSecretTestScheme(t)
  1055. server, address, tlsSecret := newPushSecretProviderServer(t)
  1056. const manifestNamespace = "tenant-a"
  1057. const providerNamespace = "provider-config-ns"
  1058. clusterProvider := &esv1.ClusterProvider{
  1059. ObjectMeta: metav1.ObjectMeta{
  1060. Name: "cluster-provider",
  1061. },
  1062. Spec: esv1.ClusterProviderSpec{
  1063. Config: esv1.ProviderConfig{
  1064. Address: address,
  1065. ProviderRef: esv1.ProviderReference{
  1066. APIVersion: "provider.external-secrets.io/v2alpha1",
  1067. Kind: "Kubernetes",
  1068. Name: "backend",
  1069. Namespace: providerNamespace,
  1070. },
  1071. },
  1072. AuthenticationScope: esv1.AuthenticationScopeProviderNamespace,
  1073. Conditions: []esv1.ClusterSecretStoreCondition{{
  1074. Namespaces: []string{manifestNamespace},
  1075. }},
  1076. },
  1077. }
  1078. kubeClient := fakeclient.NewClientBuilder().
  1079. WithScheme(scheme).
  1080. WithObjects(
  1081. clusterProvider,
  1082. &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{
  1083. Name: manifestNamespace,
  1084. Labels: map[string]string{
  1085. "kubernetes.io/metadata.name": manifestNamespace,
  1086. },
  1087. }},
  1088. &corev1.Secret{
  1089. ObjectMeta: metav1.ObjectMeta{
  1090. Name: "external-secrets-provider-tls",
  1091. Namespace: providerNamespace,
  1092. },
  1093. Data: tlsSecret,
  1094. },
  1095. ).
  1096. Build()
  1097. r := &Reconciler{Client: kubeClient, Log: logr.Discard()}
  1098. ps := &esapi.PushSecret{
  1099. ObjectMeta: metav1.ObjectMeta{
  1100. Name: "pushsecret",
  1101. Namespace: manifestNamespace,
  1102. },
  1103. Status: esapi.PushSecretStatus{
  1104. SyncedPushSecrets: esapi.SyncedPushSecretsMap{
  1105. "ClusterProvider/cluster-provider": {
  1106. "remote/keep/property": {
  1107. Match: esapi.PushSecretMatch{
  1108. SecretKey: "keep",
  1109. RemoteRef: esapi.PushSecretRemoteRef{
  1110. RemoteKey: "remote/keep",
  1111. Property: "property",
  1112. },
  1113. },
  1114. },
  1115. "remote/delete/property": {
  1116. Match: esapi.PushSecretMatch{
  1117. SecretKey: "delete",
  1118. RemoteRef: esapi.PushSecretRemoteRef{
  1119. RemoteKey: "remote/delete",
  1120. Property: "property",
  1121. },
  1122. },
  1123. },
  1124. },
  1125. },
  1126. },
  1127. }
  1128. newMap := esapi.SyncedPushSecretsMap{
  1129. "ClusterProvider/cluster-provider": {
  1130. "remote/keep/property": ps.Status.SyncedPushSecrets["ClusterProvider/cluster-provider"]["remote/keep/property"],
  1131. },
  1132. }
  1133. result, err := r.DeleteSecretFromProvidersV2(context.Background(), ps, newMap, map[esapi.PushSecretStoreRef]any{
  1134. {Name: "cluster-provider", Kind: esv1.ClusterProviderKindStr}: clusterProvider,
  1135. })
  1136. if err != nil {
  1137. t.Fatalf("DeleteSecretFromProvidersV2() error = %v", err)
  1138. }
  1139. if server.deleteRequest == nil {
  1140. t.Fatal("expected delete request to be recorded")
  1141. }
  1142. if server.deleteRequest.SourceNamespace != providerNamespace {
  1143. t.Fatalf("unexpected source namespace: %q", server.deleteRequest.SourceNamespace)
  1144. }
  1145. if server.deleteRequest.ProviderRef == nil || server.deleteRequest.ProviderRef.Namespace != providerNamespace {
  1146. t.Fatalf("unexpected provider ref: %#v", server.deleteRequest.ProviderRef)
  1147. }
  1148. if server.deleteRequest.RemoteRef == nil || server.deleteRequest.RemoteRef.RemoteKey != "remote/delete" || server.deleteRequest.RemoteRef.Property != "property" {
  1149. t.Fatalf("unexpected delete ref: %#v", server.deleteRequest.RemoteRef)
  1150. }
  1151. storeState, ok := result["ClusterProvider/cluster-provider"]
  1152. if !ok {
  1153. t.Fatalf("expected synced state for cluster provider, got %#v", result)
  1154. }
  1155. if len(storeState) != 1 {
  1156. t.Fatalf("expected one remaining synced entry, got %#v", storeState)
  1157. }
  1158. if _, ok := storeState["remote/keep/property"]; !ok {
  1159. t.Fatalf("expected keep entry to remain, got %#v", storeState)
  1160. }
  1161. if _, ok := storeState["remote/delete/property"]; ok {
  1162. t.Fatalf("expected delete entry to be removed, got %#v", storeState)
  1163. }
  1164. }
  1165. func newPushSecretTestScheme(t *testing.T) *runtime.Scheme {
  1166. t.Helper()
  1167. scheme := runtime.NewScheme()
  1168. utilruntime.Must(clientgoscheme.AddToScheme(scheme))
  1169. utilruntime.Must(esv1.AddToScheme(scheme))
  1170. utilruntime.Must(esapi.AddToScheme(scheme))
  1171. return scheme
  1172. }
  1173. func newPushSecretProviderServer(t *testing.T) (*pushsecretRecordingProviderServer, string, map[string][]byte) {
  1174. t.Helper()
  1175. serverCert, serverKey, clientCert, clientKey, caCert := newPushSecretTLSArtifacts(t, "127.0.0.1")
  1176. caPool := x509.NewCertPool()
  1177. if !caPool.AppendCertsFromPEM(caCert) {
  1178. t.Fatal("failed to append CA cert")
  1179. }
  1180. tlsCert, err := tls.X509KeyPair(serverCert, serverKey)
  1181. if err != nil {
  1182. t.Fatalf("X509KeyPair() error = %v", err)
  1183. }
  1184. lis, err := net.Listen("tcp", "127.0.0.1:0")
  1185. if err != nil {
  1186. t.Fatalf("Listen() error = %v", err)
  1187. }
  1188. server := &pushsecretRecordingProviderServer{}
  1189. grpcServer := grpc.NewServer(grpc.Creds(credentials.NewTLS(&tls.Config{
  1190. MinVersion: tls.VersionTLS12,
  1191. Certificates: []tls.Certificate{tlsCert},
  1192. ClientCAs: caPool,
  1193. ClientAuth: tls.RequireAndVerifyClientCert,
  1194. })))
  1195. pb.RegisterSecretStoreProviderServer(grpcServer, server)
  1196. go func() {
  1197. _ = grpcServer.Serve(lis)
  1198. }()
  1199. t.Cleanup(func() {
  1200. grpcServer.Stop()
  1201. _ = lis.Close()
  1202. })
  1203. return server, lis.Addr().String(), map[string][]byte{
  1204. "ca.crt": caCert,
  1205. "client.crt": clientCert,
  1206. "client.key": clientKey,
  1207. }
  1208. }
  1209. func newPushSecretTLSArtifacts(t *testing.T, host string) (serverCertPEM, serverKeyPEM, clientCertPEM, clientKeyPEM, caCertPEM []byte) {
  1210. t.Helper()
  1211. caKey, err := rsa.GenerateKey(rand.Reader, 2048)
  1212. if err != nil {
  1213. t.Fatalf("GenerateKey() error = %v", err)
  1214. }
  1215. caTemplate := &x509.Certificate{
  1216. SerialNumber: big.NewInt(1),
  1217. Subject: pkix.Name{
  1218. CommonName: "pushsecret-test-ca",
  1219. },
  1220. NotBefore: time.Now().Add(-time.Hour),
  1221. NotAfter: time.Now().Add(24 * time.Hour),
  1222. KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
  1223. BasicConstraintsValid: true,
  1224. IsCA: true,
  1225. }
  1226. caDER, err := x509.CreateCertificate(rand.Reader, caTemplate, caTemplate, &caKey.PublicKey, caKey)
  1227. if err != nil {
  1228. t.Fatalf("CreateCertificate() error = %v", err)
  1229. }
  1230. caCert, err := x509.ParseCertificate(caDER)
  1231. if err != nil {
  1232. t.Fatalf("ParseCertificate() error = %v", err)
  1233. }
  1234. serverCertPEM, serverKeyPEM = newPushSecretSignedTLSCert(t, caCert, caKey, 2, host, []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth})
  1235. clientCertPEM, clientKeyPEM = newPushSecretSignedTLSCert(t, caCert, caKey, 3, host, []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth})
  1236. caCertPEM = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: caDER})
  1237. return serverCertPEM, serverKeyPEM, clientCertPEM, clientKeyPEM, caCertPEM
  1238. }
  1239. func newPushSecretSignedTLSCert(t *testing.T, caCert *x509.Certificate, caKey *rsa.PrivateKey, serial int64, host string, usages []x509.ExtKeyUsage) ([]byte, []byte) {
  1240. t.Helper()
  1241. key, err := rsa.GenerateKey(rand.Reader, 2048)
  1242. if err != nil {
  1243. t.Fatalf("GenerateKey() error = %v", err)
  1244. }
  1245. template := &x509.Certificate{
  1246. SerialNumber: big.NewInt(serial),
  1247. Subject: pkix.Name{
  1248. CommonName: host,
  1249. },
  1250. NotBefore: time.Now().Add(-time.Hour),
  1251. NotAfter: time.Now().Add(24 * time.Hour),
  1252. KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
  1253. ExtKeyUsage: usages,
  1254. }
  1255. if ip := net.ParseIP(host); ip != nil {
  1256. template.IPAddresses = []net.IP{ip}
  1257. } else {
  1258. template.DNSNames = []string{host}
  1259. }
  1260. der, err := x509.CreateCertificate(rand.Reader, template, caCert, &key.PublicKey, caKey)
  1261. if err != nil {
  1262. t.Fatalf("CreateCertificate() error = %v", err)
  1263. }
  1264. return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der}),
  1265. pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})
  1266. }