diff --git a/apis/projectcontour/v1/httpproxy.go b/apis/projectcontour/v1/httpproxy.go index 976795d6903..e49d97d37eb 100644 --- a/apis/projectcontour/v1/httpproxy.go +++ b/apis/projectcontour/v1/httpproxy.go @@ -348,6 +348,24 @@ type VirtualHost struct { // +optional JWTProviders []JWTProvider `json:"jwtProviders,omitempty"` + // The policy for managing request headers during proxying. + // Headers are appended to requests in the following order, + // service level headers, + // route level headers, + // virtual host level headers, + // global level headers. + // +optional + RequestHeadersPolicy *HeadersPolicy `json:"requestHeadersPolicy,omitempty"` + // The policy for managing response headers during proxying. + // Rewriting the 'Host' header is not supported. + // Headers are appended to responses in the following order, + // service level headers, + // route level headers, + // virtual host level headers, + // global level headers. + // +optional + ResponseHeadersPolicy *HeadersPolicy `json:"responseHeadersPolicy,omitempty"` + // IPAllowFilterPolicy is a list of ipv4/6 filter rules for which matching // requests should be allowed. All other requests will be denied. // Only one of IPAllowFilterPolicy and IPDenyFilterPolicy can be defined. @@ -584,10 +602,20 @@ type Route struct { // **NOTE: The header rewrite is only done while forwarding and has no bearing // on the routing decision. // + // Headers are appended to requests in the following order, + // service level headers, + // route level headers, + // virtual host level headers, + // global level headers. // +optional RequestHeadersPolicy *HeadersPolicy `json:"requestHeadersPolicy,omitempty"` // The policy for managing response headers during proxying. // Rewriting the 'Host' header is not supported. + // Headers are appended to responses in the following order, + // service level headers, + // route level headers, + // virtual host level headers, + // global level headers. // +optional ResponseHeadersPolicy *HeadersPolicy `json:"responseHeadersPolicy,omitempty"` // The policies for rewriting Set-Cookie header attributes. Note that @@ -1045,10 +1073,20 @@ type Service struct { // values Mirror bool `json:"mirror,omitempty"` // The policy for managing request headers during proxying. + // Headers are appended to requests in the following order, + // service level headers, + // route level headers, + // virtual host level headers, + // global level headers. // +optional RequestHeadersPolicy *HeadersPolicy `json:"requestHeadersPolicy,omitempty"` // The policy for managing response headers during proxying. // Rewriting the 'Host' header is not supported. + // Headers are appended to responses in the following order, + // service level headers, + // route level headers, + // virtual host level headers, + // global level headers. // +optional ResponseHeadersPolicy *HeadersPolicy `json:"responseHeadersPolicy,omitempty"` // The policies for rewriting Set-Cookie header attributes. diff --git a/apis/projectcontour/v1/zz_generated.deepcopy.go b/apis/projectcontour/v1/zz_generated.deepcopy.go index 3207fef641a..f88785597f8 100644 --- a/apis/projectcontour/v1/zz_generated.deepcopy.go +++ b/apis/projectcontour/v1/zz_generated.deepcopy.go @@ -1481,6 +1481,16 @@ func (in *VirtualHost) DeepCopyInto(out *VirtualHost) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.RequestHeadersPolicy != nil { + in, out := &in.RequestHeadersPolicy, &out.RequestHeadersPolicy + *out = new(HeadersPolicy) + (*in).DeepCopyInto(*out) + } + if in.ResponseHeadersPolicy != nil { + in, out := &in.ResponseHeadersPolicy, &out.ResponseHeadersPolicy + *out = new(HeadersPolicy) + (*in).DeepCopyInto(*out) + } if in.IPAllowFilterPolicy != nil { in, out := &in.IPAllowFilterPolicy, &out.IPAllowFilterPolicy *out = make([]IPFilterPolicy, len(*in)) diff --git a/changelogs/unreleased/5586-deveshk0-minor.md b/changelogs/unreleased/5586-deveshk0-minor.md new file mode 100644 index 00000000000..6c1d7d6ddab --- /dev/null +++ b/changelogs/unreleased/5586-deveshk0-minor.md @@ -0,0 +1,27 @@ +## HTTPProxy: Allow custom host header on HttpProxy level. + +This Change allows you set custom host headers on httpProxy level, Please note headers are appended to requests/responses in the following order: weighted cluster level headers, route level headers, virtual host level headers and finally global level headers. + +#### Example +```yaml +apiVersion: projectcontour.io/v1 +kind: HTTPProxy +metadata: + name: custom-host-header +spec: + fqdn: local.projectcontour.io + requestHeadersPolicy: + set: + - name: x-header + value: somevalue + responseHeadersPolicy: + set: + - name: x-powered-by + value: contour + routes: + - conditions: + - prefix: / + services: + - name: s1 + port: 80 +``` \ No newline at end of file diff --git a/examples/contour/01-crds.yaml b/examples/contour/01-crds.yaml index e722f3952f5..5696fca49cc 100644 --- a/examples/contour/01-crds.yaml +++ b/examples/contour/01-crds.yaml @@ -6383,6 +6383,11 @@ spec: Provided header must come from trusted source. **NOTE: The header rewrite is only done while forwarding and has no bearing on the routing decision. + Headers are appended to requests in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. properties: remove: description: Remove specifies a list of HTTP header names @@ -6472,6 +6477,11 @@ spec: description: |- The policy for managing response headers during proxying. Rewriting the 'Host' header is not supported. + Headers are appended to responses in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. properties: remove: description: Remove specifies a list of HTTP header names @@ -6673,8 +6683,13 @@ spec: - tls type: string requestHeadersPolicy: - description: The policy for managing request headers during - proxying. + description: |- + The policy for managing request headers during proxying. + Headers are appended to requests in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. properties: remove: description: Remove specifies a list of HTTP header @@ -6709,6 +6724,11 @@ spec: description: |- The policy for managing response headers during proxying. Rewriting the 'Host' header is not supported. + Headers are appended to responses in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. properties: remove: description: Remove specifies a list of HTTP header @@ -7081,8 +7101,13 @@ spec: - tls type: string requestHeadersPolicy: - description: The policy for managing request headers during - proxying. + description: |- + The policy for managing request headers during proxying. + Headers are appended to requests in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. properties: remove: description: Remove specifies a list of HTTP header @@ -7117,6 +7142,11 @@ spec: description: |- The policy for managing response headers during proxying. Rewriting the 'Host' header is not supported. + Headers are appended to responses in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. properties: remove: description: Remove specifies a list of HTTP header @@ -7815,6 +7845,83 @@ spec: - unit type: object type: object + requestHeadersPolicy: + description: |- + The policy for managing request headers during proxying. + Headers are appended to requests in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. + properties: + remove: + description: Remove specifies a list of HTTP header names + to remove. + items: + type: string + type: array + set: + description: |- + Set specifies a list of HTTP header values that will be set in the HTTP header. + If the header does not exist it will be added, otherwise it will be overwritten with the new value. + items: + description: HeaderValue represents a header name/value + pair + properties: + name: + description: Name represents a key of a header + minLength: 1 + type: string + value: + description: Value represents the value of a header + specified by a key + minLength: 1 + type: string + required: + - name + - value + type: object + type: array + type: object + responseHeadersPolicy: + description: |- + The policy for managing response headers during proxying. + Rewriting the 'Host' header is not supported. + Headers are appended to responses in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. + properties: + remove: + description: Remove specifies a list of HTTP header names + to remove. + items: + type: string + type: array + set: + description: |- + Set specifies a list of HTTP header values that will be set in the HTTP header. + If the header does not exist it will be added, otherwise it will be overwritten with the new value. + items: + description: HeaderValue represents a header name/value + pair + properties: + name: + description: Name represents a key of a header + minLength: 1 + type: string + value: + description: Value represents the value of a header + specified by a key + minLength: 1 + type: string + required: + - name + - value + type: object + type: array + type: object tls: description: |- If present the fields describes TLS properties of the virtual diff --git a/examples/render/contour-deployment.yaml b/examples/render/contour-deployment.yaml index 8e55222cf8b..8a72debd56f 100644 --- a/examples/render/contour-deployment.yaml +++ b/examples/render/contour-deployment.yaml @@ -6603,6 +6603,11 @@ spec: Provided header must come from trusted source. **NOTE: The header rewrite is only done while forwarding and has no bearing on the routing decision. + Headers are appended to requests in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. properties: remove: description: Remove specifies a list of HTTP header names @@ -6692,6 +6697,11 @@ spec: description: |- The policy for managing response headers during proxying. Rewriting the 'Host' header is not supported. + Headers are appended to responses in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. properties: remove: description: Remove specifies a list of HTTP header names @@ -6893,8 +6903,13 @@ spec: - tls type: string requestHeadersPolicy: - description: The policy for managing request headers during - proxying. + description: |- + The policy for managing request headers during proxying. + Headers are appended to requests in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. properties: remove: description: Remove specifies a list of HTTP header @@ -6929,6 +6944,11 @@ spec: description: |- The policy for managing response headers during proxying. Rewriting the 'Host' header is not supported. + Headers are appended to responses in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. properties: remove: description: Remove specifies a list of HTTP header @@ -7301,8 +7321,13 @@ spec: - tls type: string requestHeadersPolicy: - description: The policy for managing request headers during - proxying. + description: |- + The policy for managing request headers during proxying. + Headers are appended to requests in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. properties: remove: description: Remove specifies a list of HTTP header @@ -7337,6 +7362,11 @@ spec: description: |- The policy for managing response headers during proxying. Rewriting the 'Host' header is not supported. + Headers are appended to responses in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. properties: remove: description: Remove specifies a list of HTTP header @@ -8035,6 +8065,83 @@ spec: - unit type: object type: object + requestHeadersPolicy: + description: |- + The policy for managing request headers during proxying. + Headers are appended to requests in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. + properties: + remove: + description: Remove specifies a list of HTTP header names + to remove. + items: + type: string + type: array + set: + description: |- + Set specifies a list of HTTP header values that will be set in the HTTP header. + If the header does not exist it will be added, otherwise it will be overwritten with the new value. + items: + description: HeaderValue represents a header name/value + pair + properties: + name: + description: Name represents a key of a header + minLength: 1 + type: string + value: + description: Value represents the value of a header + specified by a key + minLength: 1 + type: string + required: + - name + - value + type: object + type: array + type: object + responseHeadersPolicy: + description: |- + The policy for managing response headers during proxying. + Rewriting the 'Host' header is not supported. + Headers are appended to responses in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. + properties: + remove: + description: Remove specifies a list of HTTP header names + to remove. + items: + type: string + type: array + set: + description: |- + Set specifies a list of HTTP header values that will be set in the HTTP header. + If the header does not exist it will be added, otherwise it will be overwritten with the new value. + items: + description: HeaderValue represents a header name/value + pair + properties: + name: + description: Name represents a key of a header + minLength: 1 + type: string + value: + description: Value represents the value of a header + specified by a key + minLength: 1 + type: string + required: + - name + - value + type: object + type: array + type: object tls: description: |- If present the fields describes TLS properties of the virtual diff --git a/examples/render/contour-gateway-provisioner.yaml b/examples/render/contour-gateway-provisioner.yaml index 46a3eea4987..c048e4f984d 100644 --- a/examples/render/contour-gateway-provisioner.yaml +++ b/examples/render/contour-gateway-provisioner.yaml @@ -6394,6 +6394,11 @@ spec: Provided header must come from trusted source. **NOTE: The header rewrite is only done while forwarding and has no bearing on the routing decision. + Headers are appended to requests in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. properties: remove: description: Remove specifies a list of HTTP header names @@ -6483,6 +6488,11 @@ spec: description: |- The policy for managing response headers during proxying. Rewriting the 'Host' header is not supported. + Headers are appended to responses in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. properties: remove: description: Remove specifies a list of HTTP header names @@ -6684,8 +6694,13 @@ spec: - tls type: string requestHeadersPolicy: - description: The policy for managing request headers during - proxying. + description: |- + The policy for managing request headers during proxying. + Headers are appended to requests in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. properties: remove: description: Remove specifies a list of HTTP header @@ -6720,6 +6735,11 @@ spec: description: |- The policy for managing response headers during proxying. Rewriting the 'Host' header is not supported. + Headers are appended to responses in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. properties: remove: description: Remove specifies a list of HTTP header @@ -7092,8 +7112,13 @@ spec: - tls type: string requestHeadersPolicy: - description: The policy for managing request headers during - proxying. + description: |- + The policy for managing request headers during proxying. + Headers are appended to requests in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. properties: remove: description: Remove specifies a list of HTTP header @@ -7128,6 +7153,11 @@ spec: description: |- The policy for managing response headers during proxying. Rewriting the 'Host' header is not supported. + Headers are appended to responses in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. properties: remove: description: Remove specifies a list of HTTP header @@ -7826,6 +7856,83 @@ spec: - unit type: object type: object + requestHeadersPolicy: + description: |- + The policy for managing request headers during proxying. + Headers are appended to requests in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. + properties: + remove: + description: Remove specifies a list of HTTP header names + to remove. + items: + type: string + type: array + set: + description: |- + Set specifies a list of HTTP header values that will be set in the HTTP header. + If the header does not exist it will be added, otherwise it will be overwritten with the new value. + items: + description: HeaderValue represents a header name/value + pair + properties: + name: + description: Name represents a key of a header + minLength: 1 + type: string + value: + description: Value represents the value of a header + specified by a key + minLength: 1 + type: string + required: + - name + - value + type: object + type: array + type: object + responseHeadersPolicy: + description: |- + The policy for managing response headers during proxying. + Rewriting the 'Host' header is not supported. + Headers are appended to responses in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. + properties: + remove: + description: Remove specifies a list of HTTP header names + to remove. + items: + type: string + type: array + set: + description: |- + Set specifies a list of HTTP header values that will be set in the HTTP header. + If the header does not exist it will be added, otherwise it will be overwritten with the new value. + items: + description: HeaderValue represents a header name/value + pair + properties: + name: + description: Name represents a key of a header + minLength: 1 + type: string + value: + description: Value represents the value of a header + specified by a key + minLength: 1 + type: string + required: + - name + - value + type: object + type: array + type: object tls: description: |- If present the fields describes TLS properties of the virtual diff --git a/examples/render/contour-gateway.yaml b/examples/render/contour-gateway.yaml index 2489a481d34..1ccf9b45bef 100644 --- a/examples/render/contour-gateway.yaml +++ b/examples/render/contour-gateway.yaml @@ -6419,6 +6419,11 @@ spec: Provided header must come from trusted source. **NOTE: The header rewrite is only done while forwarding and has no bearing on the routing decision. + Headers are appended to requests in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. properties: remove: description: Remove specifies a list of HTTP header names @@ -6508,6 +6513,11 @@ spec: description: |- The policy for managing response headers during proxying. Rewriting the 'Host' header is not supported. + Headers are appended to responses in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. properties: remove: description: Remove specifies a list of HTTP header names @@ -6709,8 +6719,13 @@ spec: - tls type: string requestHeadersPolicy: - description: The policy for managing request headers during - proxying. + description: |- + The policy for managing request headers during proxying. + Headers are appended to requests in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. properties: remove: description: Remove specifies a list of HTTP header @@ -6745,6 +6760,11 @@ spec: description: |- The policy for managing response headers during proxying. Rewriting the 'Host' header is not supported. + Headers are appended to responses in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. properties: remove: description: Remove specifies a list of HTTP header @@ -7117,8 +7137,13 @@ spec: - tls type: string requestHeadersPolicy: - description: The policy for managing request headers during - proxying. + description: |- + The policy for managing request headers during proxying. + Headers are appended to requests in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. properties: remove: description: Remove specifies a list of HTTP header @@ -7153,6 +7178,11 @@ spec: description: |- The policy for managing response headers during proxying. Rewriting the 'Host' header is not supported. + Headers are appended to responses in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. properties: remove: description: Remove specifies a list of HTTP header @@ -7851,6 +7881,83 @@ spec: - unit type: object type: object + requestHeadersPolicy: + description: |- + The policy for managing request headers during proxying. + Headers are appended to requests in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. + properties: + remove: + description: Remove specifies a list of HTTP header names + to remove. + items: + type: string + type: array + set: + description: |- + Set specifies a list of HTTP header values that will be set in the HTTP header. + If the header does not exist it will be added, otherwise it will be overwritten with the new value. + items: + description: HeaderValue represents a header name/value + pair + properties: + name: + description: Name represents a key of a header + minLength: 1 + type: string + value: + description: Value represents the value of a header + specified by a key + minLength: 1 + type: string + required: + - name + - value + type: object + type: array + type: object + responseHeadersPolicy: + description: |- + The policy for managing response headers during proxying. + Rewriting the 'Host' header is not supported. + Headers are appended to responses in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. + properties: + remove: + description: Remove specifies a list of HTTP header names + to remove. + items: + type: string + type: array + set: + description: |- + Set specifies a list of HTTP header values that will be set in the HTTP header. + If the header does not exist it will be added, otherwise it will be overwritten with the new value. + items: + description: HeaderValue represents a header name/value + pair + properties: + name: + description: Name represents a key of a header + minLength: 1 + type: string + value: + description: Value represents the value of a header + specified by a key + minLength: 1 + type: string + required: + - name + - value + type: object + type: array + type: object tls: description: |- If present the fields describes TLS properties of the virtual diff --git a/examples/render/contour.yaml b/examples/render/contour.yaml index aa7c4c33bbe..036e111f957 100644 --- a/examples/render/contour.yaml +++ b/examples/render/contour.yaml @@ -6603,6 +6603,11 @@ spec: Provided header must come from trusted source. **NOTE: The header rewrite is only done while forwarding and has no bearing on the routing decision. + Headers are appended to requests in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. properties: remove: description: Remove specifies a list of HTTP header names @@ -6692,6 +6697,11 @@ spec: description: |- The policy for managing response headers during proxying. Rewriting the 'Host' header is not supported. + Headers are appended to responses in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. properties: remove: description: Remove specifies a list of HTTP header names @@ -6893,8 +6903,13 @@ spec: - tls type: string requestHeadersPolicy: - description: The policy for managing request headers during - proxying. + description: |- + The policy for managing request headers during proxying. + Headers are appended to requests in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. properties: remove: description: Remove specifies a list of HTTP header @@ -6929,6 +6944,11 @@ spec: description: |- The policy for managing response headers during proxying. Rewriting the 'Host' header is not supported. + Headers are appended to responses in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. properties: remove: description: Remove specifies a list of HTTP header @@ -7301,8 +7321,13 @@ spec: - tls type: string requestHeadersPolicy: - description: The policy for managing request headers during - proxying. + description: |- + The policy for managing request headers during proxying. + Headers are appended to requests in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. properties: remove: description: Remove specifies a list of HTTP header @@ -7337,6 +7362,11 @@ spec: description: |- The policy for managing response headers during proxying. Rewriting the 'Host' header is not supported. + Headers are appended to responses in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. properties: remove: description: Remove specifies a list of HTTP header @@ -8035,6 +8065,83 @@ spec: - unit type: object type: object + requestHeadersPolicy: + description: |- + The policy for managing request headers during proxying. + Headers are appended to requests in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. + properties: + remove: + description: Remove specifies a list of HTTP header names + to remove. + items: + type: string + type: array + set: + description: |- + Set specifies a list of HTTP header values that will be set in the HTTP header. + If the header does not exist it will be added, otherwise it will be overwritten with the new value. + items: + description: HeaderValue represents a header name/value + pair + properties: + name: + description: Name represents a key of a header + minLength: 1 + type: string + value: + description: Value represents the value of a header + specified by a key + minLength: 1 + type: string + required: + - name + - value + type: object + type: array + type: object + responseHeadersPolicy: + description: |- + The policy for managing response headers during proxying. + Rewriting the 'Host' header is not supported. + Headers are appended to responses in the following order, + weighted cluster level headers, + route level headers, + virtual host level headers, + global level headers. + properties: + remove: + description: Remove specifies a list of HTTP header names + to remove. + items: + type: string + type: array + set: + description: |- + Set specifies a list of HTTP header values that will be set in the HTTP header. + If the header does not exist it will be added, otherwise it will be overwritten with the new value. + items: + description: HeaderValue represents a header name/value + pair + properties: + name: + description: Name represents a key of a header + minLength: 1 + type: string + value: + description: Value represents the value of a header + specified by a key + minLength: 1 + type: string + required: + - name + - value + type: object + type: array + type: object tls: description: |- If present the fields describes TLS properties of the virtual diff --git a/internal/dag/builder_test.go b/internal/dag/builder_test.go index ffeced315ff..36487b2675f 100644 --- a/internal/dag/builder_test.go +++ b/internal/dag/builder_test.go @@ -21,6 +21,7 @@ import ( "github.com/stretchr/testify/assert" core_v1 "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" networking_v1 "k8s.io/api/networking/v1" meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -8851,6 +8852,47 @@ func TestDAGInsert(t *testing.T) { }}, }, } + + // proxyWithVhostHeader has a vhost level header rewrites. + proxyWithVhostHeader := &contour_v1.HTTPProxy{ + ObjectMeta: meta_v1.ObjectMeta{ + Name: "example-com", + Namespace: "default", + }, + Spec: contour_v1.HTTPProxySpec{ + VirtualHost: &contour_v1.VirtualHost{ + Fqdn: "example.com", + RequestHeadersPolicy: &contour_v1.HeadersPolicy{ + Set: []contour_v1.HeaderValue{{ + Name: "In-Foo", + Value: "bar", + }}, + Remove: []string{ + "In-Baz", + }, + }, + ResponseHeadersPolicy: &contour_v1.HeadersPolicy{ + Set: []contour_v1.HeaderValue{{ + Name: "Out-Foo", + Value: "bar", + }}, + Remove: []string{ + "Out-Baz", + }, + }, + }, + Routes: []contour_v1.Route{{ + Conditions: []contour_v1.MatchCondition{{ + Prefix: "/", + }}, + Services: []contour_v1.Service{{ + Name: "kuard", + Port: 8080, + }}, + }}, + }, + } + // proxy111 has a route that rewrites headers. proxy111 := &contour_v1.HTTPProxy{ ObjectMeta: meta_v1.ObjectMeta{ @@ -12072,6 +12114,31 @@ func TestDAGInsert(t *testing.T) { ), }, + "insert httpproxy with vhost-level header manipulation": { + objs: []any{ + proxyWithVhostHeader, s1, + }, + want: listeners( + &Listener{ + Name: HTTP_LISTENER_NAME, + Port: 8080, + VirtualHosts: virtualhosts( + virtualhostWithHeader("example.com", map[string]string{ + "In-Foo": "bar", + }, []string{"In-Baz"}, map[string]string{ + "Out-Foo": "bar", + }, []string{"Out-Baz"}, &Route{ + PathMatchCondition: prefixString("/"), + Clusters: []*Cluster{{ + Upstream: service(s1), + SNI: "", + }}, + }), + ), + }, + ), + }, + // issue 1399 "service shared across ingress and httpproxy tcpproxy": { objs: []any{ @@ -15926,7 +15993,22 @@ func virtualhost(name string, first *Route, rest ...*Route) *VirtualHost { } } -func securevirtualhost(name string, sec *core_v1.Secret, first *Route, rest ...*Route) *SecureVirtualHost { +func virtualhostWithHeader(name string, requestSet map[string]string, requestRemove []string, responseSet map[string]string, responseRemove []string, first *Route, rest ...*Route) *VirtualHost { + return &VirtualHost{ + Name: name, + RequestHeadersPolicy: &HeadersPolicy{ + Set: requestSet, + Remove: requestRemove, + }, + ResponseHeadersPolicy: &HeadersPolicy{ + Set: responseSet, + Remove: responseRemove, + }, + Routes: routes(append([]*Route{first}, rest...)...), + } +} + +func securevirtualhost(name string, sec *v1.Secret, first *Route, rest ...*Route) *SecureVirtualHost { return &SecureVirtualHost{ VirtualHost: VirtualHost{ Name: name, diff --git a/internal/dag/dag.go b/internal/dag/dag.go index 85fb54a6a34..4df956e577e 100644 --- a/internal/dag/dag.go +++ b/internal/dag/dag.go @@ -734,6 +734,13 @@ type VirtualHost struct { // CORSPolicy is the cross-origin policy to apply to the VirtualHost. CORSPolicy *CORSPolicy + // The policy for managing request headers during proxying. + RequestHeadersPolicy *HeadersPolicy + + // The policy for managing response headers during proxying. + // Rewriting the 'Host' header is not supported. + ResponseHeadersPolicy *HeadersPolicy + // RateLimitPolicy defines if/how requests for the virtual host // are rate limited. RateLimitPolicy *RateLimitPolicy diff --git a/internal/dag/httpproxy_processor.go b/internal/dag/httpproxy_processor.go index 7dac1510315..59667655228 100644 --- a/internal/dag/httpproxy_processor.go +++ b/internal/dag/httpproxy_processor.go @@ -536,6 +536,25 @@ func (p *HTTPProxyProcessor) computeHTTPProxy(proxy *contour_v1.HTTPProxy) { return } insecure.CORSPolicy = cp + dynamicHeaders := map[string]string{ + "CONTOUR_NAMESPACE": proxy.Namespace, + } + + requestHeadersPolicy, err := headersPolicyRoute(proxy.Spec.VirtualHost.RequestHeadersPolicy, false, dynamicHeaders) + if err != nil { + validCond.AddErrorf(contour_v1.ConditionTypeRouteError, "RequestHeadersPolicyInvalid", + "%s on response headers", err) + return + } + responseHeadersPolicy, err := headersPolicyRoute(proxy.Spec.VirtualHost.ResponseHeadersPolicy, false, dynamicHeaders) + if err != nil { + validCond.AddErrorf(contour_v1.ConditionTypeRouteError, "ResponseHeaderPolicyInvalid", + "%s on response headers", err) + return + } + + insecure.RequestHeadersPolicy = requestHeadersPolicy + insecure.ResponseHeadersPolicy = responseHeadersPolicy var isValidRLP bool insecure.RateLimitPolicy, isValidRLP = computeVirtualHostRateLimitPolicy(proxy, p.GlobalRateLimitService, validCond) @@ -580,6 +599,9 @@ func (p *HTTPProxyProcessor) computeHTTPProxy(proxy *contour_v1.HTTPProxy) { return } + secure.RequestHeadersPolicy = requestHeadersPolicy + secure.ResponseHeadersPolicy = responseHeadersPolicy + addRoutes(secure, routes) // Process JWT verification requirements. diff --git a/internal/envoy/v3/route.go b/internal/envoy/v3/route.go index ecc2a4934fa..517c69d5311 100644 --- a/internal/envoy/v3/route.go +++ b/internal/envoy/v3/route.go @@ -70,6 +70,15 @@ func VirtualHostAndRoutes(vh *dag.VirtualHost, dagRoutes []*dag.Route, secure bo evh.RateLimits = GlobalRateLimits(vh.RateLimitPolicy.Global.Descriptors) } + if vh.RequestHeadersPolicy != nil { + evh.RequestHeadersToAdd = append(headerValueList(vh.RequestHeadersPolicy.Set, false), headerValueList(vh.RequestHeadersPolicy.Add, true)...) + evh.RequestHeadersToRemove = vh.RequestHeadersPolicy.Remove + } + if vh.ResponseHeadersPolicy != nil { + evh.ResponseHeadersToAdd = append(headerValueList(vh.ResponseHeadersPolicy.Set, false), headerValueList(vh.ResponseHeadersPolicy.Add, true)...) + evh.ResponseHeadersToRemove = vh.ResponseHeadersPolicy.Remove + } + if len(vh.IPFilterRules) > 0 { if evh.TypedPerFilterConfig == nil { evh.TypedPerFilterConfig = map[string]*anypb.Any{} diff --git a/internal/envoy/v3/route_test.go b/internal/envoy/v3/route_test.go index 2f91680d7e4..e00bd9cddb4 100644 --- a/internal/envoy/v3/route_test.go +++ b/internal/envoy/v3/route_test.go @@ -19,6 +19,7 @@ import ( "time" envoy_config_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" + envoy_core_v3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" envoy_config_rbac_v3 "github.com/envoyproxy/go-control-plane/envoy/config/rbac/v3" envoy_config_route_v3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3" envoy_filter_http_cors_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/cors/v3" @@ -27,17 +28,16 @@ import ( envoy_internal_redirect_safe_cross_scheme_v3 "github.com/envoyproxy/go-control-plane/envoy/extensions/internal_redirect/safe_cross_scheme/v3" envoy_matcher_v3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3" envoy_type_v3 "github.com/envoyproxy/go-control-plane/envoy/type/v3" + "github.com/projectcontour/contour/internal/dag" + "github.com/projectcontour/contour/internal/fixture" + "github.com/projectcontour/contour/internal/protobuf" + "github.com/projectcontour/contour/internal/timeout" "github.com/stretchr/testify/assert" "google.golang.org/protobuf/types/known/anypb" "google.golang.org/protobuf/types/known/durationpb" "google.golang.org/protobuf/types/known/wrapperspb" core_v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/util/intstr" - - "github.com/projectcontour/contour/internal/dag" - "github.com/projectcontour/contour/internal/fixture" - "github.com/projectcontour/contour/internal/protobuf" - "github.com/projectcontour/contour/internal/timeout" ) func TestRouteRoute(t *testing.T) { @@ -2730,3 +2730,91 @@ func TestRouteRedirect(t *testing.T) { func virtualhosts(v ...*envoy_config_route_v3.VirtualHost) []*envoy_config_route_v3.VirtualHost { return v } + +func TestVirtualHostAndRoutes(t *testing.T) { + tests := map[string]struct { + vh *dag.VirtualHost + route []*dag.Route + secure bool + want *envoy_config_route_v3.VirtualHost + }{ + "dag virtual host to envoy virtual host with custom header": { + vh: &dag.VirtualHost{ + Name: "example.com", + RequestHeadersPolicy: &dag.HeadersPolicy{ + Set: map[string]string{ + "In-Foo": "bar", + }, + Add: map[string]string{ + "In-Add": "bar", + }, + Remove: []string{"In-Baz"}, + }, + ResponseHeadersPolicy: &dag.HeadersPolicy{ + Set: map[string]string{ + "Out-Foo": "bar", + }, + Add: map[string]string{ + "Out-Add": "bar", + }, + Remove: []string{"Out-Baz"}, + }, + }, + route: []*dag.Route{ + { + PathMatchCondition: &dag.ExactMatchCondition{ + Path: "/foo", + }, + }, + }, + secure: false, + want: &envoy_config_route_v3.VirtualHost{ + Name: "example.com", + Domains: []string{"example.com"}, + RequestHeadersToAdd: []*envoy_core_v3.HeaderValueOption{{ + Header: &envoy_core_v3.HeaderValue{ + Key: "In-Foo", + Value: "bar", + }, + AppendAction: envoy_core_v3.HeaderValueOption_OVERWRITE_IF_EXISTS_OR_ADD, + }, { + Header: &envoy_core_v3.HeaderValue{ + Key: "In-Add", + Value: "bar", + }, + }}, + RequestHeadersToRemove: []string{"In-Baz"}, + ResponseHeadersToAdd: []*envoy_core_v3.HeaderValueOption{{ + Header: &envoy_core_v3.HeaderValue{ + Key: "Out-Foo", + Value: "bar", + }, + AppendAction: envoy_core_v3.HeaderValueOption_OVERWRITE_IF_EXISTS_OR_ADD, + }, { + Header: &envoy_core_v3.HeaderValue{ + Key: "Out-Add", + Value: "bar", + }, + }}, + ResponseHeadersToRemove: []string{"Out-Baz"}, + Routes: []*envoy_config_route_v3.Route{ + { + Match: &envoy_config_route_v3.RouteMatch{ + PathSpecifier: &envoy_config_route_v3.RouteMatch_Path{Path: "/foo"}, + }, + Action: &envoy_config_route_v3.Route_Route{Route: &envoy_config_route_v3.RouteAction{ + ClusterSpecifier: &envoy_config_route_v3.RouteAction_WeightedClusters{WeightedClusters: nil}, + }}, + }, + }, + }, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + got := VirtualHostAndRoutes(tc.vh, tc.route, tc.secure) + protobuf.ExpectEqual(t, tc.want, got) + }) + } +} diff --git a/site/content/docs/main/config/api-reference.html b/site/content/docs/main/config/api-reference.html index 82e03f685da..4430eb9d922 100644 --- a/site/content/docs/main/config/api-reference.html +++ b/site/content/docs/main/config/api-reference.html @@ -2101,7 +2101,8 @@
(Appears on: Route, -Service) +Service, +VirtualHost)
HeadersPolicy defines how headers are managed during forwarding. @@ -3742,6 +3743,11 @@
**NOTE: The header rewrite is only done while forwarding and has no bearing on the routing decision.
+Headers are appended to requests in the following order, +weighted cluster level headers, +route level headers, +virtual host level headers, +global level headers.
The policy for managing response headers during proxying. -Rewriting the ‘Host’ header is not supported.
+Rewriting the ‘Host’ header is not supported. +Headers are appended to responses in the following order, +weighted cluster level headers, +route level headers, +virtual host level headers, +global level headers.The policy for managing request headers during proxying.
+The policy for managing request headers during proxying. +Headers are appended to requests in the following order, +weighted cluster level headers, +route level headers, +virtual host level headers, +global level headers.
The policy for managing response headers during proxying. -Rewriting the ‘Host’ header is not supported.
+Rewriting the ‘Host’ header is not supported. +Headers are appended to responses in the following order, +weighted cluster level headers, +route level headers, +virtual host level headers, +global level headers.requestHeadersPolicy
+The policy for managing request headers during proxying. +Headers are appended to requests in the following order, +weighted cluster level headers, +route level headers, +virtual host level headers, +global level headers.
+responseHeadersPolicy
+The policy for managing response headers during proxying. +Rewriting the ‘Host’ header is not supported. +Headers are appended to responses in the following order, +weighted cluster level headers, +route level headers, +virtual host level headers, +global level headers.
+ipAllowPolicy