...

Text file src/sigs.k8s.io/gateway-api/geps/gep-726.md

Documentation: sigs.k8s.io/gateway-api/geps

     1# GEP-726: Add Path Redirects and Rewrites
     2
     3* Issue: [#726](https://github.com/kubernetes-sigs/gateway-api/issues/726)
     4* Status: Standard
     5
     6## TLDR
     7
     8This GEP proposes adding support for path redirects and rewrites in addition to
     9host rewrites. This would augment the existing host redirection capabilities.
    10
    11## Goals
    12
    13* Implement path redirects.
    14* Implement the most portable and simple forms of path rewrites.
    15* Describe how more advanced rewrite and redirect and redirect capabilities
    16  could be added in the future.
    17
    18## API
    19
    20Although many implementations support very advanced rewrite and redirect
    21capabilities, the following are the most [portable](#portability) concepts that
    22are not already supported by the Gateway API:
    23
    24* Path redirects
    25* Path prefix redirects
    26* Path prefix rewrites
    27* Host rewrites
    28
    29Although regular expression based redirects and rewrites are commonly supported,
    30there is significantly more variation in both if and how they are implemented.
    31Given the wide support for this concept, it is important to design the API in a
    32way that would make it easy to add this capability in the future.
    33
    34### Path Modifiers
    35
    36Both redirects and rewrites would share the same `PathModifier` types:
    37
    38```go
    39// HTTPPathModifierType defines the type of path redirect.
    40type HTTPPathModifierType string
    41
    42const (
    43  // This type of modifier indicates that the complete path will be replaced by
    44  // the path redirect value.
    45  AbsoluteHTTPPathModifier HTTPPathModifierType = "Absolute"
    46
    47  // This type of modifier indicates that any prefix path matches will be
    48  // replaced by the substitution value. For example, a path with a prefix match
    49  // of "/foo" and a ReplacePrefixMatch substitution of "/bar" will have the
    50  // "/foo" prefix replaced with "/bar" in matching requests.
    51  PrefixMatchHTTPPathModifier HTTPPathModifierType = "ReplacePrefixMatch"
    52)
    53
    54// HTTPPathModifier defines configuration for path modifiers.
    55type HTTPPathModifier struct {
    56  // Type defines the type of path modifier.
    57  //
    58  // +kubebuilder:validation:Enum=Absolute;ReplacePrefixMatch
    59  Type HTTPPathModifierType `json:"type"`
    60
    61  // Substitution defines the HTTP path value to substitute. An empty value ("")
    62  // indicates that the portion of the path to be changed should be removed from
    63  // the resulting path. For example, a request to "/foo/bar" with a prefix
    64  // match of "/foo" would be modified to "/bar".
    65  //
    66  // +kubebuilder:validation:MaxLength=1024
    67  Substitution string `json:"substitution"`
    68}
    69```
    70
    71### Redirects
    72
    73The existing `RequestRedirect` filter can be expanded to support path redirects.
    74In the following example, a request to `/foo/abc` would be redirected to
    75`/bar/abc`.
    76
    77```yaml
    78kind: HTTPRoute
    79apiVersion: gateway.networking.k8s.io/v1alpha2
    80metadata:
    81  name: http-filter-1
    82spec:
    83  rules:
    84    - matches:
    85      - path:
    86          type: Prefix
    87          value: /foo
    88      filters:
    89      - type: RequestRedirect
    90        requestRedirect:
    91          hostname: foo.com
    92          path:
    93            type: ReplacePrefixMatch
    94            value: /bar
    95```
    96
    97This would be represented with the following API addition to the existing
    98HTTPRequestRedirect filter:
    99```go
   100// HTTPRequestRedirect defines a filter that redirects a request. At most one of
   101// these filters may be used on a Route rule. This may not be used on the same
   102// Route rule as a HTTPRequestRewrite filter.
   103//
   104// Support: Extended
   105type HTTPRequestRedirect struct {
   106  // Path defines a path redirect.
   107  //
   108  // Support: Extended
   109  //
   110  // +optional
   111  Path *HTTPPathModifier `json:"path,omitempty"`
   112  // ...
   113}
   114```
   115
   116### Rewrites
   117
   118A new `URLRewrite` filter can be added to support rewrites. In the following
   119example, a request to `example.com/foo/abc` would be rewritten to
   120`example.net/bar/abc`.
   121
   122```yaml
   123kind: HTTPRoute
   124apiVersion: gateway.networking.k8s.io/v1alpha2
   125metadata:
   126  name: http-filter-1
   127spec:
   128  hostnames:
   129  - example.com
   130  rules:
   131    - matches:
   132      - path:
   133          type: Prefix
   134          value: /foo
   135      filters:
   136      - type: URLRewrite
   137        requestRewrite:
   138          hostname: example.net
   139          path:
   140            type: ReplacePrefixMatch
   141            substitution: /bar
   142```
   143
   144This would be represent with the following API additions:
   145```go
   146// HTTPURLRewrite defines a filter that modifies a request during forwarding.
   147// At most one of these filters may be used on a Route rule. This may not be
   148// used on the same Route rule as a HTTPRequestRedirect filter.
   149//
   150// Support: Extended
   151type HTTPURLRewrite struct {
   152  // Hostname is the value to be used to replace the Host header value during
   153  // forwarding.
   154  //
   155  // Support: Extended
   156  //
   157  // +optional
   158  // +kubebuilder:validation:MaxLength=255
   159  Hostname *string `json:"hostname,omitempty"`
   160
   161  // Path defines a path rewrite.
   162  //
   163  // Support: Extended
   164  //
   165  // +optional
   166  Path *HTTPPathModifier `json:"path,omitempty"`
   167}
   168```
   169
   170Note: `RequestRewrite` was originally considered as a name for this filter.
   171`URLRewrite` was chosen as it more clearly represented the capabilities of the
   172filter and would not be confused with header or query param modification.
   173
   174## Portability
   175
   176When considering what should be possible in the API, it's worth evaluating what
   177common tooling is capable of. This is by no means a complete list, but this
   178provides a high level overview of how this is configured across different
   179implementations.
   180
   181Although not all of these implementations directly support prefix rewrites or
   182redirects, the ones that don't include regular expression support which can be
   183used to implement prefix rewrites and redirects.
   184
   185Note: This section intentionally excludes the redirect capabilities already
   186contained in the API.
   187
   188### Envoy
   189Envoy supports the following relevant capabilities
   190([reference](https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route_components.proto)):
   191
   192* path_redirect (redirect only)
   193* prefix_rewrite (redirect and forwarding)
   194* regex_rewrite (redirect and forwarding)
   195* host_rewrite_literal (forwarding only)
   196* strip_query (redirect only)
   197
   198Note that path rewrite relies on the prefix match for the route, there is not
   199a way to differentiate between the prefix used for matching and rewriting.
   200
   201### Google Cloud
   202Google Cloud URL Maps support the following relevant capabilities
   203([reference](https://cloud.google.com/compute/docs/reference/rest/v1/urlMaps)):
   204
   205* pathPrefixRewrite (forwarding only)
   206* hostRewrite (forwarding only)
   207* pathRedirect (redirect only)
   208* prefixRedirect (redirect only)
   209* stripQuery (redirect only)
   210
   211Note that path rewrite relies on the prefix match for the route, there is not
   212a way to differentiate between the prefix used for matching and rewriting.
   213
   214### HAProxy
   215HAProxy supports the following relevant capabilities
   216([reference](https://cbonte.github.io/haproxy-dconv/2.5/configuration.html)):
   217
   218* http-request set-path (advanced path rewrite capabilities)
   219* http-request replace-path (rewrites entire path)
   220* http-request replace-pathq (rewrites entire path + query string)
   221* http-request replace-uri (URI rewrite based on input regex)
   222* redirect location (advanced redirect capabilities)
   223
   224### NGINX
   225The NGINX rewrite module contains the following relevant capabilities
   226([reference](http://nginx.org/en/docs/http/ngx_http_rewrite_module.html)):
   227
   228* PCRE regex based rewrites
   229* Rewrite directive can be used during forwarding or redirects
   230* Rewrite directive can affect host, path, or both
   231* Rewrite directive can be chained
   232
   233## Future Extension
   234There are two relatively common types of path rewrite/redirect that are not
   235covered by this proposal:
   236
   2371. Replace a path prefix separate from the match
   2382. Replace with a Regular Expression substitution
   239
   240Both of the following can be represented by adding a new field new types. For
   241example, this config would result in a request to `/foo/baz` to be rewritten to
   242`/bar/baz`:
   243
   244```yaml
   245filters:
   246- type: RequestRewrite
   247  requestRewrite:
   248    path:
   249      type: ReplacePrefix
   250      pattern: /foo
   251      substitution: /bar
   252```
   253
   254Similarly, this config would result in a request to `/foo/bar/baz` being
   255rewritten to `/foo/other/baz`.
   256```yaml
   257filters:
   258- type: RequestRewrite
   259  requestRewrite:
   260    path:
   261      type: RegularExpression
   262      pattern: /foo/(.*)/baz
   263      substitution: other
   264```
   265
   266Although both of the above are natural extensions of the API, they are not quite
   267as broadly supported. For that reason, this GEP proposes omitting these types
   268from the initial implementation.
   269
   270## Alternatives
   271
   272### 1. Generic Path Match Replacement
   273Instead of the `ReplacePrefixMatch` option proposed above, we could have a
   274`ReplacePathMatch` option. This would provide significantly more flexibility and
   275room for growth than prefix replacement.
   276
   277Unfortunately it would be difficult to represent conformance and support levels.
   278It also would have limited value. Replacing "Exact" match types would be nearly
   279identical to the "Absolute" match type, and replacing "RegularExpression" match
   280types would likely not yield the desired result. In most cases, RegEx rewrites
   281are implemented separately from RegEx path matching. So a user may want to match
   282all paths matching one RegEx, but use a separate RegEx + substitution value for
   283rewrites.
   284
   285It is theoretically possible that future patch match types could be useful as a
   286rewrite source, but the common proxies described above seem to be limited to the
   287rewrite types described above.
   288
   289### 2. Top Level Rewrite Fields
   290Although a small difference, we could restructure how the path rewrites and
   291redirects were configured. One example would be adding top level fields in the
   292filters for each kind of path rewrite or redirect. That would result in a change
   293like this:
   294
   295**Before:**
   296```yaml
   297requestRewrite:
   298  hostname: foo.com
   299  path:
   300    type: Prefix
   301    substitution: /bar
   302```
   303
   304**After:**
   305```yaml
   306requestRewrite:
   307  hostname: foo.com
   308  pathPrefix: /bar
   309```
   310
   311Although simpler for the initial use cases, it may become more difficult to
   312maintain and validate as additional types of rewrites and redirects were added.
   313
   314
   315## References
   316
   317Issues:
   318
   319- [#200: Add support for configurable HTTP redirects](https://github.com/kubernetes-sigs/gateway-api/issues/200)
   320- [#678: Add support for HTTP rewrites](https://github.com/kubernetes-sigs/gateway-api/issues/678)

View as plain text