diff --git a/internal/gatewayapi/clienttrafficpolicy.go b/internal/gatewayapi/clienttrafficpolicy.go index 843109838f..b520f65924 100644 --- a/internal/gatewayapi/clienttrafficpolicy.go +++ b/internal/gatewayapi/clienttrafficpolicy.go @@ -303,22 +303,13 @@ func resolveCTPolicyTargetRef( } // If sectionName is set, make sure its valid - sectionName := targetRef.SectionName - if sectionName != nil { - found := false - for _, l := range gateway.listeners { - if l.Name == *sectionName { - found = true - break - } - } - if !found { - message := fmt.Sprintf("No section name %s found for %s", *sectionName, key.String()) - - return gateway.GatewayContext, &status.PolicyResolveError{ - Reason: gwapiv1a2.PolicyReasonInvalid, - Message: message, - } + if targetRef.SectionName != nil { + if err := validateGatewayListenerSectionName( + *targetRef.SectionName, + key, + gateway.listeners, + ); err != nil { + return gateway.GatewayContext, err } } diff --git a/internal/gatewayapi/securitypolicy.go b/internal/gatewayapi/securitypolicy.go index b40257d963..997f0c8c93 100644 --- a/internal/gatewayapi/securitypolicy.go +++ b/internal/gatewayapi/securitypolicy.go @@ -398,6 +398,17 @@ func resolveSecurityPolicyGatewayTargetRef( return nil, nil } + // If sectionName is set, make sure its valid + if target.SectionName != nil { + if err := validateGatewayListenerSectionName( + *target.SectionName, + key, + gateway.listeners, + ); err != nil { + return gateway.GatewayContext, err + } + } + if target.SectionName == nil { // Check if another policy targeting the same Gateway exists if gateway.attached { diff --git a/internal/gatewayapi/testdata/clienttrafficpolicy-status-conditions.out.yaml b/internal/gatewayapi/testdata/clienttrafficpolicy-status-conditions.out.yaml index fa03f1f305..e4086f621f 100644 --- a/internal/gatewayapi/testdata/clienttrafficpolicy-status-conditions.out.yaml +++ b/internal/gatewayapi/testdata/clienttrafficpolicy-status-conditions.out.yaml @@ -103,8 +103,8 @@ clientTrafficPolicies: sectionName: foo-bar conditions: - lastTransitionTime: null - message: No section name foo-bar found for envoy-gateway/gateway-3 - reason: Invalid + message: No section name foo-bar found for Gateway envoy-gateway/gateway-3 + reason: TargetNotFound status: "False" type: Accepted controllerName: gateway.envoyproxy.io/gatewayclass-controller diff --git a/internal/gatewayapi/testdata/securitypolicy-invalid-no-section-name-listener.in.yaml b/internal/gatewayapi/testdata/securitypolicy-invalid-no-section-name-listener.in.yaml new file mode 100644 index 0000000000..b96956b8a3 --- /dev/null +++ b/internal/gatewayapi/testdata/securitypolicy-invalid-no-section-name-listener.in.yaml @@ -0,0 +1,51 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + namespace: envoy-gateway + name: gateway-1 + spec: + gatewayClassName: envoy-gateway-class + listeners: + - name: listener-1 + protocol: HTTP + port: 80 + hostname: listener-1.gateway-1.envoyproxy.io + allowedRoutes: + namespaces: + from: All +httpRoutes: +- apiVersion: gateway.networking.k8s.io/v1 + kind: HTTPRoute + metadata: + namespace: default + name: httproute-1 + spec: + hostnames: + - listener-1.gateway-1.envoyproxy.io + parentRefs: + - namespace: envoy-gateway + name: gateway-1 + sectionName: listener-1 + rules: + - matches: + - path: + value: "/foo" + backendRefs: + - name: service-1 + port: 8080 +securityPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + namespace: envoy-gateway + name: policy-for-gateway-1 + spec: + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + sectionName: not-found-section-name + cors: + allowHeaders: + - "x-gateway-1" diff --git a/internal/gatewayapi/testdata/securitypolicy-invalid-no-section-name-listener.out.yaml b/internal/gatewayapi/testdata/securitypolicy-invalid-no-section-name-listener.out.yaml new file mode 100644 index 0000000000..3f896cfafc --- /dev/null +++ b/internal/gatewayapi/testdata/securitypolicy-invalid-no-section-name-listener.out.yaml @@ -0,0 +1,187 @@ +gateways: +- apiVersion: gateway.networking.k8s.io/v1 + kind: Gateway + metadata: + creationTimestamp: null + name: gateway-1 + namespace: envoy-gateway + spec: + gatewayClassName: envoy-gateway-class + listeners: + - allowedRoutes: + namespaces: + from: All + hostname: listener-1.gateway-1.envoyproxy.io + name: listener-1 + 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: listener-1 + 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: + creationTimestamp: null + name: httproute-1 + namespace: default + spec: + hostnames: + - listener-1.gateway-1.envoyproxy.io + parentRefs: + - name: gateway-1 + namespace: envoy-gateway + sectionName: listener-1 + rules: + - backendRefs: + - name: service-1 + port: 8080 + matches: + - path: + value: /foo + 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: gateway-1 + namespace: envoy-gateway + sectionName: listener-1 +infraIR: + envoy-gateway/gateway-1: + proxy: + listeners: + - address: null + name: envoy-gateway/gateway-1/listener-1 + ports: + - containerPort: 10080 + name: http-80 + protocol: HTTP + servicePort: 80 + metadata: + labels: + gateway.envoyproxy.io/owning-gateway-name: gateway-1 + gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway + ownerReference: + kind: GatewayClass + name: envoy-gateway-class + name: envoy-gateway/gateway-1 + namespace: envoy-gateway-system +securityPolicies: +- apiVersion: gateway.envoyproxy.io/v1alpha1 + kind: SecurityPolicy + metadata: + creationTimestamp: null + name: policy-for-gateway-1 + namespace: envoy-gateway + spec: + cors: + allowHeaders: + - x-gateway-1 + targetRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + sectionName: not-found-section-name + status: + ancestors: + - ancestorRef: + group: gateway.networking.k8s.io + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + sectionName: not-found-section-name + conditions: + - lastTransitionTime: null + message: No section name not-found-section-name found for Gateway envoy-gateway/gateway-1 + reason: TargetNotFound + status: "False" + type: Accepted + controllerName: gateway.envoyproxy.io/gatewayclass-controller +xdsIR: + envoy-gateway/gateway-1: + accessLog: + json: + - path: /dev/stdout + http: + - address: 0.0.0.0 + hostnames: + - listener-1.gateway-1.envoyproxy.io + isHTTP2: false + metadata: + kind: Gateway + name: gateway-1 + namespace: envoy-gateway + sectionName: listener-1 + name: envoy-gateway/gateway-1/listener-1 + path: + escapedSlashesAction: UnescapeAndRedirect + mergeSlashes: true + port: 10080 + routes: + - destination: + metadata: + kind: HTTPRoute + name: httproute-1 + namespace: default + name: httproute/default/httproute-1/rule/0 + settings: + - addressType: IP + endpoints: + - host: 7.7.7.7 + port: 8080 + metadata: + name: service-1 + namespace: default + sectionName: "8080" + name: httproute/default/httproute-1/rule/0/backend/0 + protocol: HTTP + weight: 1 + hostname: listener-1.gateway-1.envoyproxy.io + isHTTP2: false + metadata: + kind: HTTPRoute + name: httproute-1 + namespace: default + name: httproute/default/httproute-1/rule/0/match/0/listener-1_gateway-1_envoyproxy_io + pathMatch: + distinct: false + name: "" + prefix: /foo + readyListener: + address: 0.0.0.0 + ipFamily: IPv4 + path: /ready + port: 19003 diff --git a/internal/gatewayapi/validate.go b/internal/gatewayapi/validate.go index d97ee80713..d2b72b4bfb 100644 --- a/internal/gatewayapi/validate.go +++ b/internal/gatewayapi/validate.go @@ -14,6 +14,7 @@ import ( corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/validation" gwapiv1 "sigs.k8s.io/gateway-api/apis/v1" @@ -1037,3 +1038,29 @@ func (t *Translator) validateExtServiceBackendReference( } return nil } + +// validateGatewayListenerSectionName check: +// if the section name exists in the target Gateway listeners. +func validateGatewayListenerSectionName( + sectionName gwapiv1.SectionName, + targetKey types.NamespacedName, + listeners []*ListenerContext, +) *status.PolicyResolveError { + found := false + for _, l := range listeners { + if l.Name == sectionName { + found = true + break + } + } + if !found { + message := fmt.Sprintf("No section name %s found for Gateway %s", + string(sectionName), targetKey.String()) + + return &status.PolicyResolveError{ + Reason: gwapiv1a2.PolicyReasonTargetNotFound, + Message: message, + } + } + return nil +}