Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[1.14] Expose most_specific_header_mutations wins and use append_action #8705

Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
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
- type: FIX
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FIX, as it's a backport so we can't use NEW_FEATURE, but want this change to be visible to users.

description: |
Add support for `most_specific_header_mutations_wins` to allow the more specific header mutation to win. Toggling it on would reverse the evaluation order of the header mutations, allowing more specific header mutations to overwrite less specific.
https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route.proto#envoy-v3-api-field-config-route-v3-routeconfiguration-most-specific-header-mutations-wins
issueLink: https://github.com/solo-io/gloo/issues/8690
resolvesIssue: false
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ Headers can be inherited by children objects, 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
Expand Down Expand Up @@ -275,4 +276,63 @@ Headers can be inherited by children objects, 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`.

### Reversing the order of header manipulation evaluation

You can reverse the order in which header manipulations are evaluated so that order of evaluation becomes: global level headers, virtual host level headers, route level headers, and finally weighted cluster level headers.
With the order of evaluation being reversed, more specific header manipulations can override less specific ones.

To reverse the order of evaluation, set the `mostSpecificHeaderMutationsWins` field to `true` in the [routeOptions]({{< versioned_link_path fromRoot="/reference/api/github.com/solo-io/gloo/projects/gloo/api/v1/options.proto.sk/#routeconfigurationoptions" >}}) settings for a [Gateway]({{< versioned_link_path fromRoot="/reference/api/github.com/solo-io/gloo/projects/gateway/api/v1/gateway.proto.sk/" >}}).

The route options that you set on the Gateway will apply to all routes that the Gateway serves.

```yaml
apiVersion: gateway.solo.io/v1
kind: Gateway
metadata: # collapsed for brevity
spec:
routeOptions:
mostSpecificHeaderMutationsWins: true
status: # collapsed for brevity
```

Following the VirtualService and RouteTable setup in the previous section, add the `append` setting to `false` on the RouteTable. This way, the header values added by the RouteTable override less specific headers instead of append to the values.
{{< highlight yaml "hl_lines=25-25" >}}
apiVersion: gateway.solo.io/v1
kind: RouteTable
metadata:
name: 'a-routes'
namespace: 'a'
spec:
routes:
- matchers:
# the path matchers in this RouteTable must begin with the prefix `/a/`
- prefix: '/a/1'
routeAction:
single:
upstream:
name: 'foo-upstream'
options:
headerManipulation:
requestHeadersToAdd:
- header:
key: x-route-table
value: a
responseHeadersToAdd:
- header:
key: x-route-table
value: a
append: false
{{< /highlight >}}

With `mostSpecificHeaderMutationsWins` set to `true` and `append` set to `false`, now the `x-route-table` response header in the child RouteTable overwrites the parent object's value of `alphabet` instead to `a`.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions install/helm/gloo/crds/gateway.solo.io_v1_Gateway.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4570,6 +4570,9 @@ spec:
minimum: 0
nullable: true
type: integer
mostSpecificHeaderMutationsWins:
nullable: true
type: boolean
type: object
ssl:
type: boolean
Expand Down
3 changes: 3 additions & 0 deletions install/helm/gloo/crds/gloo.solo.io_v1_Proxy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,9 @@ spec:
minimum: 0
nullable: true
type: integer
mostSpecificHeaderMutationsWins:
nullable: true
type: boolean
type: object
sslConfigurations:
items:
Expand Down
11 changes: 9 additions & 2 deletions pkg/utils/api_conversion/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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:
Expand All @@ -117,7 +124,7 @@ func ToEnvoyHeaderValueOptions(option *envoycore_sk.HeaderValueOption, secrets *
Key: key,
Value: value,
},
Append: option.GetAppend(),
AppendAction: appendAction,
})
}
return result, nil
Expand Down
4 changes: 1 addition & 3 deletions pkg/utils/api_conversion/type_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{})
Expand Down
8 changes: 6 additions & 2 deletions projects/gateway/pkg/translator/translator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ var _ = Describe("Translator", func() {
GatewayType: &v1.Gateway_HttpGateway{
HttpGateway: &v1.HttpGateway{},
},
BindPort: 2,
RouteOptions: &gloov1.RouteConfigurationOptions{},
BindPort: 2,
},
{
Metadata: &core.Metadata{Namespace: ns2, Name: "name2"},
Expand All @@ -56,7 +57,8 @@ var _ = Describe("Translator", func() {
},
BindPort: 2,
RouteOptions: &gloov1.RouteConfigurationOptions{
MaxDirectResponseBodySizeBytes: &wrappers.UInt32Value{Value: 2048},
MaxDirectResponseBodySizeBytes: &wrappers.UInt32Value{Value: 2048},
MostSpecificHeaderMutationsWins: &wrappers.BoolValue{Value: true},
},
},
},
Expand Down Expand Up @@ -128,8 +130,10 @@ var _ = Describe("Translator", func() {
snap.Gateways[0].Options = &gloov1.ListenerOptions{
AccessLoggingService: als,
}
Expect(snap.Gateways[0].RouteOptions.MostSpecificHeaderMutationsWins).To(BeNil())

Expect(snap.Gateways[1].RouteOptions.MaxDirectResponseBodySizeBytes.Value).To(BeEquivalentTo(2048))
Expect(snap.Gateways[1].RouteOptions.MostSpecificHeaderMutationsWins.Value).To(BeTrue())

httpGateway := snap.Gateways[0].GetHttpGateway()
Expect(httpGateway).NotTo(BeNil())
Expand Down
5 changes: 5 additions & 0 deletions projects/gloo/api/v1/options.proto
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@ message RouteConfigurationOptions {
// Please refer to the [Envoy documentation](https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route.proto#envoy-v3-api-field-config-route-v3-routeconfiguration-max-direct-response-body-size-bytes)
// for more details about the `max_direct_response_body_size_bytes` attribute.
google.protobuf.UInt32Value max_direct_response_body_size_bytes = 1;

// By default, headers that should be added/removed are evaluated from most to least specific.
// To allow setting overrides at the route or virtual host level, this order can be reversed by setting this option to true.
// Refer to the [Envoy documentation](https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route.proto#envoy-v3-api-field-config-route-v3-routeconfiguration-most-specific-header-mutations-wins) for more details.
google.protobuf.BoolValue most_specific_header_mutations_wins = 2;
}

// Optional, feature-specific configuration that lives on http listeners
Expand Down
5 changes: 3 additions & 2 deletions projects/gloo/api/v1/options/headers/headers.proto
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
6 changes: 6 additions & 0 deletions projects/gloo/pkg/api/v1/options.pb.clone.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions projects/gloo/pkg/api/v1/options.pb.equal.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading