diff --git a/internal/provider/kubernetes/controller.go b/internal/provider/kubernetes/controller.go index 7ce7f615b31..cccb20f2c49 100644 --- a/internal/provider/kubernetes/controller.go +++ b/internal/provider/kubernetes/controller.go @@ -552,57 +552,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) } } @@ -625,12 +605,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. @@ -2123,42 +2169,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 b2d6cdaf79b..d732dc62625 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 7f90d876287..3a6b3727dd3 100644 --- a/internal/provider/kubernetes/indexers.go +++ b/internal/provider/kubernetes/indexers.go @@ -602,35 +602,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 ddb03391207..fcc93b3771b 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.BackendObjectReference.Group, - Kind: backendRef.BackendObjectReference.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.BackendObjectReference.Group, - Kind: backendRef.BackendObjectReference.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.BackendObjectReference.Group, - Kind: backendRef.BackendObjectReference.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. @@ -381,42 +315,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.BackendObjectReference.Group, - Kind: mirrorBackendRef.BackendObjectReference.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) } } else if filter.Type == gwapiv1.HTTPRouteFilterExtensionRef { // NOTE: filters must be in the same namespace as the HTTPRoute @@ -499,34 +408,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.BackendObjectReference.Group, - Kind: backendRef.BackendObjectReference.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) } } } @@ -580,33 +472,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.BackendObjectReference.Group, - Kind: backendRef.BackendObjectReference.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 9eb2589111f..3c28708ba32 100644 --- a/release-notes/current.yaml +++ b/release-notes/current.yaml @@ -52,6 +52,7 @@ bug fixes: | Fix kubernetes resources not being deleted when the customized name used. Do not treat essential resource like namespace as the missing resource while loading from file. Do not set retriable status codes to 503 when RetryOn is configured in BackendTrafficPolicy. + Fix reference grant from SecurityPolicy to referenced remoteJWKS backend not respected. # Enhancements that improve performance. performance improvements: |