diff --git a/api/v1alpha1/httproutefilter_types.go b/api/v1alpha1/httproutefilter_types.go index e0e4755f214..fcd708a5275 100644 --- a/api/v1alpha1/httproutefilter_types.go +++ b/api/v1alpha1/httproutefilter_types.go @@ -37,6 +37,13 @@ type HTTPRouteFilterSpec struct { // HTTPURLRewriteFilter define rewrites of HTTP URL components such as path and host type HTTPURLRewriteFilter struct { + // Hostname is the value to be used to replace the Host header value during + // forwarding. + // + // Support: Extended + // + // +optional + Hostname *HTTPHostnameModifier `json:"hostname,omitempty"` // Path defines a path rewrite. // // +optional @@ -53,6 +60,14 @@ const ( RegexHTTPPathModifier HTTPPathModifierType = "ReplaceRegexMatch" ) +// HTTPPathModifierType defines the type of Hostname rewrite. +type HTTPHostnameModifierType string + +const ( + HeaderHTTPHostnameModifier HTTPHostnameModifierType = "SetFromHeader" + BackendHTTPHostnameModifier HTTPHostnameModifierType = "SetFromBackend" +) + type ReplaceRegexMatch struct { // Pattern matches a regular expression against the value of the HTTP Path.The regex string must // adhere to the syntax documented in https://github.com/google/re2/wiki/Syntax. @@ -91,6 +106,26 @@ type HTTPPathModifier struct { ReplaceRegexMatch *ReplaceRegexMatch `json:"replaceRegexMatch,omitempty"` } +// +kubebuilder:validation:XValidation:message="setFromHeader must be nil if the type is not SetFromHeader",rule="!(has(self.setFromHeader) && self.type != 'SetFromHeader')" +// +kubebuilder:validation:XValidation:message="setFromHeader must be specified for SetFromHeader type",rule="!(!has(self.setFromHeader) && self.type == 'SetFromHeader')" +// +kubebuilder:validation:XValidation:message="setFromBackend must be nil if the type is not SetFromBackend",rule="!(has(self.setFromBackend) && self.type != 'SetFromBackend')" +// +kubebuilder:validation:XValidation:message="setFromBackend must be specified for SetFromBackend type",rule="!(!has(self.setFromBackend) && self.type == 'SetFromBackend')" +type HTTPHostnameModifier struct { + // +kubebuilder:validation:Enum=SetFromHeader;SetFromBackend + // +kubebuilder:validation:Required + Type HTTPHostnameModifierType `json:"type"` + + // SetFromHeader indicates that the Host header value would be replaced with the value of the header specified in setFromHeader. + // https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-field-config-route-v3-routeaction-host-rewrite-header + // +optional + SetFromHeader *string `json:"setFromHeader,omitempty"` + + // SetFromBackend indicates that the Host header value would be replaced by the DNS name of the backend if it exists. + // https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-field-config-route-v3-routeaction-auto-host-rewrite + // +optional + SetFromBackend *bool `json:"setFromBackend,omitempty"` +} + //+kubebuilder:object:root=true // HTTPRouteFilterList contains a list of HTTPRouteFilter resources. diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index b6b944b3a14..9af23c7970b 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -2709,6 +2709,31 @@ func (in *HTTPExtAuthService) DeepCopy() *HTTPExtAuthService { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPHostnameModifier) DeepCopyInto(out *HTTPHostnameModifier) { + *out = *in + if in.SetFromHeader != nil { + in, out := &in.SetFromHeader, &out.SetFromHeader + *out = new(string) + **out = **in + } + if in.SetFromBackend != nil { + in, out := &in.SetFromBackend, &out.SetFromBackend + *out = new(bool) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPHostnameModifier. +func (in *HTTPHostnameModifier) DeepCopy() *HTTPHostnameModifier { + if in == nil { + return nil + } + out := new(HTTPHostnameModifier) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *HTTPPathModifier) DeepCopyInto(out *HTTPPathModifier) { *out = *in @@ -2835,6 +2860,11 @@ func (in *HTTPTimeout) DeepCopy() *HTTPTimeout { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *HTTPURLRewriteFilter) DeepCopyInto(out *HTTPURLRewriteFilter) { *out = *in + if in.Hostname != nil { + in, out := &in.Hostname, &out.Hostname + *out = new(HTTPHostnameModifier) + (*in).DeepCopyInto(*out) + } if in.Path != nil { in, out := &in.Path, &out.Path *out = new(HTTPPathModifier) diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_httproutefilters.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_httproutefilters.yaml index 11447cc470e..b3b6ef171af 100644 --- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_httproutefilters.yaml +++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_httproutefilters.yaml @@ -53,6 +53,43 @@ spec: description: HTTPURLRewriteFilter define rewrites of HTTP URL components such as path and host properties: + hostname: + description: |- + Hostname is the value to be used to replace the Host header value during + forwarding. + + Support: Extended + properties: + setFromBackend: + description: |- + SetFromBackend indicates that the Host header value would be replaced by the DNS name of the backend if it exists. + https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-field-config-route-v3-routeaction-auto-host-rewrite + type: boolean + setFromHeader: + description: |- + SetFromHeader indicates that the Host header value would be replaced with the value of the header specified in setFromHeader. + https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-field-config-route-v3-routeaction-host-rewrite-header + type: string + type: + description: HTTPPathModifierType defines the type of Hostname + rewrite. + enum: + - SetFromHeader + - SetFromBackend + type: string + required: + - type + type: object + x-kubernetes-validations: + - message: setFromHeader must be nil if the type is not SetFromHeader + rule: '!(has(self.setFromHeader) && self.type != ''SetFromHeader'')' + - message: setFromHeader must be specified for SetFromHeader type + rule: '!(!has(self.setFromHeader) && self.type == ''SetFromHeader'')' + - message: setFromBackend must be nil if the type is not SetFromBackend + rule: '!(has(self.setFromBackend) && self.type != ''SetFromBackend'')' + - message: setFromBackend must be specified for SetFromBackend + type + rule: '!(!has(self.setFromBackend) && self.type == ''SetFromBackend'')' path: description: Path defines a path rewrite. properties: diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md index 193e698722c..77b25ec8f8b 100644 --- a/site/content/en/latest/api/extension_types.md +++ b/site/content/en/latest/api/extension_types.md @@ -1938,6 +1938,37 @@ _Appears in:_ | `headersToBackend` | _string array_ | false | HeadersToBackend are the authorization response headers that will be added
to the original client request before sending it to the backend server.
Note that coexisting headers will be overridden.
If not specified, no authorization response headers will be added to the
original client request. | +#### HTTPHostnameModifier + + + + + +_Appears in:_ +- [HTTPURLRewriteFilter](#httpurlrewritefilter) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `type` | _[HTTPHostnameModifierType](#httphostnamemodifiertype)_ | true | | +| `setFromHeader` | _string_ | false | SetFromHeader indicates that the Host header value would be replaced with the value of the header specified in setFromHeader.
https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-field-config-route-v3-routeaction-host-rewrite-header | +| `setFromBackend` | _boolean_ | false | SetFromBackend indicates that the Host header value would be replaced by the DNS name of the backend if it exists.
https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-field-config-route-v3-routeaction-auto-host-rewrite | + + +#### HTTPHostnameModifierType + +_Underlying type:_ _string_ + +HTTPPathModifierType defines the type of Hostname rewrite. + +_Appears in:_ +- [HTTPHostnameModifier](#httphostnamemodifier) + +| Value | Description | +| ----- | ----------- | +| `SetFromHeader` | | +| `SetFromBackend` | | + + #### HTTPPathModifier @@ -2053,6 +2084,7 @@ _Appears in:_ | Field | Type | Required | Description | | --- | --- | --- | --- | +| `hostname` | _[HTTPHostnameModifier](#httphostnamemodifier)_ | false | Hostname is the value to be used to replace the Host header value during
forwarding.

Support: Extended | | `path` | _[HTTPPathModifier](#httppathmodifier)_ | false | Path defines a path rewrite. | diff --git a/site/content/zh/latest/api/extension_types.md b/site/content/zh/latest/api/extension_types.md index 193e698722c..77b25ec8f8b 100644 --- a/site/content/zh/latest/api/extension_types.md +++ b/site/content/zh/latest/api/extension_types.md @@ -1938,6 +1938,37 @@ _Appears in:_ | `headersToBackend` | _string array_ | false | HeadersToBackend are the authorization response headers that will be added
to the original client request before sending it to the backend server.
Note that coexisting headers will be overridden.
If not specified, no authorization response headers will be added to the
original client request. | +#### HTTPHostnameModifier + + + + + +_Appears in:_ +- [HTTPURLRewriteFilter](#httpurlrewritefilter) + +| Field | Type | Required | Description | +| --- | --- | --- | --- | +| `type` | _[HTTPHostnameModifierType](#httphostnamemodifiertype)_ | true | | +| `setFromHeader` | _string_ | false | SetFromHeader indicates that the Host header value would be replaced with the value of the header specified in setFromHeader.
https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-field-config-route-v3-routeaction-host-rewrite-header | +| `setFromBackend` | _boolean_ | false | SetFromBackend indicates that the Host header value would be replaced by the DNS name of the backend if it exists.
https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto#envoy-v3-api-field-config-route-v3-routeaction-auto-host-rewrite | + + +#### HTTPHostnameModifierType + +_Underlying type:_ _string_ + +HTTPPathModifierType defines the type of Hostname rewrite. + +_Appears in:_ +- [HTTPHostnameModifier](#httphostnamemodifier) + +| Value | Description | +| ----- | ----------- | +| `SetFromHeader` | | +| `SetFromBackend` | | + + #### HTTPPathModifier @@ -2053,6 +2084,7 @@ _Appears in:_ | Field | Type | Required | Description | | --- | --- | --- | --- | +| `hostname` | _[HTTPHostnameModifier](#httphostnamemodifier)_ | false | Hostname is the value to be used to replace the Host header value during
forwarding.

Support: Extended | | `path` | _[HTTPPathModifier](#httppathmodifier)_ | false | Path defines a path rewrite. | diff --git a/test/cel-validation/httproutefilter_test.go b/test/cel-validation/httproutefilter_test.go index e9ebf380fb6..10051a216a4 100644 --- a/test/cel-validation/httproutefilter_test.go +++ b/test/cel-validation/httproutefilter_test.go @@ -16,6 +16,7 @@ import ( "time" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/utils/ptr" egv1a1 "github.com/envoyproxy/gateway/api/v1alpha1" ) @@ -85,6 +86,88 @@ func TestHTTPRouteFilter(t *testing.T) { "spec.urlRewrite.path.replaceRegexMatch.pattern: Invalid value: \"\": spec.urlRewrite.path.replaceRegexMatch.pattern in body should be at least 1 chars long", }, }, + { + desc: "Valid SetFromHeader", + mutate: func(httproutefilter *egv1a1.HTTPRouteFilter) { + httproutefilter.Spec = egv1a1.HTTPRouteFilterSpec{ + URLRewrite: &egv1a1.HTTPURLRewriteFilter{ + Hostname: &egv1a1.HTTPHostnameModifier{ + Type: egv1a1.HeaderHTTPHostnameModifier, + SetFromHeader: ptr.To("foo"), + }, + }, + } + }, + wantErrors: []string{}, + }, + { + desc: "Valid SetFromBackend", + mutate: func(httproutefilter *egv1a1.HTTPRouteFilter) { + httproutefilter.Spec = egv1a1.HTTPRouteFilterSpec{ + URLRewrite: &egv1a1.HTTPURLRewriteFilter{ + Hostname: &egv1a1.HTTPHostnameModifier{ + Type: egv1a1.BackendHTTPHostnameModifier, + SetFromBackend: ptr.To(true), + }, + }, + } + }, + wantErrors: []string{}, + }, + { + desc: "invalid SetFromHeader missing settings", + mutate: func(httproutefilter *egv1a1.HTTPRouteFilter) { + httproutefilter.Spec = egv1a1.HTTPRouteFilterSpec{ + URLRewrite: &egv1a1.HTTPURLRewriteFilter{ + Hostname: &egv1a1.HTTPHostnameModifier{ + Type: egv1a1.HeaderHTTPHostnameModifier, + }, + }, + } + }, + wantErrors: []string{"spec.urlRewrite.hostname: Invalid value: \"object\": setFromHeader must be specified for SetFromHeader type"}, + }, + { + desc: "invalid SetFromBackend missing settings", + mutate: func(httproutefilter *egv1a1.HTTPRouteFilter) { + httproutefilter.Spec = egv1a1.HTTPRouteFilterSpec{ + URLRewrite: &egv1a1.HTTPURLRewriteFilter{ + Hostname: &egv1a1.HTTPHostnameModifier{ + Type: egv1a1.BackendHTTPHostnameModifier, + }, + }, + } + }, + wantErrors: []string{"spec.urlRewrite.hostname: Invalid value: \"object\": setFromBackend must be specified for SetFromBackend type"}, + }, + { + desc: "invalid SetFromBackend type", + mutate: func(httproutefilter *egv1a1.HTTPRouteFilter) { + httproutefilter.Spec = egv1a1.HTTPRouteFilterSpec{ + URLRewrite: &egv1a1.HTTPURLRewriteFilter{ + Hostname: &egv1a1.HTTPHostnameModifier{ + Type: egv1a1.BackendHTTPHostnameModifier, + SetFromHeader: ptr.To("foo"), + }, + }, + } + }, + wantErrors: []string{"spec.urlRewrite.hostname: Invalid value: \"object\": setFromHeader must be nil if the type is not SetFromHeader, spec.urlRewrite.hostname: Invalid value: \"object\": setFromBackend must be specified for SetFromBackend type"}, + }, + { + desc: "invalid SetFromHeader type", + mutate: func(httproutefilter *egv1a1.HTTPRouteFilter) { + httproutefilter.Spec = egv1a1.HTTPRouteFilterSpec{ + URLRewrite: &egv1a1.HTTPURLRewriteFilter{ + Hostname: &egv1a1.HTTPHostnameModifier{ + Type: egv1a1.HeaderHTTPHostnameModifier, + SetFromBackend: ptr.To(true), + }, + }, + } + }, + wantErrors: []string{"spec.urlRewrite.hostname: Invalid value: \"object\": setFromHeader must be specified for SetFromHeader type, spec.urlRewrite.hostname: Invalid value: \"object\": setFromBackend must be nil if the type is not SetFromBackend]"}, + }, } for _, tc := range cases {