diff --git a/.changelog/10613.txt b/.changelog/10613.txt new file mode 100644 index 0000000000..6499f35ae6 --- /dev/null +++ b/.changelog/10613.txt @@ -0,0 +1,3 @@ +```release-note:improvement +connect: Support manipulating HTTP headers in the mesh. +``` \ No newline at end of file diff --git a/api/config_entry_discoverychain.go b/api/config_entry_discoverychain.go index 5419292fed..384e2ce474 100644 --- a/api/config_entry_discoverychain.go +++ b/api/config_entry_discoverychain.go @@ -61,14 +61,16 @@ type ServiceRouteHTTPMatchQueryParam struct { } type ServiceRouteDestination struct { - Service string `json:",omitempty"` - ServiceSubset string `json:",omitempty" alias:"service_subset"` - Namespace string `json:",omitempty"` - PrefixRewrite string `json:",omitempty" alias:"prefix_rewrite"` - RequestTimeout time.Duration `json:",omitempty" alias:"request_timeout"` - NumRetries uint32 `json:",omitempty" alias:"num_retries"` - RetryOnConnectFailure bool `json:",omitempty" alias:"retry_on_connect_failure"` - RetryOnStatusCodes []uint32 `json:",omitempty" alias:"retry_on_status_codes"` + Service string `json:",omitempty"` + ServiceSubset string `json:",omitempty" alias:"service_subset"` + Namespace string `json:",omitempty"` + PrefixRewrite string `json:",omitempty" alias:"prefix_rewrite"` + RequestTimeout time.Duration `json:",omitempty" alias:"request_timeout"` + NumRetries uint32 `json:",omitempty" alias:"num_retries"` + RetryOnConnectFailure bool `json:",omitempty" alias:"retry_on_connect_failure"` + RetryOnStatusCodes []uint32 `json:",omitempty" alias:"retry_on_status_codes"` + RequestHeaders *HTTPHeaderModifiers `json:",omitempty" alias:"request_headers"` + ResponseHeaders *HTTPHeaderModifiers `json:",omitempty" alias:"response_headers"` } func (e *ServiceRouteDestination) MarshalJSON() ([]byte, error) { @@ -127,10 +129,12 @@ func (e *ServiceSplitterConfigEntry) GetCreateIndex() uint64 { return e.Crea func (e *ServiceSplitterConfigEntry) GetModifyIndex() uint64 { return e.ModifyIndex } type ServiceSplit struct { - Weight float32 - Service string `json:",omitempty"` - ServiceSubset string `json:",omitempty" alias:"service_subset"` - Namespace string `json:",omitempty"` + Weight float32 + Service string `json:",omitempty"` + ServiceSubset string `json:",omitempty" alias:"service_subset"` + Namespace string `json:",omitempty"` + RequestHeaders *HTTPHeaderModifiers `json:",omitempty" alias:"request_headers"` + ResponseHeaders *HTTPHeaderModifiers `json:",omitempty" alias:"response_headers"` } type ServiceResolverConfigEntry struct { @@ -287,3 +291,21 @@ type CookieConfig struct { // The path to set for the cookie Path string `json:",omitempty"` } + +// HTTPHeaderModifiers is a set of rules for HTTP header modification that +// should be performed by proxies as the request passes through them. It can +// operate on either request or response headers depending on the context in +// which it is used. +type HTTPHeaderModifiers struct { + // Add is a set of name -> value pairs that should be appended to the request + // or response (i.e. allowing duplicates if the same header already exists). + Add map[string]string `json:",omitempty"` + + // Set is a set of name -> value pairs that should be added to the request or + // response, overwriting any existing header values of the same name. + Set map[string]string `json:",omitempty"` + + // Remove is the set of header names that should be stripped from the request + // or response. + Remove []string `json:",omitempty"` +} diff --git a/api/config_entry_discoverychain_test.go b/api/config_entry_discoverychain_test.go index 5f8b81e075..aae84eb361 100644 --- a/api/config_entry_discoverychain_test.go +++ b/api/config_entry_discoverychain_test.go @@ -193,6 +193,14 @@ func TestAPI_ConfigEntry_DiscoveryChain(t *testing.T) { Service: "test-failover", ServiceSubset: "v1", Namespace: defaultNamespace, + RequestHeaders: &HTTPHeaderModifiers{ + Set: map[string]string{ + "x-foo": "bar", + }, + }, + ResponseHeaders: &HTTPHeaderModifiers{ + Remove: []string{"x-foo"}, + }, }, { Weight: 10, @@ -235,6 +243,14 @@ func TestAPI_ConfigEntry_DiscoveryChain(t *testing.T) { NumRetries: 5, RetryOnConnectFailure: true, RetryOnStatusCodes: []uint32{500, 503, 401}, + RequestHeaders: &HTTPHeaderModifiers{ + Set: map[string]string{ + "x-foo": "bar", + }, + }, + ResponseHeaders: &HTTPHeaderModifiers{ + Remove: []string{"x-foo"}, + }, }, }, }, diff --git a/api/config_entry_gateways.go b/api/config_entry_gateways.go index 822c093f2b..47369d9224 100644 --- a/api/config_entry_gateways.go +++ b/api/config_entry_gateways.go @@ -83,6 +83,10 @@ type IngressService struct { // using a "tcp" listener. Hosts []string + // Allow HTTP header manipulation to be configured. + RequestHeaders *HTTPHeaderModifiers `json:",omitempty" alias:"request_headers"` + ResponseHeaders *HTTPHeaderModifiers `json:",omitempty" alias:"response_headers"` + // Namespace is the namespace where the service is located. // Namespacing is a Consul Enterprise feature. Namespace string `json:",omitempty"` diff --git a/api/config_entry_gateways_test.go b/api/config_entry_gateways_test.go index c98ab321c1..22b15259b9 100644 --- a/api/config_entry_gateways_test.go +++ b/api/config_entry_gateways_test.go @@ -78,6 +78,14 @@ func TestAPI_ConfigEntries_IngressGateway(t *testing.T) { { Name: "asdf", Hosts: []string{"test.example.com"}, + RequestHeaders: &HTTPHeaderModifiers{ + Set: map[string]string{ + "x-foo": "bar", + }, + }, + ResponseHeaders: &HTTPHeaderModifiers{ + Remove: []string{"x-foo"}, + }, }, }, },