Status: Accepted
This proposal describes how Contour will provide users the ability to rewrite HTTP cookie attributes.
The Set-Cookie
HTTP response header is used by an HTTP server to send a piece of data that a client can return to the server.
HTTP cookies can be used to correlate multiple requests from a single client, usually a browser.
Cookies are often used to implement a user "session."
The Set-Cookie
header must contain a cookie name and value to set, as well as attributes that determine how a client should determine whether it should send it along with a particular request.
Contour's "cookie" load balancing strategy enables users to implement "session affinity" by configuring Envoy to generate a cookie that can be returned in subsequent requests to route to a consistent backend app instance.
A commonly requested feature for Contour is to make attributes of this cookie configurable.
Users may want to apply security or other settings to ensure browsers treat these cookies appropriately.
For example, the SameSite
and Secure
attributes are currently not set by Envoy when it generates the X-Contour-Session-Affinity
.
In addition, some users may want to ensure certain attributes are set on application generated cookies.
These attributes may be things an application may not be able to accurately set, without prior knowledge of how the application is deployed.
For example, if Contour is in use to rewrite the path or hostname of a request before it reaches an application backend, the application may not be able to accurately set the Path
and Domain
attributes in a Set-Cookie
response header.
Envoy does not at the moment provide a native mechanism with which HTTP cookies can be modified. Envoy does of course provide the ability to write Lua code to implement custom logic for processing HTTP requests and responses. It appears most community solutions to cookie rewriting utilize this method to customize HTTP cookies. However, the Envoy project does appear to be open to an API change to allow some form of cookie rewriting.
- Provide users ability to follow best practices in cookie attribute settings
- Offer features in Contour/Envoy provided by other L7 proxies
- Address various open issues related to cookie attributes
- Provide full ability to configure Contour/Envoy with arbitrary Lua code
- Allow users to customize all aspects of an HTTP cookie (value, other attributes not mentioned below, etc.)
HTTPProxy
will have new fields to configure cookie rewrite policy.
Cookie attributes related to infrastructure and security considerations will be customizable.
Other fields, cookie names, and values will not be made customizable.
The fields we will offer as customizable are as follows:
- Path
- Domain
- Secure
- SameSite
Initially, the added field for cookie rewriting will be added to the Route
and Service
elements of the HTTPProxy
.
Envoy provides configuration of Lua using the typed_per_filter_config
on routes, virtual hosts, or weighted clusters.
See here for more.
Additionally, a future step may be to allow customization of the Contour/Envoy generated session affinity cookie directly in the "cookie" load balancing strategy.
This design will initially require users to customize that via the general setting on the HTTPProxy
Route
element.
We can possibly add some cookie manipulation parameters to the "cookie" load balancing strategy settings so the X-Contour-Session-Affinity
cookie can be customized more cleanly.
If/when we move/add the cookie-based hash to the request hash load balancing strategy we provide, we can provide cookie manipulation parameters there to customize any cookies generated by Contour/Envoy.
See this draft PR for an example of how we can move cookie-hashing to the request hash load balancing strategy.
We can include cookie manipulation parameters in the new CookieHashOptions
struct.
Below are the changes to the HTTPProxy
Route
and Service
elements and accompanying types:
type Route struct {
...
// +optional
CookieRewritePolicy []CookieRewritePolicy `json:"cookieRewritePolicy,omitempty"`
}
type Service struct {
...
// +optional
CookieRewritePolicy []CookieRewritePolicy `json:"cookieRewritePolicy,omitempty"`
}
type CookieRewritePolicy struct {
// Name is the name of the cookie for which attributes will be rewritten.
Name string `json:"name"`
// +optional
PathRewrite *CookiePathRewrite `json:"pathRewrite,omitempty"`
// +optional
DomainRewrite *CookieDomainRewrite `json:"domainRewrite,omitempty"`
// +optional
Secure *bool `json:"secure,omitempty"`
// +optional
// +kubebuilder:validation:Enum=Strict;Lax;None
SameSite *string `json:"sameSite,omitempty"`
}
type CookiePathRewrite struct {
// Value is the value to rewrite the Path attribute to.
// For now this is required.
// +required
// +kubebuilder:validation:Minimum=1
Value string `json:"value"`
}
type CookieDomainRewrite struct {
// Value is the value to rewrite the Domain attribute to.
// For now this is required.
// +required
// +kubebuilder:validation:Minimum=1
Value string `json:"value"`
}
Users will be able to optionally specify a list of cookies they would like to rewrite.
Cookies will be found in the set of Set-Cookie
headers set by the server's response by name and be rewritten accordingly.
For now only a direct replacement rather than regex rewrite or the like is provided but more advanced features could be developed in the future.
Each attribute provided is optional to change, not setting a value will mean the attribute if present will be left alone from the original response.
If an attribute that is requested to be rewritten is not present in the relevant Set-Cookie
header, it will be added to it.
The various fields users may configure need to validated based on RFC 6265. Particularly the cookie name is of importance as we are matching on that to find the correct cookie attributes to update.
In addition, cookie rewrite rules for a Route
and Service
will need to be collected and deduplicated.
One restriction that we may want to make is that multiple rewrite policies may not be supplied for the same cookie name on a given Route
or Service
.
In line with recent work around surfacing status and return status codes on HTTPProxy
misconfiguration, we should set detailed status conditions when there are configuration errors, similar to the processing that is done for request/response header policies.
We may first choose the method that currently exists, which is to invalidate the whole HTTPProxy
but move forward in the future to programming an HTTP 502 response code.
Initially at least, this feature will be implemented with Envoy's Lua filter using the envoy_on_response
hook.
We can use the fact that Envoy allows configuring Lua scripts per-route and per-cluster to ensure we only manipulate cookies in appropriate responses and do not have to do complicated matching in the global Lua scripts.
We will parse Set-Cookie
headers into a table keyed by the cookie name.
Each entry will be a table keyed by attribute name.
We should be able to generate code to simply iterate through and rewrite attributes accordingly.
We may choose to do everything with inline Lua or use the Lua filter's ability to supply a map of named source codes for the various scripts we need to generate.
In addition, parallel implementation on a native Envoy feature may provide us the ability to remove Lua completely in the future.
If/when we want to provide convenience fields to customize Contour/Envoy generated session affinity cookies, we can provide them in to the relevant configuration structs for those features. Implementing that should be as simple as matching on the correct cookies and performing the same manipulations as described above.
An addition to this design that is not present is to be provide the ability to configure all cookie attributes and possibly even names and values. This would give users a very powerful tool.
However, currently the design for this feature is such that we are only allowing configuration of things an app or app developer might not know to be able to program correctly. Attributes that are related to web security and the infrastructure an app runs with are currently made available to users only.
Other things like Max-Age
and Expires
are harder for a platform to justify setting and seem like they should be under control of the app, as they are likely application logic dependent.
However, this design does not limit us from adding the ability to manipulate these fields in the future.
One alternative could be to always automatically rewrite the Domain
and Path
elements to the correct values.
Hostname and path rewrite rules should be known by Contour and it could rewrite these values accordingly.
However, if we do default to this, it may not work for all cases, e.g. as a site may be returning a cookie for a specific or more general path and rewriting it automatically could be incorrect. Rather than provide a restrictive API, we can start out with giving users full manual flexibility. We may look to add an option to do automatic rewrites if users desire.
With this feature, we are allowing users to set security settings on cookies, which is an improvement on what Contour does today.
In terms of security and other considerations around Lua scripting, we are not letting users supply Lua code, and should be generating Lua scripts scoped to this feature. Input will be validated before Lua code is generated so we should not allow arbitrary code to be passed to Envoy.
Initially using the Envoy Lua filter, there should be no compatibility issues as this feature has existed for a while.
If we do end up getting the Envoy native feature merged, we can wait to merge the Lua implementation until a couple releases with the Envoy feature.
- In Contour version X, Envoy version Y we can implement feature with Lua.
- In Contour version X+1, Envoy version Y+1 (with native feature), we will still implement this feature with Lua in Contour
- In Contour version X+2, Envoy version Y+2, we can implement cookie reqriting with the Envoy native feature
An open question may be where this will fit into the Gateway API HTTPRoute
configuration.
Should this be an extension or some other mechanism which Contour can use to extend the base Gateway API fields?
Is it useful for a Contour administrator to be able to set a global policy on cookie attributes?
Is it useful for a HTTPProxy
"tree" to have a global set of policies on cookie attributes?