From 7a04ed85fb9e47b5ee09b1b865db38849e3585f9 Mon Sep 17 00:00:00 2001 From: Joshua Reese Date: Wed, 17 Sep 2025 11:06:11 -0500 Subject: [PATCH 1/2] Update Gateway API to v1.3.0, target Envoy Gateway v1.5.0. Updated e2e test to avoid assertion failure on the `OverlappingTLSConfig` condition. Future work will be done to remove the v4 and v6 hostnames from being programmed on Gateways, which will remove this condition. --- Makefile | 21 +- .../networking.datumapis.com_httpproxies.yaml | 620 +++++++++++++++++- config/crd/gateway/kustomization.yaml | 41 +- config/dev/kustomization.yaml | 1 + config/tools/envoy-gateway/kustomization.yaml | 2 +- go.mod | 6 +- go.sum | 4 +- .../validation/gateway_validation_test.go | 2 +- test/e2e/gateway/chainsaw-test.yaml | 120 +--- 9 files changed, 688 insertions(+), 129 deletions(-) diff --git a/Makefile b/Makefile index 9cad3d38..a38c5b30 100644 --- a/Makefile +++ b/Makefile @@ -79,6 +79,8 @@ test-e2e: chainsaw echo "No Kind cluster is running. Please start a Kind cluster before running the e2e tests."; \ exit 1; \ } + $(KIND) get kubeconfig --name nso-standard > $(TMPDIR)/.kind-nso-standard.yaml + $(KIND) get kubeconfig --name nso-infra > $(TMPDIR)/.kind-nso-infra.yaml $(CHAINSAW) test ./test/e2e \ --cluster nso-standard=$(TMPDIR)/.kind-nso-standard.yaml \ --cluster nso-infra=$(TMPDIR)/.kind-nso-infra.yaml @@ -142,7 +144,10 @@ set-image-controller: manifests kustomize prepare-infra-cluster: cert-manager envoy-gateway external-dns .PHONY: prepare-e2e -prepare-e2e: chainsaw set-image-controller cert-manager envoy-gateway external-dns load-image-all deploy-e2e +prepare-e2e: chainsaw set-image-controller cert-manager load-image-all deploy-e2e + +.PHONY: prepare-dev +prepare-dev: chainsaw set-image-controller cert-manager install .PHONY: load-image-all load-image-all: load-image-operator @@ -167,12 +172,10 @@ external-dns: .PHONY: kind-standard-cluster kind-standard-cluster: kind $(KIND) create cluster --config=config/tools/kind/standard-cluster.yaml - $(KIND) get kubeconfig --name nso-standard > $(TMPDIR)/.kind-nso-standard.yaml .PHONY: kind-infra-cluster kind-infra-cluster: kind $(KIND) create cluster --config=config/tools/kind/infra-cluster.yaml - $(KIND) get kubeconfig --name nso-infra > $(TMPDIR)/.kind-nso-infra.yaml ##@ Deployment @@ -182,23 +185,23 @@ endif .PHONY: install install: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config. - $(KUSTOMIZE) build config/dev | $(KUBECTL) apply -f - + $(KUSTOMIZE) build --enable-helm config/dev | $(KUBECTL) apply -f - .PHONY: uninstall uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. - $(KUSTOMIZE) build config/crd | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f - + $(KUSTOMIZE) build --enable-helm config/crd | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f - .PHONY: deploy deploy: set-image-controller ## Deploy controller to the K8s cluster specified in ~/.kube/config. - $(KUSTOMIZE) build config/default | $(KUBECTL) apply -f - + $(KUSTOMIZE) build --enable-helm config/default | $(KUBECTL) apply -f - .PHONY: deploy-e2e deploy-e2e: set-image-controller - $(KUSTOMIZE) build config/e2e | $(KUBECTL) apply -f - + $(KUSTOMIZE) build --enable-helm config/e2e | $(KUBECTL) apply -f - .PHONY: undeploy undeploy: kustomize ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. - $(KUSTOMIZE) build config/default | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f - + $(KUSTOMIZE) build --enable-helm config/default | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f - @@ -238,7 +241,7 @@ CRDOC_VERSION ?= v0.6.4 KIND_VERSION ?= v0.27.0 # renovate: datasource=go depName=github.com/kyverno/chainsaw -CHAINSAW_VERSION ?= v0.2.12 +CHAINSAW_VERSION ?= v0.2.13 # renovate: datasource=go depName=github.com/cert-manager/cmctl/v2 CMCTL_VERSION ?= v2.1.1 diff --git a/config/crd/bases/networking.datumapis.com_httpproxies.yaml b/config/crd/bases/networking.datumapis.com_httpproxies.yaml index 68bb78aa..93f9f0e1 100644 --- a/config/crd/bases/networking.datumapis.com_httpproxies.yaml +++ b/config/crd/bases/networking.datumapis.com_httpproxies.yaml @@ -146,7 +146,296 @@ spec: examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. + + + properties: + cors: + description: |- + CORS defines a schema for a filter that responds to the + cross-origin request based on HTTP response header. + + Support: Extended + + + properties: + allowCredentials: + description: |- + AllowCredentials indicates whether the actual cross-origin request allows + to include credentials. + + The only valid value for the `Access-Control-Allow-Credentials` response + header is true (case-sensitive). + + If the credentials are not allowed in cross-origin requests, the gateway + will omit the header `Access-Control-Allow-Credentials` entirely rather + than setting its value to false. + + Support: Extended + enum: + - true + type: boolean + allowHeaders: + description: |- + AllowHeaders indicates which HTTP request headers are supported for + accessing the requested resource. + + Header names are not case sensitive. + + Multiple header names in the value of the `Access-Control-Allow-Headers` + response header are separated by a comma (","). + + When the `AllowHeaders` field is configured with one or more headers, the + gateway must return the `Access-Control-Allow-Headers` response header + which value is present in the `AllowHeaders` field. + + If any header name in the `Access-Control-Request-Headers` request header + is not included in the list of header names specified by the response + header `Access-Control-Allow-Headers`, it will present an error on the + client side. + + If any header name in the `Access-Control-Allow-Headers` response header + does not recognize by the client, it will also occur an error on the + client side. + + A wildcard indicates that the requests with all HTTP headers are allowed. + The `Access-Control-Allow-Headers` response header can only use `*` + wildcard as value when the `AllowCredentials` field is unspecified. + + When the `AllowCredentials` field is specified and `AllowHeaders` field + specified with the `*` wildcard, the gateway must specify one or more + HTTP headers in the value of the `Access-Control-Allow-Headers` response + header. The value of the header `Access-Control-Allow-Headers` is same as + the `Access-Control-Request-Headers` header provided by the client. If + the header `Access-Control-Request-Headers` is not included in the + request, the gateway will omit the `Access-Control-Allow-Headers` + response header, instead of specifying the `*` wildcard. A Gateway + implementation may choose to add implementation-specific default headers. + + Support: Extended + items: + description: |- + HTTPHeaderName is the name of an HTTP header. + + Valid values include: + + * "Authorization" + * "Set-Cookie" + + Invalid values include: + + - ":method" - ":" is an invalid character. This means that HTTP/2 pseudo + headers are not currently supported by this type. + - "/invalid" - "/ " is an invalid character + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + maxItems: 64 + type: array + x-kubernetes-list-type: set + allowMethods: + description: |- + AllowMethods indicates which HTTP methods are supported for accessing the + requested resource. + + Valid values are any method defined by RFC9110, along with the special + value `*`, which represents all HTTP methods are allowed. + + Method names are case sensitive, so these values are also case-sensitive. + (See https://www.rfc-editor.org/rfc/rfc2616#section-5.1.1) + + Multiple method names in the value of the `Access-Control-Allow-Methods` + response header are separated by a comma (","). + + A CORS-safelisted method is a method that is `GET`, `HEAD`, or `POST`. + (See https://fetch.spec.whatwg.org/#cors-safelisted-method) The + CORS-safelisted methods are always allowed, regardless of whether they + are specified in the `AllowMethods` field. + + When the `AllowMethods` field is configured with one or more methods, the + gateway must return the `Access-Control-Allow-Methods` response header + which value is present in the `AllowMethods` field. + + If the HTTP method of the `Access-Control-Request-Method` request header + is not included in the list of methods specified by the response header + `Access-Control-Allow-Methods`, it will present an error on the client + side. + + The `Access-Control-Allow-Methods` response header can only use `*` + wildcard as value when the `AllowCredentials` field is unspecified. + + When the `AllowCredentials` field is specified and `AllowMethods` field + specified with the `*` wildcard, the gateway must specify one HTTP method + in the value of the Access-Control-Allow-Methods response header. The + value of the header `Access-Control-Allow-Methods` is same as the + `Access-Control-Request-Method` header provided by the client. If the + header `Access-Control-Request-Method` is not included in the request, + the gateway will omit the `Access-Control-Allow-Methods` response header, + instead of specifying the `*` wildcard. A Gateway implementation may + choose to add implementation-specific default methods. + + Support: Extended + items: + enum: + - GET + - HEAD + - POST + - PUT + - DELETE + - CONNECT + - OPTIONS + - TRACE + - PATCH + - '*' + type: string + maxItems: 9 + type: array + x-kubernetes-list-type: set + x-kubernetes-validations: + - message: AllowMethods cannot contain '*' alongside + other methods + rule: '!(''*'' in self && self.size() > 1)' + allowOrigins: + description: |- + AllowOrigins indicates whether the response can be shared with requested + resource from the given `Origin`. + + The `Origin` consists of a scheme and a host, with an optional port, and + takes the form `://(:)`. + + Valid values for scheme are: `http` and `https`. + + Valid values for port are any integer between 1 and 65535 (the list of + available TCP/UDP ports). Note that, if not included, port `80` is + assumed for `http` scheme origins, and port `443` is assumed for `https` + origins. This may affect origin matching. + + The host part of the origin may contain the wildcard character `*`. These + wildcard characters behave as follows: + + * `*` is a greedy match to the _left_, including any number of + DNS labels to the left of its position. This also means that + `*` will include any number of period `.` characters to the + left of its position. + * A wildcard by itself matches all hosts. + + An origin value that includes _only_ the `*` character indicates requests + from all `Origin`s are allowed. + + When the `AllowOrigins` field is configured with multiple origins, it + means the server supports clients from multiple origins. If the request + `Origin` matches the configured allowed origins, the gateway must return + the given `Origin` and sets value of the header + `Access-Control-Allow-Origin` same as the `Origin` header provided by the + client. + + The status code of a successful response to a "preflight" request is + always an OK status (i.e., 204 or 200). + + If the request `Origin` does not match the configured allowed origins, + the gateway returns 204/200 response but doesn't set the relevant + cross-origin response headers. Alternatively, the gateway responds with + 403 status to the "preflight" request is denied, coupled with omitting + the CORS headers. The cross-origin request fails on the client side. + Therefore, the client doesn't attempt the actual cross-origin request. + + The `Access-Control-Allow-Origin` response header can only use `*` + wildcard as value when the `AllowCredentials` field is unspecified. + + When the `AllowCredentials` field is specified and `AllowOrigins` field + specified with the `*` wildcard, the gateway must return a single origin + in the value of the `Access-Control-Allow-Origin` response header, + instead of specifying the `*` wildcard. The value of the header + `Access-Control-Allow-Origin` is same as the `Origin` header provided by + the client. + + Support: Extended + items: + description: |- + The AbsoluteURI MUST NOT be a relative URI, and it MUST follow the URI syntax and + encoding rules specified in RFC3986. The AbsoluteURI MUST include both a + scheme (e.g., "http" or "spiffe") and a scheme-specific-part. URIs that + include an authority MUST include a fully qualified domain name or + IP address as the host. + The below regex is taken from the regex section in RFC 3986 with a slight modification to enforce a full URI and not relative. + maxLength: 253 + minLength: 1 + pattern: ^(([^:/?#]+):)(//([^/?#]*))([^?#]*)(\?([^#]*))?(#(.*))? + type: string + maxItems: 64 + type: array + x-kubernetes-list-type: set + exposeHeaders: + description: |- + ExposeHeaders indicates which HTTP response headers can be exposed + to client-side scripts in response to a cross-origin request. + + A CORS-safelisted response header is an HTTP header in a CORS response + that it is considered safe to expose to the client scripts. + The CORS-safelisted response headers include the following headers: + `Cache-Control` + `Content-Language` + `Content-Length` + `Content-Type` + `Expires` + `Last-Modified` + `Pragma` + (See https://fetch.spec.whatwg.org/#cors-safelisted-response-header-name) + The CORS-safelisted response headers are exposed to client by default. + + When an HTTP header name is specified using the `ExposeHeaders` field, + this additional header will be exposed as part of the response to the + client. + + Header names are not case sensitive. + + Multiple header names in the value of the `Access-Control-Expose-Headers` + response header are separated by a comma (","). + + A wildcard indicates that the responses with all HTTP headers are exposed + to clients. The `Access-Control-Expose-Headers` response header can only + use `*` wildcard as value when the `AllowCredentials` field is + unspecified. + + Support: Extended + items: + description: |- + HTTPHeaderName is the name of an HTTP header. + + Valid values include: + + * "Authorization" + * "Set-Cookie" + + Invalid values include: + + - ":method" - ":" is an invalid character. This means that HTTP/2 pseudo + headers are not currently supported by this type. + - "/invalid" - "/ " is an invalid character + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + maxItems: 64 + type: array + x-kubernetes-list-type: set + maxAge: + default: 5 + description: |- + MaxAge indicates the duration (in seconds) for the client to cache the + results of a "preflight" request. + + The information provided by the `Access-Control-Allow-Methods` and + `Access-Control-Allow-Headers` response headers can be cached by the + client until the time specified by `Access-Control-Max-Age` elapses. + + The default value of `Access-Control-Max-Age` response header is 5 + (seconds). + format: int32 + minimum: 1 + type: integer + type: object extensionRef: description: |- ExtensionRef is an optional, implementation-specific extension to the @@ -215,7 +504,7 @@ spec: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries @@ -290,7 +579,7 @@ spec: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries @@ -328,8 +617,6 @@ spec: backends. Support: Extended - - properties: backendRef: description: |- @@ -431,8 +718,6 @@ spec: Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. - - properties: denominator: default: 100 @@ -458,8 +743,6 @@ spec: Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. - - format: int32 maximum: 100 minimum: 0 @@ -467,6 +750,10 @@ spec: required: - backendRef type: object + x-kubernetes-validations: + - message: Only one of percent or fraction may be + specified in HTTPRequestMirrorFilter + rule: '!(has(self.percent) && has(self.fraction))' requestRedirect: description: |- RequestRedirect defines a schema for a filter that responds to the @@ -653,7 +940,7 @@ spec: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries @@ -728,7 +1015,7 @@ spec: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries @@ -789,6 +1076,8 @@ spec: Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. + + enum: - RequestHeaderModifier - ResponseHeaderModifier @@ -968,7 +1257,296 @@ spec: examples include request or response modification, implementing authentication strategies, rate-limiting, and traffic shaping. API guarantee/conformance is defined based on the type of the filter. + + + properties: + cors: + description: |- + CORS defines a schema for a filter that responds to the + cross-origin request based on HTTP response header. + + Support: Extended + + + properties: + allowCredentials: + description: |- + AllowCredentials indicates whether the actual cross-origin request allows + to include credentials. + + The only valid value for the `Access-Control-Allow-Credentials` response + header is true (case-sensitive). + + If the credentials are not allowed in cross-origin requests, the gateway + will omit the header `Access-Control-Allow-Credentials` entirely rather + than setting its value to false. + + Support: Extended + enum: + - true + type: boolean + allowHeaders: + description: |- + AllowHeaders indicates which HTTP request headers are supported for + accessing the requested resource. + + Header names are not case sensitive. + + Multiple header names in the value of the `Access-Control-Allow-Headers` + response header are separated by a comma (","). + + When the `AllowHeaders` field is configured with one or more headers, the + gateway must return the `Access-Control-Allow-Headers` response header + which value is present in the `AllowHeaders` field. + + If any header name in the `Access-Control-Request-Headers` request header + is not included in the list of header names specified by the response + header `Access-Control-Allow-Headers`, it will present an error on the + client side. + + If any header name in the `Access-Control-Allow-Headers` response header + does not recognize by the client, it will also occur an error on the + client side. + + A wildcard indicates that the requests with all HTTP headers are allowed. + The `Access-Control-Allow-Headers` response header can only use `*` + wildcard as value when the `AllowCredentials` field is unspecified. + + When the `AllowCredentials` field is specified and `AllowHeaders` field + specified with the `*` wildcard, the gateway must specify one or more + HTTP headers in the value of the `Access-Control-Allow-Headers` response + header. The value of the header `Access-Control-Allow-Headers` is same as + the `Access-Control-Request-Headers` header provided by the client. If + the header `Access-Control-Request-Headers` is not included in the + request, the gateway will omit the `Access-Control-Allow-Headers` + response header, instead of specifying the `*` wildcard. A Gateway + implementation may choose to add implementation-specific default headers. + + Support: Extended + items: + description: |- + HTTPHeaderName is the name of an HTTP header. + + Valid values include: + + * "Authorization" + * "Set-Cookie" + + Invalid values include: + + - ":method" - ":" is an invalid character. This means that HTTP/2 pseudo + headers are not currently supported by this type. + - "/invalid" - "/ " is an invalid character + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + maxItems: 64 + type: array + x-kubernetes-list-type: set + allowMethods: + description: |- + AllowMethods indicates which HTTP methods are supported for accessing the + requested resource. + + Valid values are any method defined by RFC9110, along with the special + value `*`, which represents all HTTP methods are allowed. + + Method names are case sensitive, so these values are also case-sensitive. + (See https://www.rfc-editor.org/rfc/rfc2616#section-5.1.1) + + Multiple method names in the value of the `Access-Control-Allow-Methods` + response header are separated by a comma (","). + + A CORS-safelisted method is a method that is `GET`, `HEAD`, or `POST`. + (See https://fetch.spec.whatwg.org/#cors-safelisted-method) The + CORS-safelisted methods are always allowed, regardless of whether they + are specified in the `AllowMethods` field. + + When the `AllowMethods` field is configured with one or more methods, the + gateway must return the `Access-Control-Allow-Methods` response header + which value is present in the `AllowMethods` field. + + If the HTTP method of the `Access-Control-Request-Method` request header + is not included in the list of methods specified by the response header + `Access-Control-Allow-Methods`, it will present an error on the client + side. + + The `Access-Control-Allow-Methods` response header can only use `*` + wildcard as value when the `AllowCredentials` field is unspecified. + + When the `AllowCredentials` field is specified and `AllowMethods` field + specified with the `*` wildcard, the gateway must specify one HTTP method + in the value of the Access-Control-Allow-Methods response header. The + value of the header `Access-Control-Allow-Methods` is same as the + `Access-Control-Request-Method` header provided by the client. If the + header `Access-Control-Request-Method` is not included in the request, + the gateway will omit the `Access-Control-Allow-Methods` response header, + instead of specifying the `*` wildcard. A Gateway implementation may + choose to add implementation-specific default methods. + + Support: Extended + items: + enum: + - GET + - HEAD + - POST + - PUT + - DELETE + - CONNECT + - OPTIONS + - TRACE + - PATCH + - '*' + type: string + maxItems: 9 + type: array + x-kubernetes-list-type: set + x-kubernetes-validations: + - message: AllowMethods cannot contain '*' alongside + other methods + rule: '!(''*'' in self && self.size() > 1)' + allowOrigins: + description: |- + AllowOrigins indicates whether the response can be shared with requested + resource from the given `Origin`. + + The `Origin` consists of a scheme and a host, with an optional port, and + takes the form `://(:)`. + + Valid values for scheme are: `http` and `https`. + + Valid values for port are any integer between 1 and 65535 (the list of + available TCP/UDP ports). Note that, if not included, port `80` is + assumed for `http` scheme origins, and port `443` is assumed for `https` + origins. This may affect origin matching. + + The host part of the origin may contain the wildcard character `*`. These + wildcard characters behave as follows: + + * `*` is a greedy match to the _left_, including any number of + DNS labels to the left of its position. This also means that + `*` will include any number of period `.` characters to the + left of its position. + * A wildcard by itself matches all hosts. + + An origin value that includes _only_ the `*` character indicates requests + from all `Origin`s are allowed. + + When the `AllowOrigins` field is configured with multiple origins, it + means the server supports clients from multiple origins. If the request + `Origin` matches the configured allowed origins, the gateway must return + the given `Origin` and sets value of the header + `Access-Control-Allow-Origin` same as the `Origin` header provided by the + client. + + The status code of a successful response to a "preflight" request is + always an OK status (i.e., 204 or 200). + + If the request `Origin` does not match the configured allowed origins, + the gateway returns 204/200 response but doesn't set the relevant + cross-origin response headers. Alternatively, the gateway responds with + 403 status to the "preflight" request is denied, coupled with omitting + the CORS headers. The cross-origin request fails on the client side. + Therefore, the client doesn't attempt the actual cross-origin request. + + The `Access-Control-Allow-Origin` response header can only use `*` + wildcard as value when the `AllowCredentials` field is unspecified. + + When the `AllowCredentials` field is specified and `AllowOrigins` field + specified with the `*` wildcard, the gateway must return a single origin + in the value of the `Access-Control-Allow-Origin` response header, + instead of specifying the `*` wildcard. The value of the header + `Access-Control-Allow-Origin` is same as the `Origin` header provided by + the client. + + Support: Extended + items: + description: |- + The AbsoluteURI MUST NOT be a relative URI, and it MUST follow the URI syntax and + encoding rules specified in RFC3986. The AbsoluteURI MUST include both a + scheme (e.g., "http" or "spiffe") and a scheme-specific-part. URIs that + include an authority MUST include a fully qualified domain name or + IP address as the host. + The below regex is taken from the regex section in RFC 3986 with a slight modification to enforce a full URI and not relative. + maxLength: 253 + minLength: 1 + pattern: ^(([^:/?#]+):)(//([^/?#]*))([^?#]*)(\?([^#]*))?(#(.*))? + type: string + maxItems: 64 + type: array + x-kubernetes-list-type: set + exposeHeaders: + description: |- + ExposeHeaders indicates which HTTP response headers can be exposed + to client-side scripts in response to a cross-origin request. + + A CORS-safelisted response header is an HTTP header in a CORS response + that it is considered safe to expose to the client scripts. + The CORS-safelisted response headers include the following headers: + `Cache-Control` + `Content-Language` + `Content-Length` + `Content-Type` + `Expires` + `Last-Modified` + `Pragma` + (See https://fetch.spec.whatwg.org/#cors-safelisted-response-header-name) + The CORS-safelisted response headers are exposed to client by default. + + When an HTTP header name is specified using the `ExposeHeaders` field, + this additional header will be exposed as part of the response to the + client. + + Header names are not case sensitive. + + Multiple header names in the value of the `Access-Control-Expose-Headers` + response header are separated by a comma (","). + + A wildcard indicates that the responses with all HTTP headers are exposed + to clients. The `Access-Control-Expose-Headers` response header can only + use `*` wildcard as value when the `AllowCredentials` field is + unspecified. + + Support: Extended + items: + description: |- + HTTPHeaderName is the name of an HTTP header. + + Valid values include: + + * "Authorization" + * "Set-Cookie" + + Invalid values include: + + - ":method" - ":" is an invalid character. This means that HTTP/2 pseudo + headers are not currently supported by this type. + - "/invalid" - "/ " is an invalid character + maxLength: 256 + minLength: 1 + pattern: ^[A-Za-z0-9!#$%&'*+\-.^_\x60|~]+$ + type: string + maxItems: 64 + type: array + x-kubernetes-list-type: set + maxAge: + default: 5 + description: |- + MaxAge indicates the duration (in seconds) for the client to cache the + results of a "preflight" request. + + The information provided by the `Access-Control-Allow-Methods` and + `Access-Control-Allow-Headers` response headers can be cached by the + client until the time specified by `Access-Control-Max-Age` elapses. + + The default value of `Access-Control-Max-Age` response header is 5 + (seconds). + format: int32 + minimum: 1 + type: integer + type: object extensionRef: description: |- ExtensionRef is an optional, implementation-specific extension to the @@ -1036,7 +1614,7 @@ spec: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries @@ -1110,7 +1688,7 @@ spec: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries @@ -1148,8 +1726,6 @@ spec: backends. Support: Extended - - properties: backendRef: description: |- @@ -1251,8 +1827,6 @@ spec: Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. - - properties: denominator: default: 100 @@ -1278,8 +1852,6 @@ spec: Only one of Fraction or Percent may be specified. If neither field is specified, 100% of requests will be mirrored. - - format: int32 maximum: 100 minimum: 0 @@ -1287,6 +1859,10 @@ spec: required: - backendRef type: object + x-kubernetes-validations: + - message: Only one of percent or fraction may be specified + in HTTPRequestMirrorFilter + rule: '!(has(self.percent) && has(self.fraction))' requestRedirect: description: |- RequestRedirect defines a schema for a filter that responds to the @@ -1472,7 +2048,7 @@ spec: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries @@ -1546,7 +2122,7 @@ spec: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, the first entry with an equivalent name MUST be considered for a match. Subsequent entries @@ -1607,6 +2183,8 @@ spec: Unknown values here must result in the implementation setting the Accepted Condition for the Route to `status: False`, with a Reason of `UnsupportedValue`. + + enum: - RequestHeaderModifier - ResponseHeaderModifier @@ -1796,7 +2374,7 @@ spec: name: description: |- Name is the name of the HTTP Header to be matched. Name matching MUST be - case insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). + case-insensitive. (See https://tools.ietf.org/html/rfc7230#section-3.2). If multiple entries specify equivalent header names, only the first entry with an equivalent name MUST be considered for a match. Subsequent diff --git a/config/crd/gateway/kustomization.yaml b/config/crd/gateway/kustomization.yaml index ce62edd1..4c5b6a12 100644 --- a/config/crd/gateway/kustomization.yaml +++ b/config/crd/gateway/kustomization.yaml @@ -1,20 +1,53 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization -resources: - - github.com/kubernetes-sigs/gateway-api/config/crd/?ref=v1.2.1 + +helmCharts: +- name: gateway-crds-helm + repo: oci://docker.io/envoyproxy + # IMPORTANT - When upgrading the helm chart, ensure there's no undesired CRDs + # added in the new version. If there are, remove them in the patches below. + version: v1.5.0 + releaseName: gateway + namespace: kube-system + valuesInline: + crds: + gatewayAPI: + enabled: true + channel: standard + envoyGateway: + enabled: true patches: - # Drop GRPCRoutes for now + # Drop resources that shouldn't be added to upstream control planes yet + # Gateway API resources - patch: | $patch: delete apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: grpcroutes.gateway.networking.k8s.io - # Drop ReferenceGrants for now - patch: | $patch: delete apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: name: referencegrants.gateway.networking.k8s.io + # Envoy gateway resources + - patch: | + $patch: delete + apiVersion: apiextensions.k8s.io/v1 + kind: CustomResourceDefinition + metadata: + name: envoyextensionpolicies.gateway.envoyproxy.io + - patch: | + $patch: delete + apiVersion: apiextensions.k8s.io/v1 + kind: CustomResourceDefinition + metadata: + name: envoypatchpolicies.gateway.envoyproxy.io + - patch: | + $patch: delete + apiVersion: apiextensions.k8s.io/v1 + kind: CustomResourceDefinition + metadata: + name: envoyproxies.gateway.envoyproxy.io diff --git a/config/dev/kustomization.yaml b/config/dev/kustomization.yaml index 33b3de79..adfe85cb 100644 --- a/config/dev/kustomization.yaml +++ b/config/dev/kustomization.yaml @@ -3,6 +3,7 @@ namePrefix: network-services-operator- resources: - ../crd + - ../crd/gateway - ../webhook - ../certmanager diff --git a/config/tools/envoy-gateway/kustomization.yaml b/config/tools/envoy-gateway/kustomization.yaml index bfb95ca2..4d6b2ac7 100644 --- a/config/tools/envoy-gateway/kustomization.yaml +++ b/config/tools/envoy-gateway/kustomization.yaml @@ -5,5 +5,5 @@ helmCharts: includeCRDs: true namespace: envoy-gateway-system releaseName: envoy-gateway - version: v1.3.2 + version: v1.5.0 repo: oci://docker.io/envoyproxy diff --git a/go.mod b/go.mod index d6087eb1..4e2f7df7 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,9 @@ go 1.24.0 toolchain go1.24.2 require ( + github.com/go-logr/logr v1.4.3 github.com/google/go-cmp v0.7.0 + github.com/google/uuid v1.6.0 github.com/onsi/ginkgo/v2 v2.23.4 github.com/stretchr/testify v1.10.0 go.miloapis.com/milo v0.1.0 @@ -16,7 +18,7 @@ require ( k8s.io/client-go v0.33.1 k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 sigs.k8s.io/controller-runtime v0.21.0 - sigs.k8s.io/gateway-api v1.2.1 + sigs.k8s.io/gateway-api v1.3.0 sigs.k8s.io/multicluster-runtime v0.21.0-alpha.8 ) @@ -34,7 +36,6 @@ require ( github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/fxamacker/cbor/v2 v2.8.0 // indirect - github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/zapr v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.21.1 // indirect @@ -46,7 +47,6 @@ require ( github.com/google/cel-go v0.25.0 // indirect github.com/google/gnostic-models v0.6.9 // indirect github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect - github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect diff --git a/go.sum b/go.sum index b7e34b4a..debede4c 100644 --- a/go.sum +++ b/go.sum @@ -252,8 +252,8 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 h1:jpcvIRr3GLoUo sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= sigs.k8s.io/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8= sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM= -sigs.k8s.io/gateway-api v1.2.1 h1:fZZ/+RyRb+Y5tGkwxFKuYuSRQHu9dZtbjenblleOLHM= -sigs.k8s.io/gateway-api v1.2.1/go.mod h1:EpNfEXNjiYfUJypf0eZ0P5iXA9ekSGWaS1WgPaM42X0= +sigs.k8s.io/gateway-api v1.3.0 h1:q6okN+/UKDATola4JY7zXzx40WO4VISk7i9DIfOvr9M= +sigs.k8s.io/gateway-api v1.3.0/go.mod h1:d8NV8nJbaRbEKem+5IuxkL8gJGOZ+FJ+NvOIltV8gDk= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= sigs.k8s.io/multicluster-runtime v0.21.0-alpha.8 h1:Pq69tTKfN8ADw8m8A3wUtP8wJ9SPQbbOsgapm3BZEPw= diff --git a/internal/validation/gateway_validation_test.go b/internal/validation/gateway_validation_test.go index 1f66a6f1..389a011d 100644 --- a/internal/validation/gateway_validation_test.go +++ b/internal/validation/gateway_validation_test.go @@ -197,7 +197,7 @@ func TestValidateGateway(t *testing.T) { Port: 80, }, }, - Addresses: []gatewayv1.GatewayAddress{ + Addresses: []gatewayv1.GatewaySpecAddress{ { Type: ptr.To(gatewayv1.IPAddressType), Value: "192.168.1.1", diff --git a/test/e2e/gateway/chainsaw-test.yaml b/test/e2e/gateway/chainsaw-test.yaml index 0c3b46a1..ec334d09 100644 --- a/test/e2e/gateway/chainsaw-test.yaml +++ b/test/e2e/gateway/chainsaw-test.yaml @@ -430,101 +430,45 @@ spec: status: "True" type: Programmed listeners: - - attachedRoutes: 0 - conditions: - - reason: Programmed - status: "True" - type: Programmed - - reason: Accepted - status: "True" - type: Accepted - - reason: ResolvedRefs - status: "True" - type: ResolvedRefs + - (conditions[?contains(['Programmed', 'Accepted', 'ResolvedRefs'], type)]): + - status: "True" + - status: "True" + - status: "True" name: http-test-e2e - - attachedRoutes: 0 - conditions: - - reason: Programmed - status: "True" - type: Programmed - - reason: Accepted - status: "True" - type: Accepted - - reason: ResolvedRefs - status: "True" - type: ResolvedRefs + - (conditions[?contains(['Programmed', 'Accepted', 'ResolvedRefs'], type)]): + - status: "True" + - status: "True" + - status: "True" name: https-test-e2e - - attachedRoutes: 0 - conditions: - - reason: Programmed - status: "True" - type: Programmed - - reason: Accepted - status: "True" - type: Accepted - - reason: ResolvedRefs - status: "True" - type: ResolvedRefs + - (conditions[?contains(['Programmed', 'Accepted', 'ResolvedRefs'], type)]): + - status: "True" + - status: "True" + - status: "True" name: http-0 - - attachedRoutes: 0 - conditions: - - reason: Programmed - status: "True" - type: Programmed - - reason: Accepted - status: "True" - type: Accepted - - reason: ResolvedRefs - status: "True" - type: ResolvedRefs + - (conditions[?contains(['Programmed', 'Accepted', 'ResolvedRefs'], type)]): + - status: "True" + - status: "True" + - status: "True" name: https-0 - - attachedRoutes: 0 - conditions: - - reason: Programmed - status: "True" - type: Programmed - - reason: Accepted - status: "True" - type: Accepted - - reason: ResolvedRefs - status: "True" - type: ResolvedRefs + - (conditions[?contains(['Programmed', 'Accepted', 'ResolvedRefs'], type)]): + - status: "True" + - status: "True" + - status: "True" name: http-1 - - attachedRoutes: 0 - conditions: - - reason: Programmed - status: "True" - type: Programmed - - reason: Accepted - status: "True" - type: Accepted - - reason: ResolvedRefs - status: "True" - type: ResolvedRefs + - (conditions[?contains(['Programmed', 'Accepted', 'ResolvedRefs'], type)]): + - status: "True" + - status: "True" + - status: "True" name: https-1 - - attachedRoutes: 0 - conditions: - - reason: Programmed - status: "True" - type: Programmed - - reason: Accepted - status: "True" - type: Accepted - - reason: ResolvedRefs - status: "True" - type: ResolvedRefs + - (conditions[?contains(['Programmed', 'Accepted', 'ResolvedRefs'], type)]): + - status: "True" + - status: "True" + - status: "True" name: http-2 - - attachedRoutes: 0 - conditions: - - reason: Programmed - status: "True" - type: Programmed - - reason: Accepted - status: "True" - type: Accepted - - reason: ResolvedRefs - status: "True" - type: ResolvedRefs + - (conditions[?contains(['Programmed', 'Accepted', 'ResolvedRefs'], type)]): + - status: "True" + - status: "True" + - status: "True" name: https-2 # Load the downstream gateway so we can get the IP address info from its From 7fb934bc19ec5acdf522adbd514db0f09a6f0a10 Mon Sep 17 00:00:00 2001 From: Joshua Reese Date: Fri, 19 Sep 2025 09:54:05 -0500 Subject: [PATCH 2/2] Do not register ClientTrafficPolicy with control plane. Due to limitations in how Envoy Gateway programs listeners, a ClientTrafficPolicy cannot be applied to non-TLS listeners with overlapping ports. Application to TLS listeners works without issue, however the inconsistency is undesirable. Future TLS settings can be provided via the Gateway Listener's TLS configuration. Header manipulation, while not "early", can be accomplished today via `RequestHeaderModifier` filters. Timeouts can also be defined in existing route rules. Remaining features are mostly around tuning, where there can be generous defaults set on the platform. --- config/crd/gateway/kustomization.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/config/crd/gateway/kustomization.yaml b/config/crd/gateway/kustomization.yaml index 4c5b6a12..25e06a46 100644 --- a/config/crd/gateway/kustomization.yaml +++ b/config/crd/gateway/kustomization.yaml @@ -51,3 +51,9 @@ patches: kind: CustomResourceDefinition metadata: name: envoyproxies.gateway.envoyproxy.io + - patch: | + $patch: delete + apiVersion: apiextensions.k8s.io/v1 + kind: CustomResourceDefinition + metadata: + name: clienttrafficpolicies.gateway.envoyproxy.io