diff --git a/internal/provider/kubernetes/controller.go b/internal/provider/kubernetes/controller.go index 629cb419233..897b030dcf7 100644 --- a/internal/provider/kubernetes/controller.go +++ b/internal/provider/kubernetes/controller.go @@ -609,57 +609,37 @@ func (r *gatewayAPIReconciler) processSecurityPolicyObjectRefs( // Add the referenced BackendRefs and ReferenceGrants in ExtAuth to Maps for later processing extAuth := policy.Spec.ExtAuth if extAuth != nil { - var backendRef *gwapiv1.BackendObjectReference + var backendRef gwapiv1.BackendObjectReference if extAuth.GRPC != nil { - backendRef = extAuth.GRPC.BackendRef + if extAuth.GRPC.BackendRef != nil { + backendRef = *extAuth.GRPC.BackendRef + } if len(extAuth.GRPC.BackendRefs) > 0 { if len(extAuth.GRPC.BackendRefs) != 0 { - backendRef = egv1a1.ToBackendObjectReference(extAuth.GRPC.BackendRefs[0]) + backendRef = extAuth.GRPC.BackendRefs[0].BackendObjectReference } } - } else { - backendRef = extAuth.HTTP.BackendRef + } else if extAuth.HTTP != nil { + if extAuth.HTTP.BackendRef != nil { + backendRef = *extAuth.HTTP.BackendRef + } if len(extAuth.HTTP.BackendRefs) > 0 { if len(extAuth.HTTP.BackendRefs) != 0 { - backendRef = egv1a1.ToBackendObjectReference(extAuth.HTTP.BackendRefs[0]) + backendRef = extAuth.HTTP.BackendRefs[0].BackendObjectReference } } } - - backendNamespace := gatewayapi.NamespaceDerefOr(backendRef.Namespace, policy.Namespace) - resourceMap.allAssociatedBackendRefs.Insert(gwapiv1.BackendObjectReference{ - Group: backendRef.Group, - Kind: backendRef.Kind, - Namespace: gatewayapi.NamespacePtr(backendNamespace), - Name: backendRef.Name, - }) - - if backendNamespace != policy.Namespace { - from := ObjectKindNamespacedName{ - kind: resource.KindSecurityPolicy, - namespace: policy.Namespace, - name: policy.Name, - } - to := ObjectKindNamespacedName{ - kind: gatewayapi.KindDerefOr(backendRef.Kind, resource.KindService), - namespace: backendNamespace, - name: string(backendRef.Name), - } - refGrant, err := r.findReferenceGrant(ctx, from, to) - switch { - case err != nil: - r.log.Error(err, "failed to find ReferenceGrant") - case refGrant == nil: - r.log.Info("no matching ReferenceGrants found", "from", from.kind, - "from namespace", from.namespace, "target", to.kind, "target namespace", to.namespace) - default: - if !resourceMap.allAssociatedReferenceGrants.Has(utils.NamespacedName(refGrant).String()) { - resourceMap.allAssociatedReferenceGrants.Insert(utils.NamespacedName(refGrant).String()) - resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) - r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, - "name", refGrant.Name) - } - } + if err := r.processBackendRef( + ctx, + resourceMap, + resourceTree, + resource.KindSecurityPolicy, + policy.Namespace, + policy.Name, + backendRef); err != nil { + r.log.Error(err, + "failed to process ExtAuth BackendRef for SecurityPolicy", + "policy", policy, "backendRef", backendRef) } } @@ -682,12 +662,78 @@ func (r *gatewayAPIReconciler) processSecurityPolicyObjectRefs( }); err != nil { r.log.Error(err, "failed to process LocalJWKS ConfigMap", "policy", policy, "localJWKS", provider.LocalJWKS) } + } else if provider.RemoteJWKS != nil { + for _, br := range provider.RemoteJWKS.BackendRefs { + if err := r.processBackendRef( + ctx, + resourceMap, + resourceTree, + resource.KindSecurityPolicy, + policy.Namespace, + policy.Name, + br.BackendObjectReference); err != nil { + r.log.Error(err, + "failed to process RemoteJWKS BackendRef for SecurityPolicy", + "policy", policy, "backendRef", br.BackendObjectReference) + } + } } } } } } +// processBackendRef adds the referenced BackendRef to the resourceMap for later processBackendRefs. +// If BackendRef exists in a different namespace and there is a ReferenceGrant, adds ReferenceGrant to the resourceTree. +func (r *gatewayAPIReconciler) processBackendRef( + ctx context.Context, + resourceMap *resourceMappings, + resourceTree *resource.Resources, + ownerKind string, + ownerNS string, + ownerName string, + backendRef gwapiv1.BackendObjectReference, +) error { + backendNamespace := gatewayapi.NamespaceDerefOr(backendRef.Namespace, ownerNS) + resourceMap.allAssociatedBackendRefs.Insert(gwapiv1.BackendObjectReference{ + Group: backendRef.Group, + Kind: backendRef.Kind, + Namespace: gatewayapi.NamespacePtr(backendNamespace), + Name: backendRef.Name, + }) + + if backendNamespace != ownerNS { + from := ObjectKindNamespacedName{ + kind: ownerKind, + namespace: ownerNS, + name: ownerName, + } + to := ObjectKindNamespacedName{ + kind: gatewayapi.KindDerefOr(backendRef.Kind, resource.KindService), + namespace: backendNamespace, + name: string(backendRef.Name), + } + refGrant, err := r.findReferenceGrant(ctx, from, to) + switch { + case err != nil: + return fmt.Errorf("failed to find ReferenceGrant: %w", err) + case refGrant == nil: + return fmt.Errorf( + "no matching ReferenceGrants found: from %s/%s to %s/%s", + from.kind, from.namespace, to.kind, to.namespace) + default: + // RefGrant found + if !resourceMap.allAssociatedReferenceGrants.Has(utils.NamespacedName(refGrant).String()) { + resourceMap.allAssociatedReferenceGrants.Insert(utils.NamespacedName(refGrant).String()) + resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) + r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, + "name", refGrant.Name) + } + } + } + return nil +} + // processOIDCHMACSecret adds the OIDC HMAC Secret to the resourceTree. // The OIDC HMAC Secret is created by the CertGen job and is used by SecurityPolicy // to configure OAuth2 filters. @@ -2184,42 +2230,17 @@ func (r *gatewayAPIReconciler) processEnvoyExtensionPolicyObjectRefs( // Add the referenced BackendRefs and ReferenceGrants in ExtAuth to Maps for later processing for _, ep := range policy.Spec.ExtProc { for _, br := range ep.BackendRefs { - backendRef := br.BackendObjectReference - - backendNamespace := gatewayapi.NamespaceDerefOr(backendRef.Namespace, policy.Namespace) - resourceMap.allAssociatedBackendRefs.Insert(gwapiv1.BackendObjectReference{ - Group: backendRef.Group, - Kind: backendRef.Kind, - Namespace: gatewayapi.NamespacePtr(backendNamespace), - Name: backendRef.Name, - }) - - if backendNamespace != policy.Namespace { - from := ObjectKindNamespacedName{ - kind: resource.KindEnvoyExtensionPolicy, - namespace: policy.Namespace, - name: policy.Name, - } - to := ObjectKindNamespacedName{ - kind: gatewayapi.KindDerefOr(backendRef.Kind, resource.KindService), - namespace: backendNamespace, - name: string(backendRef.Name), - } - refGrant, err := r.findReferenceGrant(ctx, from, to) - switch { - case err != nil: - r.log.Error(err, "failed to find ReferenceGrant") - case refGrant == nil: - r.log.Info("no matching ReferenceGrants found", "from", from.kind, - "from namespace", from.namespace, "target", to.kind, "target namespace", to.namespace) - default: - if !resourceMap.allAssociatedReferenceGrants.Has(utils.NamespacedName(refGrant).String()) { - resourceMap.allAssociatedReferenceGrants.Insert(utils.NamespacedName(refGrant).String()) - resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) - r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, - "name", refGrant.Name) - } - } + if err := r.processBackendRef( + ctx, + resourceMap, + resourceTree, + resource.KindEnvoyExtensionPolicy, + policy.Namespace, + policy.Name, + br.BackendObjectReference); err != nil { + r.log.Error(err, + "failed to process ExtProc BackendRef for EnvoyExtensionPolicy", + "policy", policy, "backendRef", br.BackendObjectReference) } } } diff --git a/internal/provider/kubernetes/controller_test.go b/internal/provider/kubernetes/controller_test.go index 4ac85754f1c..3914071992c 100644 --- a/internal/provider/kubernetes/controller_test.go +++ b/internal/provider/kubernetes/controller_test.go @@ -420,27 +420,494 @@ func TestProcessEnvoyExtensionPolicyObjectRefs(t *testing.T) { t.Run(tc.name, func(t *testing.T) { // Add objects referenced by test cases. objs := []client.Object{tc.envoyExtensionPolicy, tc.backend, tc.referenceGrant} - - // Create the reconciler. - logger := logging.DefaultLogger(os.Stdout, egv1a1.LogLevelInfo) + r := setupReferenceGrantReconciler(objs) ctx := context.Background() + resourceTree := resource.NewResources() + resourceMap := newResourceMapping() - r := &gatewayAPIReconciler{ - log: logger, - classController: "some-gateway-class", + err := r.processEnvoyExtensionPolicies(ctx, resourceTree, resourceMap) + require.NoError(t, err) + if tc.shouldBeAdded { + require.Contains(t, resourceTree.ReferenceGrants, tc.referenceGrant) + } else { + require.NotContains(t, resourceTree.ReferenceGrants, tc.referenceGrant) } + }) + } +} - r.client = fakeclient.NewClientBuilder(). - WithScheme(envoygateway.GetScheme()). - WithObjects(objs...). - WithIndex(&gwapiv1b1.ReferenceGrant{}, targetRefGrantRouteIndex, getReferenceGrantIndexerFunc). - Build() +func TestProcessSecurityPolicyObjectRefs(t *testing.T) { + testCases := []struct { + name string + securityPolicy *egv1a1.SecurityPolicy + backend *egv1a1.Backend + referenceGrant *gwapiv1b1.ReferenceGrant + shouldBeAdded bool + }{ + { + name: "valid security policy with remote jwks proper ref grant to backend", + securityPolicy: &egv1a1.SecurityPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns-1", + Name: "test-policy", + }, + Spec: egv1a1.SecurityPolicySpec{ + JWT: &egv1a1.JWT{ + Providers: []egv1a1.JWTProvider{ + { + RemoteJWKS: &egv1a1.RemoteJWKS{ + BackendCluster: egv1a1.BackendCluster{ + BackendRefs: []egv1a1.BackendRef{ + { + BackendObjectReference: gwapiv1.BackendObjectReference{ + Namespace: gatewayapi.NamespacePtr("ns-2"), + Name: "test-backend", + Kind: gatewayapi.KindPtr(resource.KindBackend), + Group: gatewayapi.GroupPtr(egv1a1.GroupName), + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + backend: &egv1a1.Backend{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns-2", + Name: "test-backend", + }, + }, + referenceGrant: &gwapiv1b1.ReferenceGrant{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns-2", + Name: "test-grant", + }, + Spec: gwapiv1b1.ReferenceGrantSpec{ + From: []gwapiv1b1.ReferenceGrantFrom{ + { + Namespace: gwapiv1.Namespace("ns-1"), + Kind: gwapiv1.Kind(resource.KindSecurityPolicy), + Group: gwapiv1.Group(egv1a1.GroupName), + }, + }, + To: []gwapiv1b1.ReferenceGrantTo{ + { + Name: gatewayapi.ObjectNamePtr("test-backend"), + Kind: gwapiv1.Kind(resource.KindBackend), + Group: gwapiv1.Group(egv1a1.GroupName), + }, + }, + }, + }, + shouldBeAdded: true, + }, + { + name: "valid security policy with remote jwks wrong namespace ref grant to backend", + securityPolicy: &egv1a1.SecurityPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns-1", + Name: "test-policy", + }, + Spec: egv1a1.SecurityPolicySpec{ + JWT: &egv1a1.JWT{ + Providers: []egv1a1.JWTProvider{ + { + RemoteJWKS: &egv1a1.RemoteJWKS{ + BackendCluster: egv1a1.BackendCluster{ + BackendRefs: []egv1a1.BackendRef{ + { + BackendObjectReference: gwapiv1.BackendObjectReference{ + Namespace: gatewayapi.NamespacePtr("ns-2"), + Name: "test-backend", + Kind: gatewayapi.KindPtr(resource.KindBackend), + Group: gatewayapi.GroupPtr(egv1a1.GroupName), + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + backend: &egv1a1.Backend{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns-2", + Name: "test-backend", + }, + }, + referenceGrant: &gwapiv1b1.ReferenceGrant{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns-2", + Name: "test-grant", + }, + Spec: gwapiv1b1.ReferenceGrantSpec{ + From: []gwapiv1b1.ReferenceGrantFrom{ + { + Namespace: gwapiv1.Namespace("ns-invalid"), + Kind: gwapiv1.Kind(resource.KindSecurityPolicy), + Group: gwapiv1.Group(egv1a1.GroupName), + }, + }, + To: []gwapiv1b1.ReferenceGrantTo{ + { + Name: gatewayapi.ObjectNamePtr("test-backend"), + Kind: gwapiv1.Kind(resource.KindBackend), + Group: gwapiv1.Group(egv1a1.GroupName), + }, + }, + }, + }, + shouldBeAdded: false, + }, + { + name: "valid security policy with extAuth grpc proper ref grant to backend", + securityPolicy: &egv1a1.SecurityPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns-1", + Name: "test-policy", + }, + Spec: egv1a1.SecurityPolicySpec{ + ExtAuth: &egv1a1.ExtAuth{ + GRPC: &egv1a1.GRPCExtAuthService{ + BackendCluster: egv1a1.BackendCluster{ + BackendRefs: []egv1a1.BackendRef{ + { + BackendObjectReference: gwapiv1.BackendObjectReference{ + Namespace: gatewayapi.NamespacePtr("ns-2"), + Name: "test-backend", + Kind: gatewayapi.KindPtr(resource.KindBackend), + Group: gatewayapi.GroupPtr(egv1a1.GroupName), + }, + }, + }, + }, + }, + }, + }, + }, + backend: &egv1a1.Backend{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns-2", + Name: "test-backend", + }, + }, + referenceGrant: &gwapiv1b1.ReferenceGrant{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns-2", + Name: "test-grant", + }, + Spec: gwapiv1b1.ReferenceGrantSpec{ + From: []gwapiv1b1.ReferenceGrantFrom{ + { + Namespace: gwapiv1.Namespace("ns-1"), + Kind: gwapiv1.Kind(resource.KindSecurityPolicy), + Group: gwapiv1.Group(egv1a1.GroupName), + }, + }, + To: []gwapiv1b1.ReferenceGrantTo{ + { + Name: gatewayapi.ObjectNamePtr("test-backend"), + Kind: gwapiv1.Kind(resource.KindBackend), + Group: gwapiv1.Group(egv1a1.GroupName), + }, + }, + }, + }, + shouldBeAdded: true, + }, + { + name: "valid security policy with extAuth grpc proper ref grant to backend (deprecated field)", + securityPolicy: &egv1a1.SecurityPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns-1", + Name: "test-policy", + }, + Spec: egv1a1.SecurityPolicySpec{ + ExtAuth: &egv1a1.ExtAuth{ + GRPC: &egv1a1.GRPCExtAuthService{ + BackendCluster: egv1a1.BackendCluster{ + BackendRef: &gwapiv1.BackendObjectReference{ + Namespace: gatewayapi.NamespacePtr("ns-2"), + Name: "test-backend", + Kind: gatewayapi.KindPtr(resource.KindBackend), + Group: gatewayapi.GroupPtr(egv1a1.GroupName), + }, + }, + }, + }, + }, + }, + backend: &egv1a1.Backend{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns-2", + Name: "test-backend", + }, + }, + referenceGrant: &gwapiv1b1.ReferenceGrant{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns-2", + Name: "test-grant", + }, + Spec: gwapiv1b1.ReferenceGrantSpec{ + From: []gwapiv1b1.ReferenceGrantFrom{ + { + Namespace: gwapiv1.Namespace("ns-1"), + Kind: gwapiv1.Kind(resource.KindSecurityPolicy), + Group: gwapiv1.Group(egv1a1.GroupName), + }, + }, + To: []gwapiv1b1.ReferenceGrantTo{ + { + Name: gatewayapi.ObjectNamePtr("test-backend"), + Kind: gwapiv1.Kind(resource.KindBackend), + Group: gwapiv1.Group(egv1a1.GroupName), + }, + }, + }, + }, + shouldBeAdded: true, + }, + { + name: "valid security policy with extAuth grpc wrong namespace ref grant to backend", + securityPolicy: &egv1a1.SecurityPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns-1", + Name: "test-policy", + }, + Spec: egv1a1.SecurityPolicySpec{ + ExtAuth: &egv1a1.ExtAuth{ + GRPC: &egv1a1.GRPCExtAuthService{ + BackendCluster: egv1a1.BackendCluster{ + BackendRefs: []egv1a1.BackendRef{ + { + BackendObjectReference: gwapiv1.BackendObjectReference{ + Namespace: gatewayapi.NamespacePtr("ns-2"), + Name: "test-backend", + Kind: gatewayapi.KindPtr(resource.KindBackend), + Group: gatewayapi.GroupPtr(egv1a1.GroupName), + }, + }, + }, + }, + }, + }, + }, + }, + backend: &egv1a1.Backend{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns-2", + Name: "test-backend", + }, + }, + referenceGrant: &gwapiv1b1.ReferenceGrant{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns-2", + Name: "test-grant", + }, + Spec: gwapiv1b1.ReferenceGrantSpec{ + From: []gwapiv1b1.ReferenceGrantFrom{ + { + Namespace: gwapiv1.Namespace("ns-invalid"), + Kind: gwapiv1.Kind(resource.KindSecurityPolicy), + Group: gwapiv1.Group(egv1a1.GroupName), + }, + }, + To: []gwapiv1b1.ReferenceGrantTo{ + { + Name: gatewayapi.ObjectNamePtr("test-backend"), + Kind: gwapiv1.Kind(resource.KindBackend), + Group: gwapiv1.Group(egv1a1.GroupName), + }, + }, + }, + }, + shouldBeAdded: false, + }, + { + name: "valid security policy with extAuth http proper ref grant to backend", + securityPolicy: &egv1a1.SecurityPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns-1", + Name: "test-policy", + }, + Spec: egv1a1.SecurityPolicySpec{ + ExtAuth: &egv1a1.ExtAuth{ + HTTP: &egv1a1.HTTPExtAuthService{ + BackendCluster: egv1a1.BackendCluster{ + BackendRefs: []egv1a1.BackendRef{ + { + BackendObjectReference: gwapiv1.BackendObjectReference{ + Namespace: gatewayapi.NamespacePtr("ns-2"), + Name: "test-backend", + Kind: gatewayapi.KindPtr(resource.KindBackend), + Group: gatewayapi.GroupPtr(egv1a1.GroupName), + }, + }, + }, + }, + }, + }, + }, + }, + backend: &egv1a1.Backend{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns-2", + Name: "test-backend", + }, + }, + referenceGrant: &gwapiv1b1.ReferenceGrant{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns-2", + Name: "test-grant", + }, + Spec: gwapiv1b1.ReferenceGrantSpec{ + From: []gwapiv1b1.ReferenceGrantFrom{ + { + Namespace: gwapiv1.Namespace("ns-1"), + Kind: gwapiv1.Kind(resource.KindSecurityPolicy), + Group: gwapiv1.Group(egv1a1.GroupName), + }, + }, + To: []gwapiv1b1.ReferenceGrantTo{ + { + Name: gatewayapi.ObjectNamePtr("test-backend"), + Kind: gwapiv1.Kind(resource.KindBackend), + Group: gwapiv1.Group(egv1a1.GroupName), + }, + }, + }, + }, + shouldBeAdded: true, + }, + { + name: "valid security policy with extAuth http proper ref grant to backend (deprecated field)", + securityPolicy: &egv1a1.SecurityPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns-1", + Name: "test-policy", + }, + Spec: egv1a1.SecurityPolicySpec{ + ExtAuth: &egv1a1.ExtAuth{ + HTTP: &egv1a1.HTTPExtAuthService{ + BackendCluster: egv1a1.BackendCluster{ + BackendRef: &gwapiv1.BackendObjectReference{ + Namespace: gatewayapi.NamespacePtr("ns-2"), + Name: "test-backend", + Kind: gatewayapi.KindPtr(resource.KindBackend), + Group: gatewayapi.GroupPtr(egv1a1.GroupName), + }, + }, + }, + }, + }, + }, + backend: &egv1a1.Backend{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns-2", + Name: "test-backend", + }, + }, + referenceGrant: &gwapiv1b1.ReferenceGrant{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns-2", + Name: "test-grant", + }, + Spec: gwapiv1b1.ReferenceGrantSpec{ + From: []gwapiv1b1.ReferenceGrantFrom{ + { + Namespace: gwapiv1.Namespace("ns-1"), + Kind: gwapiv1.Kind(resource.KindSecurityPolicy), + Group: gwapiv1.Group(egv1a1.GroupName), + }, + }, + To: []gwapiv1b1.ReferenceGrantTo{ + { + Name: gatewayapi.ObjectNamePtr("test-backend"), + Kind: gwapiv1.Kind(resource.KindBackend), + Group: gwapiv1.Group(egv1a1.GroupName), + }, + }, + }, + }, + shouldBeAdded: true, + }, + { + name: "valid security policy with extAuth http wrong namespace ref grant to backend", + securityPolicy: &egv1a1.SecurityPolicy{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns-1", + Name: "test-policy", + }, + Spec: egv1a1.SecurityPolicySpec{ + ExtAuth: &egv1a1.ExtAuth{ + HTTP: &egv1a1.HTTPExtAuthService{ + BackendCluster: egv1a1.BackendCluster{ + BackendRefs: []egv1a1.BackendRef{ + { + BackendObjectReference: gwapiv1.BackendObjectReference{ + Namespace: gatewayapi.NamespacePtr("ns-2"), + Name: "test-backend", + Kind: gatewayapi.KindPtr(resource.KindBackend), + Group: gatewayapi.GroupPtr(egv1a1.GroupName), + }, + }, + }, + }, + }, + }, + }, + }, + backend: &egv1a1.Backend{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns-2", + Name: "test-backend", + }, + }, + referenceGrant: &gwapiv1b1.ReferenceGrant{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: "ns-2", + Name: "test-grant", + }, + Spec: gwapiv1b1.ReferenceGrantSpec{ + From: []gwapiv1b1.ReferenceGrantFrom{ + { + Namespace: gwapiv1.Namespace("ns-invalid"), + Kind: gwapiv1.Kind(resource.KindSecurityPolicy), + Group: gwapiv1.Group(egv1a1.GroupName), + }, + }, + To: []gwapiv1b1.ReferenceGrantTo{ + { + Name: gatewayapi.ObjectNamePtr("test-backend"), + Kind: gwapiv1.Kind(resource.KindBackend), + Group: gwapiv1.Group(egv1a1.GroupName), + }, + }, + }, + }, + shouldBeAdded: false, + }, + } + + for i := range testCases { + tc := testCases[i] + // Run the test cases. + t.Run(tc.name, func(t *testing.T) { + // Add objects referenced by test cases. + objs := []client.Object{tc.securityPolicy, tc.backend, tc.referenceGrant} + r := setupReferenceGrantReconciler(objs) + ctx := context.Background() resourceTree := resource.NewResources() resourceMap := newResourceMapping() - err := r.processEnvoyExtensionPolicies(ctx, resourceTree, resourceMap) + err := r.processSecurityPolicies(ctx, resourceTree, resourceMap) require.NoError(t, err) if tc.shouldBeAdded { require.Contains(t, resourceTree.ReferenceGrants, tc.referenceGrant) @@ -450,3 +917,19 @@ func TestProcessEnvoyExtensionPolicyObjectRefs(t *testing.T) { }) } } + +func setupReferenceGrantReconciler(objs []client.Object) *gatewayAPIReconciler { + logger := logging.DefaultLogger(os.Stdout, egv1a1.LogLevelInfo) + + r := &gatewayAPIReconciler{ + log: logger, + classController: "some-gateway-class", + } + + r.client = fakeclient.NewClientBuilder(). + WithScheme(envoygateway.GetScheme()). + WithObjects(objs...). + WithIndex(&gwapiv1b1.ReferenceGrant{}, targetRefGrantRouteIndex, getReferenceGrantIndexerFunc). + Build() + return r +} diff --git a/internal/provider/kubernetes/indexers.go b/internal/provider/kubernetes/indexers.go index 5114ce5882e..17fcb85968a 100644 --- a/internal/provider/kubernetes/indexers.go +++ b/internal/provider/kubernetes/indexers.go @@ -610,35 +610,49 @@ func secretSecurityPolicyIndexFunc(rawObj client.Object) []string { func backendSecurityPolicyIndexFunc(rawObj client.Object) []string { securityPolicy := rawObj.(*egv1a1.SecurityPolicy) - var backendRef *gwapiv1.BackendObjectReference + var ( + backendRefs []gwapiv1.BackendObjectReference + values []string + ) if securityPolicy.Spec.ExtAuth != nil { if securityPolicy.Spec.ExtAuth.HTTP != nil { http := securityPolicy.Spec.ExtAuth.HTTP - backendRef = http.BackendRef + if http.BackendRef != nil { + backendRefs = append(backendRefs, *http.BackendRef) + } if len(http.BackendRefs) > 0 { - backendRef = egv1a1.ToBackendObjectReference(http.BackendRefs[0]) + backendRefs = append(backendRefs, http.BackendRefs[0].BackendObjectReference) } } else if securityPolicy.Spec.ExtAuth.GRPC != nil { grpc := securityPolicy.Spec.ExtAuth.GRPC - backendRef = grpc.BackendRef + if grpc.BackendRef != nil { + backendRefs = append(backendRefs, *grpc.BackendRef) + } if len(grpc.BackendRefs) > 0 { - backendRef = egv1a1.ToBackendObjectReference(grpc.BackendRefs[0]) + backendRefs = append(backendRefs, grpc.BackendRefs[0].BackendObjectReference) + } + } + } + if securityPolicy.Spec.JWT != nil { + for _, provider := range securityPolicy.Spec.JWT.Providers { + if provider.RemoteJWKS != nil { + for _, backendRef := range provider.RemoteJWKS.BackendRefs { + backendRefs = append(backendRefs, backendRef.BackendObjectReference) + } } } } - if backendRef != nil { - return []string{ + for _, reference := range backendRefs { + values = append(values, types.NamespacedName{ - Namespace: gatewayapi.NamespaceDerefOr(backendRef.Namespace, securityPolicy.Namespace), - Name: string(backendRef.Name), + Namespace: gatewayapi.NamespaceDerefOr(reference.Namespace, securityPolicy.Namespace), + Name: string(reference.Name), }.String(), - } + ) } - - // This should not happen because the CEL validation should catch it. - return []string{} + return values } func configMapSecurityPolicyIndexFunc(rawObj client.Object) []string { diff --git a/internal/provider/kubernetes/routes.go b/internal/provider/kubernetes/routes.go index f2c676066f3..20f8ad292d5 100644 --- a/internal/provider/kubernetes/routes.go +++ b/internal/provider/kubernetes/routes.go @@ -61,34 +61,17 @@ func (r *gatewayAPIReconciler) processTLSRoutes(ctx context.Context, gatewayName r.log.Error(err, "invalid backendRef") continue } - - backendNamespace := gatewayapi.NamespaceDerefOr(backendRef.Namespace, tlsRoute.Namespace) - resourceMap.allAssociatedBackendRefs.Insert(gwapiv1.BackendObjectReference{ - Group: backendRef.Group, - Kind: backendRef.Kind, - Namespace: gatewayapi.NamespacePtr(backendNamespace), - Name: backendRef.Name, - }) - - if backendNamespace != tlsRoute.Namespace { - from := ObjectKindNamespacedName{kind: resource.KindTLSRoute, namespace: tlsRoute.Namespace, name: tlsRoute.Name} - to := ObjectKindNamespacedName{kind: gatewayapi.KindDerefOr(backendRef.Kind, resource.KindService), namespace: backendNamespace, name: string(backendRef.Name)} - refGrant, err := r.findReferenceGrant(ctx, from, to) - switch { - case err != nil: - r.log.Error(err, "failed to find ReferenceGrant") - case refGrant == nil: - r.log.Info("no matching ReferenceGrants found", "from", from.kind, - "from namespace", from.namespace, "target", to.kind, "target namespace", to.namespace) - default: - refGrantNamespacedName := utils.NamespacedName(refGrant).String() - if !resourceMap.allAssociatedReferenceGrants.Has(refGrantNamespacedName) { - resourceMap.allAssociatedReferenceGrants.Insert(refGrantNamespacedName) - resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) - r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, - "name", refGrant.Name) - } - } + if err := r.processBackendRef( + ctx, + resourceMap, + resourceTree, + resource.KindTLSRoute, + tlsRoute.Namespace, + tlsRoute.Name, + backendRef.BackendObjectReference); err != nil { + r.log.Error(err, + "failed to process BackendRef for TLSRoute", + "tlsRoute", tlsRoute, "backendRef", backendRef.BackendObjectReference) } } } @@ -143,42 +126,17 @@ func (r *gatewayAPIReconciler) processGRPCRoutes(ctx context.Context, gatewayNam r.log.Error(err, "invalid backendRef") continue } - - backendNamespace := gatewayapi.NamespaceDerefOr(backendRef.Namespace, grpcRoute.Namespace) - resourceMap.allAssociatedBackendRefs.Insert(gwapiv1.BackendObjectReference{ - Group: backendRef.Group, - Kind: backendRef.Kind, - Namespace: gatewayapi.NamespacePtr(backendNamespace), - Name: backendRef.Name, - }) - - if backendNamespace != grpcRoute.Namespace { - from := ObjectKindNamespacedName{ - kind: resource.KindGRPCRoute, - namespace: grpcRoute.Namespace, - name: grpcRoute.Name, - } - to := ObjectKindNamespacedName{ - kind: gatewayapi.KindDerefOr(backendRef.Kind, resource.KindService), - namespace: backendNamespace, - name: string(backendRef.Name), - } - refGrant, err := r.findReferenceGrant(ctx, from, to) - switch { - case err != nil: - r.log.Error(err, "failed to find ReferenceGrant") - case refGrant == nil: - r.log.Info("no matching ReferenceGrants found", "from", from.kind, - "from namespace", from.namespace, "target", to.kind, "target namespace", to.namespace) - default: - refGrantNamespacedName := utils.NamespacedName(refGrant).String() - if !resourceMap.allAssociatedReferenceGrants.Has(refGrantNamespacedName) { - resourceMap.allAssociatedReferenceGrants.Insert(refGrantNamespacedName) - resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) - r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, - "name", refGrant.Name) - } - } + if err := r.processBackendRef( + ctx, + resourceMap, + resourceTree, + resource.KindGRPCRoute, + grpcRoute.Namespace, + grpcRoute.Name, + backendRef.BackendObjectReference); err != nil { + r.log.Error(err, + "failed to process BackendRef for GRPCRoute", + "grpcRoute", grpcRoute, "backendRef", backendRef.BackendObjectReference) } } @@ -281,43 +239,19 @@ func (r *gatewayAPIReconciler) processHTTPRoutes(ctx context.Context, gatewayNam r.log.Error(err, "invalid backendRef") continue } - - backendNamespace := gatewayapi.NamespaceDerefOr(backendRef.Namespace, httpRoute.Namespace) - resourceMap.allAssociatedBackendRefs.Insert(gwapiv1.BackendObjectReference{ - Group: backendRef.Group, - Kind: backendRef.Kind, - Namespace: gatewayapi.NamespacePtr(backendNamespace), - Name: backendRef.Name, - }) - - if backendNamespace != httpRoute.Namespace { - from := ObjectKindNamespacedName{ - kind: resource.KindHTTPRoute, - namespace: httpRoute.Namespace, - name: httpRoute.Name, - } - to := ObjectKindNamespacedName{ - kind: gatewayapi.KindDerefOr(backendRef.Kind, resource.KindService), - namespace: backendNamespace, - name: string(backendRef.Name), - } - refGrant, err := r.findReferenceGrant(ctx, from, to) - switch { - case err != nil: - r.log.Error(err, "failed to find ReferenceGrant") - case refGrant == nil: - r.log.Info("no matching ReferenceGrants found", "from", from.kind, - "from namespace", from.namespace, "target", to.kind, "target namespace", to.namespace) - default: - refGrantNamespacedName := utils.NamespacedName(refGrant).String() - if !resourceMap.allAssociatedReferenceGrants.Has(refGrantNamespacedName) { - resourceMap.allAssociatedReferenceGrants.Insert(refGrantNamespacedName) - resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) - r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, - "name", refGrant.Name) - } - } + if err := r.processBackendRef( + ctx, + resourceMap, + resourceTree, + resource.KindHTTPRoute, + httpRoute.Namespace, + httpRoute.Name, + backendRef.BackendObjectReference); err != nil { + r.log.Error(err, + "failed to process BackendRef for HTTPRoute", + "httpRoute", httpRoute, "backendRef", backendRef.BackendObjectReference) } + for i := range backendRef.Filters { // Some of the validation logic in processHTTPRouteFilter is not needed for backendRef filters. // However, we reuse the same function to avoid code duplication. @@ -382,42 +316,17 @@ func (r *gatewayAPIReconciler) processHTTPRouteFilter( if err := validateBackendRef(&mirrorBackendRef); err != nil { return fmt.Errorf("invalid backendRef for requestMirror filter: %w", err) } - - backendNamespace := gatewayapi.NamespaceDerefOr(mirrorBackendRef.Namespace, httpRoute.Namespace) - resourceMap.allAssociatedBackendRefs.Insert(gwapiv1.BackendObjectReference{ - Group: mirrorBackendRef.Group, - Kind: mirrorBackendRef.Kind, - Namespace: gatewayapi.NamespacePtr(backendNamespace), - Name: mirrorBackendRef.Name, - }) - - if backendNamespace != httpRoute.Namespace { - from := ObjectKindNamespacedName{ - kind: resource.KindHTTPRoute, - namespace: httpRoute.Namespace, - name: httpRoute.Name, - } - to := ObjectKindNamespacedName{ - kind: gatewayapi.KindDerefOr(mirrorBackendRef.Kind, resource.KindService), - namespace: backendNamespace, - name: string(mirrorBackendRef.Name), - } - refGrant, err := r.findReferenceGrant(ctx, from, to) - switch { - case err != nil: - r.log.Error(err, "failed to find ReferenceGrant") - case refGrant == nil: - r.log.Info("no matching ReferenceGrants found", "from", from.kind, - "from namespace", from.namespace, "target", to.kind, "target namespace", to.namespace) - default: - refGrantNamespacedName := utils.NamespacedName(refGrant).String() - if !resourceMap.allAssociatedReferenceGrants.Has(refGrantNamespacedName) { - resourceMap.allAssociatedReferenceGrants.Insert(refGrantNamespacedName) - resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) - r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, - "name", refGrant.Name) - } - } + if err := r.processBackendRef( + ctx, + resourceMap, + resourceTree, + resource.KindHTTPRoute, + httpRoute.Namespace, + httpRoute.Name, + mirrorBackendRef.BackendObjectReference); err != nil { + r.log.Error(err, + "failed to process BackendRef for HTTPRouteFilter", + "httpRoute", httpRoute, "backendRef", mirrorBackendRef.BackendObjectReference) } case gwapiv1.HTTPRouteFilterExtensionRef: // NOTE: filters must be in the same namespace as the HTTPRoute @@ -500,34 +409,17 @@ func (r *gatewayAPIReconciler) processTCPRoutes(ctx context.Context, gatewayName r.log.Error(err, "invalid backendRef") continue } - - backendNamespace := gatewayapi.NamespaceDerefOr(backendRef.Namespace, tcpRoute.Namespace) - resourceMap.allAssociatedBackendRefs.Insert(gwapiv1.BackendObjectReference{ - Group: backendRef.Group, - Kind: backendRef.Kind, - Namespace: gatewayapi.NamespacePtr(backendNamespace), - Name: backendRef.Name, - }) - - if backendNamespace != tcpRoute.Namespace { - from := ObjectKindNamespacedName{kind: resource.KindTCPRoute, namespace: tcpRoute.Namespace, name: tcpRoute.Name} - to := ObjectKindNamespacedName{kind: gatewayapi.KindDerefOr(backendRef.Kind, resource.KindService), namespace: backendNamespace, name: string(backendRef.Name)} - refGrant, err := r.findReferenceGrant(ctx, from, to) - switch { - case err != nil: - r.log.Error(err, "failed to find ReferenceGrant") - case refGrant == nil: - r.log.Info("no matching ReferenceGrants found", "from", from.kind, - "from namespace", from.namespace, "target", to.kind, "target namespace", to.namespace) - default: - refGrantNamespacedName := utils.NamespacedName(refGrant).String() - if !resourceMap.allAssociatedReferenceGrants.Has(refGrantNamespacedName) { - resourceMap.allAssociatedReferenceGrants.Insert(refGrantNamespacedName) - resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) - r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, - "name", refGrant.Name) - } - } + if err := r.processBackendRef( + ctx, + resourceMap, + resourceTree, + resource.KindTCPRoute, + tcpRoute.Namespace, + tcpRoute.Name, + backendRef.BackendObjectReference); err != nil { + r.log.Error(err, + "failed to process BackendRef for TCPRoute", + "tcpRoute", tcpRoute, "backendRef", backendRef.BackendObjectReference) } } } @@ -581,33 +473,17 @@ func (r *gatewayAPIReconciler) processUDPRoutes(ctx context.Context, gatewayName r.log.Error(err, "invalid backendRef") continue } - - backendNamespace := gatewayapi.NamespaceDerefOr(backendRef.Namespace, udpRoute.Namespace) - resourceMap.allAssociatedBackendRefs.Insert(gwapiv1.BackendObjectReference{ - Group: backendRef.Group, - Kind: backendRef.Kind, - Namespace: gatewayapi.NamespacePtr(backendNamespace), - Name: backendRef.Name, - }) - - if backendNamespace != udpRoute.Namespace { - from := ObjectKindNamespacedName{kind: resource.KindUDPRoute, namespace: udpRoute.Namespace, name: udpRoute.Name} - to := ObjectKindNamespacedName{kind: gatewayapi.KindDerefOr(backendRef.Kind, resource.KindService), namespace: backendNamespace, name: string(backendRef.Name)} - refGrant, err := r.findReferenceGrant(ctx, from, to) - switch { - case err != nil: - r.log.Error(err, "failed to find ReferenceGrant") - case refGrant == nil: - r.log.Info("no matching ReferenceGrants found", "from", from.kind, - "from namespace", from.namespace, "target", to.kind, "target namespace", to.namespace) - default: - if !resourceMap.allAssociatedReferenceGrants.Has(utils.NamespacedName(refGrant).String()) { - resourceMap.allAssociatedReferenceGrants.Insert(utils.NamespacedName(refGrant).String()) - resourceTree.ReferenceGrants = append(resourceTree.ReferenceGrants, refGrant) - r.log.Info("added ReferenceGrant to resource map", "namespace", refGrant.Namespace, - "name", refGrant.Name) - } - } + if err := r.processBackendRef( + ctx, + resourceMap, + resourceTree, + resource.KindUDPRoute, + udpRoute.Namespace, + udpRoute.Name, + backendRef.BackendObjectReference); err != nil { + r.log.Error(err, + "failed to process BackendRef for UDPRoute", + "udpRoute", udpRoute, "backendRef", backendRef.BackendObjectReference) } } } diff --git a/release-notes/current.yaml b/release-notes/current.yaml index 3271a4f9079..e4a3d75cdfb 100644 --- a/release-notes/current.yaml +++ b/release-notes/current.yaml @@ -10,6 +10,7 @@ security updates: | new features: | bug fixes: | + Fix reference grant from SecurityPolicy to referenced remoteJWKS backend not respected. # Enhancements that improve performance. performance improvements: |