diff --git a/changelog/v1.16.0-beta10/translate-to-envoy-append-action.yaml b/changelog/v1.16.0-beta10/translate-to-envoy-append-action.yaml new file mode 100644 index 00000000000..90561129283 --- /dev/null +++ b/changelog/v1.16.0-beta10/translate-to-envoy-append-action.yaml @@ -0,0 +1,7 @@ +changelog: + - type: NON_USER_FACING + issueLink: https://github.com/solo-io/gloo/issues/8525 + resolvesIssue: false + description: >- + Translate header manipulation's `append` to Envoy's `append_action`, the newer non-deprecated field. + skipCI-kube-tests:true diff --git a/docs/content/guides/traffic_management/request_processing/append_remove_headers/_index.md b/docs/content/guides/traffic_management/request_processing/append_remove_headers/_index.md index daa6fc50e5c..e7f6d724569 100644 --- a/docs/content/guides/traffic_management/request_processing/append_remove_headers/_index.md +++ b/docs/content/guides/traffic_management/request_processing/append_remove_headers/_index.md @@ -251,6 +251,7 @@ Headers can be inherited by children options, such as shown in the following exa - header: key: x-route-table value: alphabet + append: false # overwrite the value of `x-route-table` if it already exists ``` 3. In the RouteTable child object, define other headers. In the following example, the `x-route-table` header is added to requests, and the `x-route-table: a` header is added to responses. ```yaml @@ -282,4 +283,13 @@ Headers can be inherited by children options, such as shown in the following exa 4. Now, requests that match the route `/a/1` get the following headers: * The `x-gateway-start-time` request header is inherited from the parent VirtualHost option. * The `x-route-table` request header is set in the child Route option. - * The `x-route-table` response header in the child overwrites the parent object's value of `alphabet` instead to `a`, because child objects take precedence in case of conflict. + * The `x-route-table` response header in the parent overwrites the child object's value of `a` instead to `alphabet`. + +Due to how header manipulations are processed, less specific headers overwrite more specific headers. + +From the [Envoy docs](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers#custom-request-response-headers): +> 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. + +In the previous example of the `x-route-table` response header, the virtual host level header overwrites the route level header because the virtual host level header is evaluated after the route level header. +If you set `append: true` or omit this field on the virtual host, then the route level response header (`a`) would get appended to the virtual host level header (`alphabet`). +If you set `append: false` on the route, the route does not affect the virtual host because the route is evaluated before the virtual host. In the example, the response header would stay `x-route-table: alphabet`. diff --git a/docs/content/reference/api/github.com/solo-io/gloo/projects/gloo/api/v1/options/headers/headers.proto.sk.md b/docs/content/reference/api/github.com/solo-io/gloo/projects/gloo/api/v1/options/headers/headers.proto.sk.md index ed447b9e5bc..36c21aec842 100644 --- a/docs/content/reference/api/github.com/solo-io/gloo/projects/gloo/api/v1/options/headers/headers.proto.sk.md +++ b/docs/content/reference/api/github.com/solo-io/gloo/projects/gloo/api/v1/options/headers/headers.proto.sk.md @@ -65,7 +65,7 @@ Header name/value pair plus option to control append behavior. | Field | Type | Description | | ----- | ---- | ----------- | | `header` | [.headers.options.gloo.solo.io.HeaderValue](../headers.proto.sk/#headervalue) | Header name/value pair that this option applies to. | -| `append` | [.google.protobuf.BoolValue](https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/well-known-types/bool-value) | Should the value be appended? If true (default), the value is appended to existing values. | +| `append` | [.google.protobuf.BoolValue](https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/well-known-types/bool-value) | Specifies if the value should be appended or overwrite an existing header. Defaults to true. If set to true, this maps to Envoy's `append_value: APPEND_IF_EXISTS_OR_ADD`, where the value gets be appended the header's value(s) if exists, or created if it does not. If set to false, this maps to Envoy's `append_value: OVERWRITE_IF_EXISTS_OR_ADD`, where the header's value will be overwritten if it exists, or created if it does not. | diff --git a/pkg/utils/api_conversion/type.go b/pkg/utils/api_conversion/type.go index 0b614dd451d..454de372e33 100644 --- a/pkg/utils/api_conversion/type.go +++ b/pkg/utils/api_conversion/type.go @@ -81,6 +81,13 @@ func CheckForbiddenCustomHeaders(header envoycore_sk.HeaderValue) error { } func ToEnvoyHeaderValueOptions(option *envoycore_sk.HeaderValueOption, secrets *v1.SecretList, secretOptions HeaderSecretOptions) ([]*envoy_config_core_v3.HeaderValueOption, error) { + appendAction := envoy_config_core_v3.HeaderValueOption_APPEND_IF_EXISTS_OR_ADD + if appendOption := option.GetAppend(); appendOption != nil { + if appendOption.GetValue() == false { + appendAction = envoy_config_core_v3.HeaderValueOption_OVERWRITE_IF_EXISTS_OR_ADD + } + } + switch typedOption := option.GetHeaderOption().(type) { case *envoycore_sk.HeaderValueOption_Header: if err := CheckForbiddenCustomHeaders(*typedOption.Header); err != nil { @@ -92,7 +99,7 @@ func ToEnvoyHeaderValueOptions(option *envoycore_sk.HeaderValueOption, secrets * Key: typedOption.Header.GetKey(), Value: typedOption.Header.GetValue(), }, - Append: option.GetAppend(), + AppendAction: appendAction, }, }, nil case *envoycore_sk.HeaderValueOption_HeaderSecretRef: @@ -117,7 +124,7 @@ func ToEnvoyHeaderValueOptions(option *envoycore_sk.HeaderValueOption, secrets * Key: key, Value: value, }, - Append: option.GetAppend(), + AppendAction: appendAction, }) } return result, nil diff --git a/pkg/utils/api_conversion/type_test.go b/pkg/utils/api_conversion/type_test.go index 7a7f6a1e429..9f610b6b20d 100644 --- a/pkg/utils/api_conversion/type_test.go +++ b/pkg/utils/api_conversion/type_test.go @@ -33,9 +33,7 @@ var _ = Describe("Type conversion", func() { Key: "allowed", Value: "header", }, - Append: &wrappers.BoolValue{ - Value: true, - }, + AppendAction: envoy_config_core_v3.HeaderValueOption_APPEND_IF_EXISTS_OR_ADD, }, } headers, err := ToEnvoyHeaderValueOptionList(allowedHeaders, nil, HeaderSecretOptions{}) diff --git a/projects/gloo/api/v1/options/headers/headers.proto b/projects/gloo/api/v1/options/headers/headers.proto index 389357aea3c..5b922c39b8b 100644 --- a/projects/gloo/api/v1/options/headers/headers.proto +++ b/projects/gloo/api/v1/options/headers/headers.proto @@ -46,8 +46,9 @@ message HeaderValueOption { // Header name/value pair that this option applies to. HeaderValue header = 1; - // Should the value be appended? If true (default), the value is appended to - // existing values. + // Specifies if the value should be appended or overwrite an existing header. Defaults to true. + // If set to true, this maps to Envoy's `append_value: APPEND_IF_EXISTS_OR_ADD`, where the value gets be appended the header's value(s) if exists, or created if it does not. + // If set to false, this maps to Envoy's `append_value: OVERWRITE_IF_EXISTS_OR_ADD`, where the header's value will be overwritten if it exists, or created if it does not. google.protobuf.BoolValue append = 2; } diff --git a/projects/gloo/pkg/api/v1/options/headers/headers.pb.go b/projects/gloo/pkg/api/v1/options/headers/headers.pb.go index 62e05b6956c..c6a7403bf69 100644 --- a/projects/gloo/pkg/api/v1/options/headers/headers.pb.go +++ b/projects/gloo/pkg/api/v1/options/headers/headers.pb.go @@ -118,8 +118,9 @@ type HeaderValueOption struct { // Header name/value pair that this option applies to. Header *HeaderValue `protobuf:"bytes,1,opt,name=header,proto3" json:"header,omitempty"` - // Should the value be appended? If true (default), the value is appended to - // existing values. + // Specifies if the value should be appended or overwrite an existing header. Defaults to true. + // If set to true, this maps to Envoy's `append_value: APPEND_IF_EXISTS_OR_ADD`, where the value gets be appended the header's value(s) if exists, or created if it does not. + // If set to false, this maps to Envoy's `append_value: OVERWRITE_IF_EXISTS_OR_ADD`, where the header's value will be overwritten if it exists, or created if it does not. Append *wrappers.BoolValue `protobuf:"bytes,2,opt,name=append,proto3" json:"append,omitempty"` } diff --git a/projects/gloo/pkg/plugins/headers/plugin.go b/projects/gloo/pkg/plugins/headers/plugin.go index 0a5d8ef6d7b..3db1b079c3c 100644 --- a/projects/gloo/pkg/plugins/headers/plugin.go +++ b/projects/gloo/pkg/plugins/headers/plugin.go @@ -247,12 +247,19 @@ func convertResponseHeaderValueOption( return nil, CantSetHostHeaderError } + appendAction := envoy_config_core_v3.HeaderValueOption_APPEND_IF_EXISTS_OR_ADD + if appendOption := h.GetAppend(); appendOption != nil { + if appendOption.GetValue() == false { + appendAction = envoy_config_core_v3.HeaderValueOption_OVERWRITE_IF_EXISTS_OR_ADD + } + } + out = append(out, &envoy_config_core_v3.HeaderValueOption{ Header: &envoy_config_core_v3.HeaderValue{ Key: header.GetKey(), Value: header.GetValue(), }, - Append: h.GetAppend(), + AppendAction: appendAction, }) } return out, nil diff --git a/projects/gloo/pkg/plugins/headers/plugin_test.go b/projects/gloo/pkg/plugins/headers/plugin_test.go index 77ea5990019..854ee0225c2 100644 --- a/projects/gloo/pkg/plugins/headers/plugin_test.go +++ b/projects/gloo/pkg/plugins/headers/plugin_test.go @@ -299,9 +299,9 @@ var testHeaderManip = &headers.HeaderManipulation{ } var expectedHeaders = envoyHeaderManipulation{ - RequestHeadersToAdd: []*envoy_config_core_v3.HeaderValueOption{{Header: &envoy_config_core_v3.HeaderValue{Key: "foo", Value: "bar"}, Append: &wrappers.BoolValue{Value: true}}}, + RequestHeadersToAdd: []*envoy_config_core_v3.HeaderValueOption{{Header: &envoy_config_core_v3.HeaderValue{Key: "foo", Value: "bar"}, AppendAction: envoy_config_core_v3.HeaderValueOption_APPEND_IF_EXISTS_OR_ADD}}, RequestHeadersToRemove: []string{"a"}, - ResponseHeadersToAdd: []*envoy_config_core_v3.HeaderValueOption{{Header: &envoy_config_core_v3.HeaderValue{Key: "foo", Value: "bar"}, Append: &wrappers.BoolValue{Value: true}}}, + ResponseHeadersToAdd: []*envoy_config_core_v3.HeaderValueOption{{Header: &envoy_config_core_v3.HeaderValue{Key: "foo", Value: "bar"}, AppendAction: envoy_config_core_v3.HeaderValueOption_APPEND_IF_EXISTS_OR_ADD}}, ResponseHeadersToRemove: []string{"b"}, } @@ -314,8 +314,8 @@ var testHeaderManipWithSecrets = &headers.HeaderManipulation{ } var expectedHeadersWithSecrets = envoyHeaderManipulation{ - RequestHeadersToAdd: []*envoy_config_core_v3.HeaderValueOption{{Header: &envoy_config_core_v3.HeaderValue{Key: "Authorization", Value: "basic dXNlcjpwYXNzd29yZA=="}, Append: &wrappers.BoolValue{Value: true}}}, + RequestHeadersToAdd: []*envoy_config_core_v3.HeaderValueOption{{Header: &envoy_config_core_v3.HeaderValue{Key: "Authorization", Value: "basic dXNlcjpwYXNzd29yZA=="}, AppendAction: envoy_config_core_v3.HeaderValueOption_APPEND_IF_EXISTS_OR_ADD}}, RequestHeadersToRemove: []string{"a"}, - ResponseHeadersToAdd: []*envoy_config_core_v3.HeaderValueOption{{Header: &envoy_config_core_v3.HeaderValue{Key: "foo", Value: "bar"}, Append: &wrappers.BoolValue{Value: true}}}, + ResponseHeadersToAdd: []*envoy_config_core_v3.HeaderValueOption{{Header: &envoy_config_core_v3.HeaderValue{Key: "foo", Value: "bar"}, AppendAction: envoy_config_core_v3.HeaderValueOption_APPEND_IF_EXISTS_OR_ADD}}, ResponseHeadersToRemove: []string{"b"}, } diff --git a/projects/gloo/pkg/translator/translator_test.go b/projects/gloo/pkg/translator/translator_test.go index 89eac1f0f00..3dd456c768f 100644 --- a/projects/gloo/pkg/translator/translator_test.go +++ b/projects/gloo/pkg/translator/translator_test.go @@ -1277,9 +1277,7 @@ var _ = Describe("Translator", func() { Key: "Authorization", Value: "basic dXNlcjpwYXNzd29yZA==", }, - Append: &wrappers.BoolValue{ - Value: true, - }, + AppendAction: envoy_config_core_v3.HeaderValueOption_APPEND_IF_EXISTS_OR_ADD, }, } @@ -2754,9 +2752,7 @@ var _ = Describe("Translator", func() { Key: "client-id", Value: "%REQ(client-id)%", }, - Append: &wrappers.BoolValue{ - Value: false, - }, + AppendAction: envoy_config_core_v3.HeaderValueOption_OVERWRITE_IF_EXISTS_OR_ADD, }, )) })