pushsecret_controller_v2_test.go 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400
  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 := removeUnmanagedStores(context.Background(), "tenant-a", r, map[esapi.PushSecretStoreRef]esv1.GenericStore{
  755. {Name: "shared-name"}: secretStore,
  756. })
  757. if _, ok := stores[esapi.PushSecretStoreRef{Name: "shared-name"}]; !ok {
  758. t.Fatalf("expected omitted-kind store ref to be retained, got %#v", stores)
  759. }
  760. }
  761. func TestDeleteSecretFromProvidersV2DeletesRemovedStoreEvenWhenNoLongerReferenced(t *testing.T) {
  762. previous := clientmanager.V2ProvidersEnabled()
  763. clientmanager.SetV2ProvidersEnabled(true)
  764. t.Cleanup(func() {
  765. clientmanager.SetV2ProvidersEnabled(previous)
  766. })
  767. scheme := newPushSecretTestScheme(t)
  768. server, address, tlsSecret := newPushSecretProviderServer(t)
  769. clusterProvider := &esv1.ClusterProvider{
  770. ObjectMeta: metav1.ObjectMeta{
  771. Name: "cluster-provider",
  772. },
  773. Spec: esv1.ClusterProviderSpec{
  774. Config: esv1.ProviderConfig{
  775. Address: address,
  776. ProviderRef: esv1.ProviderReference{
  777. APIVersion: "provider.external-secrets.io/v2alpha1",
  778. Kind: "Kubernetes",
  779. Name: "backend",
  780. },
  781. },
  782. AuthenticationScope: esv1.AuthenticationScopeManifestNamespace,
  783. Conditions: []esv1.ClusterSecretStoreCondition{{
  784. Namespaces: []string{"tenant-a"},
  785. }},
  786. },
  787. }
  788. kubeClient := fakeclient.NewClientBuilder().
  789. WithScheme(scheme).
  790. WithObjects(
  791. clusterProvider,
  792. &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{
  793. Name: "tenant-a",
  794. Labels: map[string]string{
  795. "kubernetes.io/metadata.name": "tenant-a",
  796. },
  797. }},
  798. &corev1.Secret{
  799. ObjectMeta: metav1.ObjectMeta{
  800. Name: "external-secrets-provider-tls",
  801. Namespace: "tenant-a",
  802. },
  803. Data: tlsSecret,
  804. },
  805. ).
  806. Build()
  807. r := &Reconciler{Client: kubeClient, Log: logr.Discard()}
  808. ps := &esapi.PushSecret{
  809. ObjectMeta: metav1.ObjectMeta{
  810. Name: "pushsecret",
  811. Namespace: "tenant-a",
  812. },
  813. Status: esapi.PushSecretStatus{
  814. SyncedPushSecrets: esapi.SyncedPushSecretsMap{
  815. "ClusterProvider/cluster-provider": {
  816. "remote/path": {
  817. Match: esapi.PushSecretMatch{
  818. SecretKey: "token",
  819. RemoteRef: esapi.PushSecretRemoteRef{
  820. RemoteKey: "remote/path",
  821. Property: "property",
  822. },
  823. },
  824. },
  825. },
  826. },
  827. },
  828. }
  829. result, err := r.DeleteSecretFromProvidersV2(context.Background(), ps, esapi.SyncedPushSecretsMap{}, map[esapi.PushSecretStoreRef]any{})
  830. if err != nil {
  831. t.Fatalf("DeleteSecretFromProvidersV2() error = %v", err)
  832. }
  833. if server.deleteRequest == nil {
  834. t.Fatal("expected delete request to be recorded")
  835. }
  836. if server.deleteRequest.RemoteRef == nil || server.deleteRequest.RemoteRef.RemoteKey != "remote/path" {
  837. t.Fatalf("unexpected delete ref: %#v", server.deleteRequest.RemoteRef)
  838. }
  839. if _, ok := result["ClusterProvider/cluster-provider"]; ok {
  840. t.Fatalf("expected synced state to be cleaned up, got %#v", result)
  841. }
  842. }
  843. func TestDeleteSecretFromProvidersV2UsesProviderNamespaceAuthScope(t *testing.T) {
  844. previous := clientmanager.V2ProvidersEnabled()
  845. clientmanager.SetV2ProvidersEnabled(true)
  846. t.Cleanup(func() {
  847. clientmanager.SetV2ProvidersEnabled(previous)
  848. })
  849. scheme := newPushSecretTestScheme(t)
  850. server, address, tlsSecret := newPushSecretProviderServer(t)
  851. const manifestNamespace = "tenant-a"
  852. const providerNamespace = "provider-config-ns"
  853. clusterProvider := &esv1.ClusterProvider{
  854. ObjectMeta: metav1.ObjectMeta{
  855. Name: "cluster-provider",
  856. Labels: map[string]string{"scope": "cluster"},
  857. },
  858. Spec: esv1.ClusterProviderSpec{
  859. Config: esv1.ProviderConfig{
  860. Address: address,
  861. ProviderRef: esv1.ProviderReference{
  862. APIVersion: "provider.external-secrets.io/v2alpha1",
  863. Kind: "Kubernetes",
  864. Name: "backend",
  865. Namespace: providerNamespace,
  866. },
  867. },
  868. AuthenticationScope: esv1.AuthenticationScopeProviderNamespace,
  869. Conditions: []esv1.ClusterSecretStoreCondition{{
  870. Namespaces: []string{manifestNamespace},
  871. }},
  872. },
  873. }
  874. kubeClient := fakeclient.NewClientBuilder().
  875. WithScheme(scheme).
  876. WithObjects(
  877. clusterProvider,
  878. &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{
  879. Name: manifestNamespace,
  880. Labels: map[string]string{
  881. "kubernetes.io/metadata.name": manifestNamespace,
  882. },
  883. }},
  884. &corev1.Secret{
  885. ObjectMeta: metav1.ObjectMeta{
  886. Name: "external-secrets-provider-tls",
  887. Namespace: providerNamespace,
  888. },
  889. Data: tlsSecret,
  890. },
  891. ).
  892. Build()
  893. r := &Reconciler{Client: kubeClient, Log: logr.Discard()}
  894. ps := &esapi.PushSecret{
  895. ObjectMeta: metav1.ObjectMeta{
  896. Name: "pushsecret",
  897. Namespace: manifestNamespace,
  898. },
  899. Status: esapi.PushSecretStatus{
  900. SyncedPushSecrets: esapi.SyncedPushSecretsMap{
  901. "ClusterProvider/cluster-provider": {
  902. "remote/path": {
  903. Match: esapi.PushSecretMatch{
  904. SecretKey: "token",
  905. RemoteRef: esapi.PushSecretRemoteRef{
  906. RemoteKey: "remote/path",
  907. Property: "property",
  908. },
  909. },
  910. },
  911. },
  912. },
  913. },
  914. }
  915. result, err := r.DeleteSecretFromProvidersV2(context.Background(), ps, esapi.SyncedPushSecretsMap{}, map[esapi.PushSecretStoreRef]any{
  916. {Name: "cluster-provider", Kind: esv1.ClusterProviderKindStr}: clusterProvider,
  917. })
  918. if err != nil {
  919. t.Fatalf("DeleteSecretFromProvidersV2() error = %v", err)
  920. }
  921. if server.deleteRequest == nil {
  922. t.Fatal("expected delete request to be recorded")
  923. }
  924. if server.deleteRequest.SourceNamespace != providerNamespace {
  925. t.Fatalf("unexpected source namespace: %q", server.deleteRequest.SourceNamespace)
  926. }
  927. if server.deleteRequest.ProviderRef == nil || server.deleteRequest.ProviderRef.Namespace != providerNamespace {
  928. t.Fatalf("unexpected provider ref: %#v", server.deleteRequest.ProviderRef)
  929. }
  930. if _, ok := result["ClusterProvider/cluster-provider"]; ok {
  931. t.Fatalf("expected synced state to be cleaned up, got %#v", result)
  932. }
  933. }
  934. func TestDeleteSecretFromProvidersV2DeletesOnlyRemovedEntriesForManifestScope(t *testing.T) {
  935. previous := clientmanager.V2ProvidersEnabled()
  936. clientmanager.SetV2ProvidersEnabled(true)
  937. t.Cleanup(func() {
  938. clientmanager.SetV2ProvidersEnabled(previous)
  939. })
  940. scheme := newPushSecretTestScheme(t)
  941. server, address, tlsSecret := newPushSecretProviderServer(t)
  942. clusterProvider := &esv1.ClusterProvider{
  943. ObjectMeta: metav1.ObjectMeta{
  944. Name: "cluster-provider",
  945. },
  946. Spec: esv1.ClusterProviderSpec{
  947. Config: esv1.ProviderConfig{
  948. Address: address,
  949. ProviderRef: esv1.ProviderReference{
  950. APIVersion: "provider.external-secrets.io/v2alpha1",
  951. Kind: "Kubernetes",
  952. Name: "backend",
  953. },
  954. },
  955. AuthenticationScope: esv1.AuthenticationScopeManifestNamespace,
  956. Conditions: []esv1.ClusterSecretStoreCondition{{
  957. Namespaces: []string{"tenant-a"},
  958. }},
  959. },
  960. }
  961. kubeClient := fakeclient.NewClientBuilder().
  962. WithScheme(scheme).
  963. WithObjects(
  964. clusterProvider,
  965. &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{
  966. Name: "tenant-a",
  967. Labels: map[string]string{
  968. "kubernetes.io/metadata.name": "tenant-a",
  969. },
  970. }},
  971. &corev1.Secret{
  972. ObjectMeta: metav1.ObjectMeta{
  973. Name: "external-secrets-provider-tls",
  974. Namespace: "tenant-a",
  975. },
  976. Data: tlsSecret,
  977. },
  978. ).
  979. Build()
  980. r := &Reconciler{Client: kubeClient, Log: logr.Discard()}
  981. ps := &esapi.PushSecret{
  982. ObjectMeta: metav1.ObjectMeta{
  983. Name: "pushsecret",
  984. Namespace: "tenant-a",
  985. },
  986. Status: esapi.PushSecretStatus{
  987. SyncedPushSecrets: esapi.SyncedPushSecretsMap{
  988. "ClusterProvider/cluster-provider": {
  989. "remote/keep/property": {
  990. Match: esapi.PushSecretMatch{
  991. SecretKey: "keep",
  992. RemoteRef: esapi.PushSecretRemoteRef{
  993. RemoteKey: "remote/keep",
  994. Property: "property",
  995. },
  996. },
  997. },
  998. "remote/delete/property": {
  999. Match: esapi.PushSecretMatch{
  1000. SecretKey: "delete",
  1001. RemoteRef: esapi.PushSecretRemoteRef{
  1002. RemoteKey: "remote/delete",
  1003. Property: "property",
  1004. },
  1005. },
  1006. },
  1007. },
  1008. },
  1009. },
  1010. }
  1011. newMap := esapi.SyncedPushSecretsMap{
  1012. "ClusterProvider/cluster-provider": {
  1013. "remote/keep/property": ps.Status.SyncedPushSecrets["ClusterProvider/cluster-provider"]["remote/keep/property"],
  1014. },
  1015. }
  1016. result, err := r.DeleteSecretFromProvidersV2(context.Background(), ps, newMap, map[esapi.PushSecretStoreRef]any{
  1017. {Name: "cluster-provider", Kind: esv1.ClusterProviderKindStr}: clusterProvider,
  1018. })
  1019. if err != nil {
  1020. t.Fatalf("DeleteSecretFromProvidersV2() error = %v", err)
  1021. }
  1022. if server.deleteRequest == nil {
  1023. t.Fatal("expected delete request to be recorded")
  1024. }
  1025. if server.deleteRequest.SourceNamespace != "tenant-a" {
  1026. t.Fatalf("unexpected source namespace: %q", server.deleteRequest.SourceNamespace)
  1027. }
  1028. if server.deleteRequest.RemoteRef == nil || server.deleteRequest.RemoteRef.RemoteKey != "remote/delete" || server.deleteRequest.RemoteRef.Property != "property" {
  1029. t.Fatalf("unexpected delete ref: %#v", server.deleteRequest.RemoteRef)
  1030. }
  1031. storeState, ok := result["ClusterProvider/cluster-provider"]
  1032. if !ok {
  1033. t.Fatalf("expected synced state for cluster provider, got %#v", result)
  1034. }
  1035. if len(storeState) != 1 {
  1036. t.Fatalf("expected one remaining synced entry, got %#v", storeState)
  1037. }
  1038. if _, ok := storeState["remote/keep/property"]; !ok {
  1039. t.Fatalf("expected keep entry to remain, got %#v", storeState)
  1040. }
  1041. if _, ok := storeState["remote/delete/property"]; ok {
  1042. t.Fatalf("expected delete entry to be removed, got %#v", storeState)
  1043. }
  1044. }
  1045. func TestDeleteSecretFromProvidersV2DeletesOnlyRemovedEntriesForProviderNamespaceScope(t *testing.T) {
  1046. previous := clientmanager.V2ProvidersEnabled()
  1047. clientmanager.SetV2ProvidersEnabled(true)
  1048. t.Cleanup(func() {
  1049. clientmanager.SetV2ProvidersEnabled(previous)
  1050. })
  1051. scheme := newPushSecretTestScheme(t)
  1052. server, address, tlsSecret := newPushSecretProviderServer(t)
  1053. const manifestNamespace = "tenant-a"
  1054. const providerNamespace = "provider-config-ns"
  1055. clusterProvider := &esv1.ClusterProvider{
  1056. ObjectMeta: metav1.ObjectMeta{
  1057. Name: "cluster-provider",
  1058. },
  1059. Spec: esv1.ClusterProviderSpec{
  1060. Config: esv1.ProviderConfig{
  1061. Address: address,
  1062. ProviderRef: esv1.ProviderReference{
  1063. APIVersion: "provider.external-secrets.io/v2alpha1",
  1064. Kind: "Kubernetes",
  1065. Name: "backend",
  1066. Namespace: providerNamespace,
  1067. },
  1068. },
  1069. AuthenticationScope: esv1.AuthenticationScopeProviderNamespace,
  1070. Conditions: []esv1.ClusterSecretStoreCondition{{
  1071. Namespaces: []string{manifestNamespace},
  1072. }},
  1073. },
  1074. }
  1075. kubeClient := fakeclient.NewClientBuilder().
  1076. WithScheme(scheme).
  1077. WithObjects(
  1078. clusterProvider,
  1079. &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{
  1080. Name: manifestNamespace,
  1081. Labels: map[string]string{
  1082. "kubernetes.io/metadata.name": manifestNamespace,
  1083. },
  1084. }},
  1085. &corev1.Secret{
  1086. ObjectMeta: metav1.ObjectMeta{
  1087. Name: "external-secrets-provider-tls",
  1088. Namespace: providerNamespace,
  1089. },
  1090. Data: tlsSecret,
  1091. },
  1092. ).
  1093. Build()
  1094. r := &Reconciler{Client: kubeClient, Log: logr.Discard()}
  1095. ps := &esapi.PushSecret{
  1096. ObjectMeta: metav1.ObjectMeta{
  1097. Name: "pushsecret",
  1098. Namespace: manifestNamespace,
  1099. },
  1100. Status: esapi.PushSecretStatus{
  1101. SyncedPushSecrets: esapi.SyncedPushSecretsMap{
  1102. "ClusterProvider/cluster-provider": {
  1103. "remote/keep/property": {
  1104. Match: esapi.PushSecretMatch{
  1105. SecretKey: "keep",
  1106. RemoteRef: esapi.PushSecretRemoteRef{
  1107. RemoteKey: "remote/keep",
  1108. Property: "property",
  1109. },
  1110. },
  1111. },
  1112. "remote/delete/property": {
  1113. Match: esapi.PushSecretMatch{
  1114. SecretKey: "delete",
  1115. RemoteRef: esapi.PushSecretRemoteRef{
  1116. RemoteKey: "remote/delete",
  1117. Property: "property",
  1118. },
  1119. },
  1120. },
  1121. },
  1122. },
  1123. },
  1124. }
  1125. newMap := esapi.SyncedPushSecretsMap{
  1126. "ClusterProvider/cluster-provider": {
  1127. "remote/keep/property": ps.Status.SyncedPushSecrets["ClusterProvider/cluster-provider"]["remote/keep/property"],
  1128. },
  1129. }
  1130. result, err := r.DeleteSecretFromProvidersV2(context.Background(), ps, newMap, map[esapi.PushSecretStoreRef]any{
  1131. {Name: "cluster-provider", Kind: esv1.ClusterProviderKindStr}: clusterProvider,
  1132. })
  1133. if err != nil {
  1134. t.Fatalf("DeleteSecretFromProvidersV2() error = %v", err)
  1135. }
  1136. if server.deleteRequest == nil {
  1137. t.Fatal("expected delete request to be recorded")
  1138. }
  1139. if server.deleteRequest.SourceNamespace != providerNamespace {
  1140. t.Fatalf("unexpected source namespace: %q", server.deleteRequest.SourceNamespace)
  1141. }
  1142. if server.deleteRequest.ProviderRef == nil || server.deleteRequest.ProviderRef.Namespace != providerNamespace {
  1143. t.Fatalf("unexpected provider ref: %#v", server.deleteRequest.ProviderRef)
  1144. }
  1145. if server.deleteRequest.RemoteRef == nil || server.deleteRequest.RemoteRef.RemoteKey != "remote/delete" || server.deleteRequest.RemoteRef.Property != "property" {
  1146. t.Fatalf("unexpected delete ref: %#v", server.deleteRequest.RemoteRef)
  1147. }
  1148. storeState, ok := result["ClusterProvider/cluster-provider"]
  1149. if !ok {
  1150. t.Fatalf("expected synced state for cluster provider, got %#v", result)
  1151. }
  1152. if len(storeState) != 1 {
  1153. t.Fatalf("expected one remaining synced entry, got %#v", storeState)
  1154. }
  1155. if _, ok := storeState["remote/keep/property"]; !ok {
  1156. t.Fatalf("expected keep entry to remain, got %#v", storeState)
  1157. }
  1158. if _, ok := storeState["remote/delete/property"]; ok {
  1159. t.Fatalf("expected delete entry to be removed, got %#v", storeState)
  1160. }
  1161. }
  1162. func newPushSecretTestScheme(t *testing.T) *runtime.Scheme {
  1163. t.Helper()
  1164. scheme := runtime.NewScheme()
  1165. utilruntime.Must(clientgoscheme.AddToScheme(scheme))
  1166. utilruntime.Must(esv1.AddToScheme(scheme))
  1167. utilruntime.Must(esapi.AddToScheme(scheme))
  1168. return scheme
  1169. }
  1170. func newPushSecretProviderServer(t *testing.T) (*pushsecretRecordingProviderServer, string, map[string][]byte) {
  1171. t.Helper()
  1172. serverCert, serverKey, clientCert, clientKey, caCert := newPushSecretTLSArtifacts(t, "127.0.0.1")
  1173. caPool := x509.NewCertPool()
  1174. if !caPool.AppendCertsFromPEM(caCert) {
  1175. t.Fatal("failed to append CA cert")
  1176. }
  1177. tlsCert, err := tls.X509KeyPair(serverCert, serverKey)
  1178. if err != nil {
  1179. t.Fatalf("X509KeyPair() error = %v", err)
  1180. }
  1181. lis, err := net.Listen("tcp", "127.0.0.1:0")
  1182. if err != nil {
  1183. t.Fatalf("Listen() error = %v", err)
  1184. }
  1185. server := &pushsecretRecordingProviderServer{}
  1186. grpcServer := grpc.NewServer(grpc.Creds(credentials.NewTLS(&tls.Config{
  1187. MinVersion: tls.VersionTLS12,
  1188. Certificates: []tls.Certificate{tlsCert},
  1189. ClientCAs: caPool,
  1190. ClientAuth: tls.RequireAndVerifyClientCert,
  1191. })))
  1192. pb.RegisterSecretStoreProviderServer(grpcServer, server)
  1193. go func() {
  1194. _ = grpcServer.Serve(lis)
  1195. }()
  1196. t.Cleanup(func() {
  1197. grpcServer.Stop()
  1198. _ = lis.Close()
  1199. })
  1200. return server, lis.Addr().String(), map[string][]byte{
  1201. "ca.crt": caCert,
  1202. "client.crt": clientCert,
  1203. "client.key": clientKey,
  1204. }
  1205. }
  1206. func newPushSecretTLSArtifacts(t *testing.T, host string) (serverCertPEM, serverKeyPEM, clientCertPEM, clientKeyPEM, caCertPEM []byte) {
  1207. t.Helper()
  1208. caKey, err := rsa.GenerateKey(rand.Reader, 2048)
  1209. if err != nil {
  1210. t.Fatalf("GenerateKey() error = %v", err)
  1211. }
  1212. caTemplate := &x509.Certificate{
  1213. SerialNumber: big.NewInt(1),
  1214. Subject: pkix.Name{
  1215. CommonName: "pushsecret-test-ca",
  1216. },
  1217. NotBefore: time.Now().Add(-time.Hour),
  1218. NotAfter: time.Now().Add(24 * time.Hour),
  1219. KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign,
  1220. BasicConstraintsValid: true,
  1221. IsCA: true,
  1222. }
  1223. caDER, err := x509.CreateCertificate(rand.Reader, caTemplate, caTemplate, &caKey.PublicKey, caKey)
  1224. if err != nil {
  1225. t.Fatalf("CreateCertificate() error = %v", err)
  1226. }
  1227. caCert, err := x509.ParseCertificate(caDER)
  1228. if err != nil {
  1229. t.Fatalf("ParseCertificate() error = %v", err)
  1230. }
  1231. serverCertPEM, serverKeyPEM = newPushSecretSignedTLSCert(t, caCert, caKey, 2, host, []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth})
  1232. clientCertPEM, clientKeyPEM = newPushSecretSignedTLSCert(t, caCert, caKey, 3, host, []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth})
  1233. caCertPEM = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: caDER})
  1234. return serverCertPEM, serverKeyPEM, clientCertPEM, clientKeyPEM, caCertPEM
  1235. }
  1236. func newPushSecretSignedTLSCert(t *testing.T, caCert *x509.Certificate, caKey *rsa.PrivateKey, serial int64, host string, usages []x509.ExtKeyUsage) ([]byte, []byte) {
  1237. t.Helper()
  1238. key, err := rsa.GenerateKey(rand.Reader, 2048)
  1239. if err != nil {
  1240. t.Fatalf("GenerateKey() error = %v", err)
  1241. }
  1242. template := &x509.Certificate{
  1243. SerialNumber: big.NewInt(serial),
  1244. Subject: pkix.Name{
  1245. CommonName: host,
  1246. },
  1247. NotBefore: time.Now().Add(-time.Hour),
  1248. NotAfter: time.Now().Add(24 * time.Hour),
  1249. KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
  1250. ExtKeyUsage: usages,
  1251. }
  1252. if ip := net.ParseIP(host); ip != nil {
  1253. template.IPAddresses = []net.IP{ip}
  1254. } else {
  1255. template.DNSNames = []string{host}
  1256. }
  1257. der, err := x509.CreateCertificate(rand.Reader, template, caCert, &key.PublicKey, caKey)
  1258. if err != nil {
  1259. t.Fatalf("CreateCertificate() error = %v", err)
  1260. }
  1261. return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der}),
  1262. pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})
  1263. }