sigs.k8s.io/gateway-api@v1.0.0/geps/gep-726.md (about)

     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  
     8  This GEP proposes adding support for path redirects and rewrites in addition to
     9  host 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  
    20  Although many implementations support very advanced rewrite and redirect
    21  capabilities, the following are the most [portable](#portability) concepts that
    22  are not already supported by the Gateway API:
    23  
    24  * Path redirects
    25  * Path prefix redirects
    26  * Path prefix rewrites
    27  * Host rewrites
    28  
    29  Although regular expression based redirects and rewrites are commonly supported,
    30  there is significantly more variation in both if and how they are implemented.
    31  Given the wide support for this concept, it is important to design the API in a
    32  way that would make it easy to add this capability in the future.
    33  
    34  ### Path Modifiers
    35  
    36  Both redirects and rewrites would share the same `PathModifier` types:
    37  
    38  ```go
    39  // HTTPPathModifierType defines the type of path redirect.
    40  type HTTPPathModifierType string
    41  
    42  const (
    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.
    55  type 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  
    73  The existing `RequestRedirect` filter can be expanded to support path redirects.
    74  In the following example, a request to `/foo/abc` would be redirected to
    75  `/bar/abc`.
    76  
    77  ```yaml
    78  kind: HTTPRoute
    79  apiVersion: gateway.networking.k8s.io/v1alpha2
    80  metadata:
    81    name: http-filter-1
    82  spec:
    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  
    97  This would be represented with the following API addition to the existing
    98  HTTPRequestRedirect 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
   105  type 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  
   118  A new `URLRewrite` filter can be added to support rewrites. In the following
   119  example, a request to `example.com/foo/abc` would be rewritten to
   120  `example.net/bar/abc`.
   121  
   122  ```yaml
   123  kind: HTTPRoute
   124  apiVersion: gateway.networking.k8s.io/v1alpha2
   125  metadata:
   126    name: http-filter-1
   127  spec:
   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  
   144  This 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
   151  type 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  
   170  Note: `RequestRewrite` was originally considered as a name for this filter.
   171  `URLRewrite` was chosen as it more clearly represented the capabilities of the
   172  filter and would not be confused with header or query param modification.
   173  
   174  ## Portability
   175  
   176  When considering what should be possible in the API, it's worth evaluating what
   177  common tooling is capable of. This is by no means a complete list, but this
   178  provides a high level overview of how this is configured across different
   179  implementations.
   180  
   181  Although not all of these implementations directly support prefix rewrites or
   182  redirects, the ones that don't include regular expression support which can be
   183  used to implement prefix rewrites and redirects.
   184  
   185  Note: This section intentionally excludes the redirect capabilities already
   186  contained in the API.
   187  
   188  ### Envoy
   189  Envoy 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  
   198  Note that path rewrite relies on the prefix match for the route, there is not
   199  a way to differentiate between the prefix used for matching and rewriting.
   200  
   201  ### Google Cloud
   202  Google 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  
   211  Note that path rewrite relies on the prefix match for the route, there is not
   212  a way to differentiate between the prefix used for matching and rewriting.
   213  
   214  ### HAProxy
   215  HAProxy 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
   225  The 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
   234  There are two relatively common types of path rewrite/redirect that are not
   235  covered by this proposal:
   236  
   237  1. Replace a path prefix separate from the match
   238  2. Replace with a Regular Expression substitution
   239  
   240  Both of the following can be represented by adding a new field new types. For
   241  example, this config would result in a request to `/foo/baz` to be rewritten to
   242  `/bar/baz`:
   243  
   244  ```yaml
   245  filters:
   246  - type: RequestRewrite
   247    requestRewrite:
   248      path:
   249        type: ReplacePrefix
   250        pattern: /foo
   251        substitution: /bar
   252  ```
   253  
   254  Similarly, this config would result in a request to `/foo/bar/baz` being
   255  rewritten to `/foo/other/baz`.
   256  ```yaml
   257  filters:
   258  - type: RequestRewrite
   259    requestRewrite:
   260      path:
   261        type: RegularExpression
   262        pattern: /foo/(.*)/baz
   263        substitution: other
   264  ```
   265  
   266  Although both of the above are natural extensions of the API, they are not quite
   267  as broadly supported. For that reason, this GEP proposes omitting these types
   268  from the initial implementation.
   269  
   270  ## Alternatives
   271  
   272  ### 1. Generic Path Match Replacement
   273  Instead of the `ReplacePrefixMatch` option proposed above, we could have a
   274  `ReplacePathMatch` option. This would provide significantly more flexibility and
   275  room for growth than prefix replacement.
   276  
   277  Unfortunately it would be difficult to represent conformance and support levels.
   278  It also would have limited value. Replacing "Exact" match types would be nearly
   279  identical to the "Absolute" match type, and replacing "RegularExpression" match
   280  types would likely not yield the desired result. In most cases, RegEx rewrites
   281  are implemented separately from RegEx path matching. So a user may want to match
   282  all paths matching one RegEx, but use a separate RegEx + substitution value for
   283  rewrites.
   284  
   285  It is theoretically possible that future patch match types could be useful as a
   286  rewrite source, but the common proxies described above seem to be limited to the
   287  rewrite types described above.
   288  
   289  ### 2. Top Level Rewrite Fields
   290  Although a small difference, we could restructure how the path rewrites and
   291  redirects were configured. One example would be adding top level fields in the
   292  filters for each kind of path rewrite or redirect. That would result in a change
   293  like this:
   294  
   295  **Before:**
   296  ```yaml
   297  requestRewrite:
   298    hostname: foo.com
   299    path:
   300      type: Prefix
   301      substitution: /bar
   302  ```
   303  
   304  **After:**
   305  ```yaml
   306  requestRewrite:
   307    hostname: foo.com
   308    pathPrefix: /bar
   309  ```
   310  
   311  Although simpler for the initial use cases, it may become more difficult to
   312  maintain and validate as additional types of rewrites and redirects were added.
   313  
   314  
   315  ## References
   316  
   317  Issues:
   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)