diff --git a/internal/gatewayapi/backendtrafficpolicy.go b/internal/gatewayapi/backendtrafficpolicy.go index 7cc3cc5ae8..3e9a0b508b 100644 --- a/internal/gatewayapi/backendtrafficpolicy.go +++ b/internal/gatewayapi/backendtrafficpolicy.go @@ -263,7 +263,7 @@ func (t *Translator) processBackendTrafficPolicyForRoute( // Find the Gateway that the route belongs to and add it to the // gatewayRouteMap and ancestor list, which will be used to check // policy overrides and populate its ancestor status. - parentRefs := GetParentReferences(targetedRoute) + parentRefs := GetManagedParentReferences(targetedRoute) ancestorRefs := make([]*gwapiv1.ParentReference, 0, len(parentRefs)) // parentRefCtxs holds parent gateway/listener contexts for using in policy merge logic. parentRefCtxs := make([]*RouteParentContext, 0, len(parentRefs)) @@ -295,12 +295,7 @@ func (t *Translator) processBackendTrafficPolicyForRoute( // Do need a section name since the policy is targeting to a route. ancestorRef := getAncestorRefForPolicy(mapKey.NamespacedName, p.SectionName) ancestorRefs = append(ancestorRefs, &ancestorRef) - - // Only process parentRefs that were handled by this translator - // (skip those referencing Gateways with different GatewayClasses) - if parentRefCtx := targetedRoute.GetRouteParentContext(p); parentRefCtx != nil { - parentRefCtxs = append(parentRefCtxs, parentRefCtx) - } + parentRefCtxs = append(parentRefCtxs, targetedRoute.GetRouteParentContext(p)) } } diff --git a/internal/gatewayapi/contexts.go b/internal/gatewayapi/contexts.go index 66dbce04ba..28afe83f27 100644 --- a/internal/gatewayapi/contexts.go +++ b/internal/gatewayapi/contexts.go @@ -534,6 +534,22 @@ func GetParentReferences(route RouteContext) []gwapiv1.ParentReference { return route.GetParentReferences() } +// GetManagedParentReferences returns route parentRefs that are managed by this controller. +func GetManagedParentReferences(route RouteContext) []gwapiv1.ParentReference { + parentRefs := GetParentReferences(route) + managed := make([]gwapiv1.ParentReference, 0, len(parentRefs)) + for _, parentRef := range parentRefs { + // RouteParentContext is only created for parentRefs handled by this + // translator run. If absent, the parentRef points to a Gateway that is + // not managed by this controller. + if route.GetRouteParentContext(parentRef) == nil { + continue + } + managed = append(managed, parentRef) + } + return managed +} + // GetRouteStatus returns the RouteStatus object associated with the Route. func GetRouteStatus(route RouteContext) *gwapiv1.RouteStatus { return route.GetRouteStatus() diff --git a/internal/gatewayapi/envoyextensionpolicy.go b/internal/gatewayapi/envoyextensionpolicy.go index 20d3201966..83ea7be986 100644 --- a/internal/gatewayapi/envoyextensionpolicy.go +++ b/internal/gatewayapi/envoyextensionpolicy.go @@ -200,7 +200,7 @@ func (t *Translator) processEnvoyExtensionPolicyForRoute( // Find the Gateway that the route belongs to and add it to the // gatewayRouteMap and ancestor list, which will be used to check // policy overrides and populate its ancestor status. - parentRefs := GetParentReferences(targetedRoute) + parentRefs := GetManagedParentReferences(targetedRoute) for _, p := range parentRefs { if p.Kind == nil || *p.Kind == resource.KindGateway { namespace := targetedRoute.GetNamespace() diff --git a/internal/gatewayapi/securitypolicy.go b/internal/gatewayapi/securitypolicy.go index f8f1181833..61d63cb009 100644 --- a/internal/gatewayapi/securitypolicy.go +++ b/internal/gatewayapi/securitypolicy.go @@ -233,7 +233,7 @@ func (t *Translator) processSecurityPolicyForRoute( // Find the parent Gateways for the route and add it to the // gatewayRouteMap, which will be used to check policy override. // The parent gateways are also used to set the status of the policy. - parentRefs := GetParentReferences(targetedRoute) + parentRefs := GetManagedParentReferences(targetedRoute) for _, p := range parentRefs { if p.Kind == nil || *p.Kind == resource.KindGateway { namespace := targetedRoute.GetNamespace() diff --git a/internal/gatewayapi/securitypolicy_test.go b/internal/gatewayapi/securitypolicy_test.go index 7967a781f3..b17cde6a81 100644 --- a/internal/gatewayapi/securitypolicy_test.go +++ b/internal/gatewayapi/securitypolicy_test.go @@ -884,6 +884,23 @@ func hasParentFalseCondition(p *egv1a1.SecurityPolicy) bool { return false } +func SetRouteParentContext(route RouteContext, parentRef gwapiv1.ParentReference) { + ctx := &RouteParentContext{ParentReference: &parentRef} + switch r := route.(type) { + case *HTTPRouteContext: + ctx.HTTPRoute = r.HTTPRoute + case *GRPCRouteContext: + ctx.GRPCRoute = r.GRPCRoute + case *TLSRouteContext: + ctx.TLSRoute = r.TLSRoute + case *TCPRouteContext: + ctx.TCPRoute = r.TCPRoute + case *UDPRouteContext: + ctx.UDPRoute = r.UDPRoute + } + route.SetRouteParentContext(parentRef, ctx) +} + // --- TCP branch: validateSecurityPolicyForTCP(...) returns err -> SetTranslationErrorForPolicyAncestors(...) + return func Test_SecurityPolicy_TCP_Invalid_setsStatus_and_returns(t *testing.T) { tr := &Translator{GatewayControllerName: "gateway.envoyproxy.io/gatewayclass-controller"} @@ -923,6 +940,7 @@ func Test_SecurityPolicy_TCP_Invalid_setsStatus_and_returns(t *testing.T) { }, }, } + SetRouteParentContext(tcpRoute, tcpRoute.Spec.ParentRefs[0]) // Create the target reference target := gwapiv1.LocalPolicyTargetReferenceWithSectionName{ @@ -1000,6 +1018,7 @@ func Test_SecurityPolicy_HTTP_Invalid_setsStatus_and_returns(t *testing.T) { }, }, } + SetRouteParentContext(httpRoute, httpRoute.Spec.ParentRefs[0]) // Create the target reference target := gwapiv1.LocalPolicyTargetReferenceWithSectionName{ diff --git a/internal/gatewayapi/testdata/securitypolicy-mixed-managed-unmanaged-route-parents.in.yaml b/internal/gatewayapi/testdata/securitypolicy-mixed-managed-unmanaged-route-parents.in.yaml new file mode 100644 index 0000000000..7ef2349a21 --- /dev/null +++ b/internal/gatewayapi/testdata/securitypolicy-mixed-managed-unmanaged-route-parents.in.yaml @@ -0,0 +1,68 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: default + name: managed-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: default + name: unmanaged-gateway + spec: + gatewayClassName: other-gateway-class + listeners: + - name: http + protocol: HTTP + port: 80 + allowedRoutes: + namespaces: + from: Same + +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: mixed-parents-route + spec: + parentRefs: + - name: managed-gateway + sectionName: http + - name: unmanaged-gateway + sectionName: http + rules: + - matches: + - path: + value: "/" + backendRefs: + - name: service-1 + port: 8080 + +securityPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: default + name: route-policy + spec: + targetRefs: + - group: gateway.networking.k8s.io + kind: HTTPRoute + name: mixed-parents-route + authorization: + defaultAction: Deny + rules: + - action: Allow + principal: + clientCIDRs: + - 10.0.0.0/24 diff --git a/internal/gatewayapi/testdata/securitypolicy-mixed-managed-unmanaged-route-parents.out.yaml b/internal/gatewayapi/testdata/securitypolicy-mixed-managed-unmanaged-route-parents.out.yaml new file mode 100644 index 0000000000..983f72e57b --- /dev/null +++ b/internal/gatewayapi/testdata/securitypolicy-mixed-managed-unmanaged-route-parents.out.yaml @@ -0,0 +1,219 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + name: managed-gateway + namespace: default + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: Same + name: http + port: 80 + protocol: HTTP + status: + listeners: + - attachedRoutes: 1 + conditions: + - lastTransitionTime: null + message: Sending translated listener configuration to the data plane + reason: Programmed + status: "True" + type: Programmed + - lastTransitionTime: null + message: Listener has been successfully translated + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Listener references have been resolved + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + name: http + supportedKinds: + - group: gateway.networking.k8s.io + kind: HTTPRoute + - group: gateway.networking.k8s.io + kind: GRPCRoute +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + name: mixed-parents-route + namespace: default + spec: + parentRefs: + - name: managed-gateway + sectionName: http + - name: unmanaged-gateway + sectionName: http + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: / + status: + parents: + - conditions: + - lastTransitionTime: null + message: Route is accepted + reason: Accepted + status: "True" + type: Accepted + - lastTransitionTime: null + message: Resolved all the Object references for the Route + reason: ResolvedRefs + status: "True" + type: ResolvedRefs + controllerName: gateway.envoyproxy.io/gatewayclass-controller + parentRef: + name: managed-gateway + sectionName: http +infraIR: + default/managed-gateway: + proxy: + listeners: + - name: default/managed-gateway/http + ports: + - containerPort: 10080 + name: http-80 + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: managed-gateway + gateway.envoyproxy.io/owning-gateway-namespace: default + ownerReference: + kind: GatewayClass + name: envoy-gateway-class + name: default/managed-gateway + namespace: envoy-gateway-system +securityPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + name: route-policy + namespace: default + spec: + authorization: + defaultAction: Deny + rules: + - action: Allow + principal: + clientCIDRs: + - 10.0.0.0/24 + targetRefs: + - group: gateway.networking.k8s.io + kind: HTTPRoute + name: mixed-parents-route + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: managed-gateway + namespace: default + sectionName: http + conditions: + - lastTransitionTime: null + message: Policy has been accepted. + reason: Accepted + status: "True" + type: Accepted + controllerName: gateway.envoyproxy.io/gatewayclass-controller +xdsIR: + default/managed-gateway: + accessLog: + json: + - path: /dev/stdout + globalResources: + proxyServiceCluster: + metadata: + kind: Service + name: envoy-default-managed-gateway-f4161932 + namespace: envoy-gateway-system + sectionName: "8080" + name: default/managed-gateway + settings: + - addressType: IP + endpoints: + - host: 7.6.5.4 + port: 8080 + zone: zone1 + metadata: + kind: Service + name: envoy-default-managed-gateway-f4161932 + namespace: envoy-gateway-system + sectionName: "8080" + name: default/managed-gateway + protocol: TCP + http: + - address: 0.0.0.0 + externalPort: 80 + hostnames: + - '*' + isHTTP2: false + metadata: + kind: Gateway + name: managed-gateway + namespace: default + sectionName: http + name: default/managed-gateway/http + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - destination: + metadata: + kind: HTTPRoute + name: mixed-parents-route + namespace: default + name: httproute/default/mixed-parents-route/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + metadata: + kind: Service + name: service-1 + namespace: default + sectionName: "8080" + name: httproute/default/mixed-parents-route/rule/0/backend/0 + protocol: HTTP + weight: 1 + hostname: '*' + isHTTP2: false + metadata: + kind: HTTPRoute + name: mixed-parents-route + namespace: default + name: httproute/default/mixed-parents-route/rule/0/match/0/* + pathMatch: + distinct: false + name: "" + prefix: / + security: + authorization: + defaultAction: Deny + rules: + - action: Allow + name: securitypolicy/default/route-policy/authorization/rule/0 + principal: + clientCIDRs: + - cidr: 10.0.0.0/24 + distinct: false + ip: 10.0.0.0 + isIPv6: false + maskLen: 24 + readyListener: + address: 0.0.0.0 + ipFamily: IPv4 + path: /ready + port: 19003