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

     1  # GEP-746: Replace Cert Refs on HTTPRoute with Cross Namespace Refs from Gateway
     2  
     3  * Issue: [#746](https://github.com/kubernetes-sigs/gateway-api/issues/746)
     4  * Status: Standard
     5  
     6  ## TLDR
     7  
     8  This GEP proposes that we should remove TLS Certificate references from
     9  HTTPRoute and replace them with Cross Namespace Certificate references from
    10  Gateways. Although that is not a complete replacement on its own, this GEP shows
    11  how a controller could provide the rest of the functionality with this approach.
    12  
    13  ## Goals
    14  
    15  * Remove a confusing and underspecified part of the API - cert refs on
    16    HTTPRoute.
    17  * Add the ability to reference certificates in other namespaces from Gateways
    18    to replace much of the functionality that was enabled by cert refs on
    19    HTTPRoute.
    20  * Describe how a controller could automate self service cert attachment to
    21    Gateway listeners.
    22  
    23  ## Non-Goals
    24  
    25  * Actually provide a core implementation of a controller that can enable self
    26    service cert attachment. This may be worth considering at a later point, but
    27    is out of scope for this GEP.
    28  
    29  ## Introduction
    30  
    31  TLS Certificate references on HTTPRoute have always been a confusing part of the
    32  Gateway API. In the v1alpha2 release, we should consider removing this feature
    33  while we still can. This GEP proposes an alternative that is simpler to work
    34  with and understand, while also leaving sufficient room to enable all the same
    35  capabilities that certificate references on HTTPRoute enabled.
    36  
    37  ### Attaching TLS Certificates with Routes is Confusing
    38  One of the most confusing parts of the Gateway API is how certificates can be
    39  attached to Routes. There are a variety of different factors that lead to
    40  confusion here:
    41  
    42  * It can be natural to assume that a certificate attached to a Route only
    43    applies to that Route. In reality, it applies to the entire listener(s)
    44    associated with that Route.
    45  * This means that a Route can affect any other Routes attached to the same
    46    Gateway Listener. By attaching a Route to a Gateway Listener, you’re
    47    implicitly trusting all other Routes attached to that Gateway Listener.
    48  * When multiple Routes specify a certificate for the same Listener, it’s
    49    possible that they will conflict and create more confusion.
    50  
    51  ### Why We Did It
    52  To understand how we ended up with the ability to attach TLS certificates with
    53  Routes, it’s helpful to look at the use cases for this capability:
    54  
    55  1. Some users want Route owners to be able to attach arbitrary domains and certs
    56     to a Gateway listener.
    57     [#103](https://github.com/kubernetes-sigs/gateway-api/issues/103)
    58  1. Some users want Route owners to control certs for their applications.
    59  
    60  ### Alternative Solutions
    61  
    62  #### 1. Automation with tools like Cert-Manager
    63  When automation is acceptable, the first use case is entirely possible with
    64  tools like cert-manager that can watch Routes, generate certs for them, and
    65  attach them to a Gateway.
    66  
    67  #### 2. Cross Namespace Cert Direct References from Gateways
    68  With the already established ReferenceGrant concept, we have established a safe
    69  way to reference resources across namespaces. Although this would require some
    70  coordination between Gateway and App owners, it would enable App owners to
    71  retain full control of the certs used by their app without the extra confusion
    72  that certs in HTTPRoute have led to.
    73  
    74  ### Enabling Self-Service Certificate Attachment for App Owners
    75  Although this dramatically simplifies the API, it does not completely replace
    76  the functionality that certs attached to HTTPRoutes enabled. Most notably, it
    77  would be difficult to attach arbitrary self-provided certificates to a Gateway
    78  listener without requiring manual changes from a Gateway admin.
    79  
    80  There are a couple potential solutions here:
    81  
    82  #### 1. Implement a selector for cert references instead of direct references
    83  Although the simplicity of this approach is nice, it ends up with many of the
    84  same problems as certificates attached to Routes have and feels inconsistent
    85  with how Routes attach to Gateways.
    86  
    87  #### 2. Implement a controller that attaches certificates to Gateway listeners
    88  Similar to cert-manager, it could be possible to implement a controller that
    89  watches for Secrets with a certain label, and attaches those to the specified
    90  Gateway. Although it's out of scope for this GEP to completely define what a
    91  controller like this could look like, it would likely need to include at least
    92  one of the following safeguards:
    93  
    94  1. A way to configure which namespaces could attach certificates for each
    95     domain.
    96  2. A way to configure which namespaces could attach certificates to each
    97     Gateway (or Listener).
    98  3. A way to use ReferenceGrant to indicate where references from Secrets to
    99     Gateways were trusted from and to.
   100  
   101  ## API
   102  
   103  The API changes proposed here are quite small, mostly removing fields.
   104  
   105  ### Changes
   106  1. The `LocalObjectReference` used for the `CertificateRef` field in
   107     `GatewayTLSConfig` would be replaced with an `ObjectReference`.
   108  1. `ReferenceGrant` would be updated to note that references from Gateways to
   109     Secrets were part of the Core support level.
   110  
   111  ### Removals
   112  
   113  From HTTPRouteSpec:
   114  ```go
   115      // TLS defines the TLS certificate to use for Hostnames defined in this
   116      // Route. This configuration only takes effect if the AllowRouteOverride
   117      // field is set to true in the associated Gateway resource.
   118      //
   119      // Collisions can happen if multiple HTTPRoutes define a TLS certificate
   120      // for the same hostname. In such a case, conflict resolution guiding
   121      // principles apply, specifically, if hostnames are same and two different
   122      // certificates are specified then the certificate in the
   123      // oldest resource wins.
   124      //
   125      // Please note that HTTP Route-selection takes place after the
   126      // TLS Handshake (ClientHello). Due to this, TLS certificate defined
   127      // here will take precedence even if the request has the potential to
   128      // match multiple routes (in case multiple HTTPRoutes share the same
   129      // hostname).
   130      //
   131      // Support: Core
   132      //
   133      // +optional
   134      TLS *RouteTLSConfig `json:"tls,omitempty"`
   135  ```
   136  
   137  And the associated struct:
   138  ```go
   139  // RouteTLSConfig describes a TLS configuration defined at the Route level.
   140  type RouteTLSConfig struct {
   141      // CertificateRef is a reference to a Kubernetes object that contains a TLS
   142      // certificate and private key. This certificate is used to establish a TLS
   143      // handshake for requests that match the hostname of the associated HTTPRoute.
   144      // The referenced object MUST reside in the same namespace as HTTPRoute.
   145      //
   146      // CertificateRef can reference a standard Kubernetes resource, i.e. Secret,
   147      // or an implementation-specific custom resource.
   148      //
   149      // Support: Core (Kubernetes Secrets)
   150      //
   151      // Support: Implementation-specific (Other resource types)
   152      //
   153      CertificateRef LocalObjectReference `json:"certificateRef"`
   154  }
   155  ```
   156  
   157  From GatewayTlsConfig:
   158  ```go
   159      // RouteOverride dictates if TLS settings can be configured
   160      // via Routes or not.
   161      //
   162      // CertificateRef must be defined even if `routeOverride.certificate` is
   163      // set to 'Allow' as it will be used as the default certificate for the
   164      // listener.
   165      //
   166      // Support: Core
   167      //
   168      // +optional
   169      // +kubebuilder:default={certificate:Deny}
   170      RouteOverride *TLSOverridePolicy `json:"routeOverride,omitempty"`
   171  ```
   172  
   173  And the associated types:
   174  ```go
   175  type TLSRouteOverrideType string
   176  
   177  const (
   178      // Allows the parameter to be configured from all routes.
   179      TLSROuteOVerrideAllow TLSRouteOverrideType = "Allow"
   180  
   181      // Prohibits the parameter from being configured from any route.
   182      TLSRouteOverrideDeny TLSRouteOverrideType = "Deny"
   183  )
   184  
   185  // TLSOverridePolicy defines a schema for overriding TLS settings at the Route
   186  // level.
   187  type TLSOverridePolicy struct {
   188      // Certificate dictates if TLS certificates can be configured
   189      // via Routes. If set to 'Allow', a TLS certificate for a hostname
   190      // defined in a Route takes precedence over the certificate defined in
   191      // Gateway.
   192      //
   193      // Support: Core
   194      //
   195      // +optional
   196      // +kubebuilder:default=Deny
   197      Certificate *TLSRouteOverrideType `json:"certificate,omitempty"`
   198  }
   199  ```
   200  
   201  ## Prior Art
   202  
   203  OpenShift already supports configuring TLS certificates on Routes. Although
   204  largely similar to the Gateway API approach, there are some notable differences:
   205  
   206  * Each Route can specify a maximum of 1 hostname
   207  * When a Route is attached to a hostname, newer Routes can't use the same
   208    hostname unless all of the following are true:
   209      * The Routes are in the same namespace or the Router is configured to allow
   210        sharing hostnames across namespaces
   211      * The Routes have unique, non-overlapping paths specified
   212      * The Routes are not TCP or TLS routes
   213  
   214  A typical configuration would involve a Router with `*.example.com` that has a
   215  wildcard cert. Routes could be attached within those constraints without the
   216  need for a cert. Routes can also use a different hostname if they also provide a
   217  cert.
   218  
   219  ## Alternatives
   220  
   221  ### 1. Improved Documentation + Extended Support Level
   222  My first attempt to improve this was to create a
   223  [PR](https://github.com/kubernetes-sigs/gateway-api/pull/739) that would clarify
   224  the documentation around how this works and lower the support level to extended.
   225  
   226  Trying to improve the documentation around this feature made it clear how easy
   227  it would be to get confused by how it worked. It would be only natural to assume
   228  that a cert attached to a Route would only apply to that Route. The conflict
   229  resolution semantics associated with this were both complicated and difficult to
   230  surface to a user through status or other means.
   231  
   232  Lowering the support level from core to extended also didn't make sense.
   233  Although some implementers were uncomfortable with supporting this feature due
   234  to the potential for vulnerabilities, that was not a sufficient reason to lower
   235  the support level. An extended support level should only be used for features
   236  that cannot be universally supported. That was not the case here. Instead there
   237  were just very real questions around the safety of the feature.
   238  
   239  The combination of those 2 factors led me to believe that this feature was not
   240  well thought out and should be removed. Since this was essentially just a
   241  shortcut to attaching certificates to a Gateway listener from different sources,
   242  it seemed like there had to be a way that was both safer and easier to
   243  understand. That led to this proposal.
   244  
   245  ### 2. Implement Hostname Restrictions
   246  Similar to the OpenShift approach described above, we could enforce the
   247  following:
   248  
   249  1. Only a single hostname may be specified for HTTPRoutes with a certificate
   250     reference.
   251  1. The oldest HTTPRoute to attach a certificate to a hostname would effectively
   252     own that hostname. No other HTTPRoutes could be attached with the same
   253     hostname unless they were explicitly allowed by that HTTPRoute.
   254  
   255  The second condition would be difficult to validate. As we've seen elsewhere in
   256  the API, it's difficult to determine which resource was first to claim a
   257  hostname or path. Instead we have to rely on the oldest resource, which can
   258  result in some weird and potentially breaking changes if an older resource
   259  chooses to claim a hostname.
   260  
   261  ## References
   262  
   263  Docs:
   264  
   265  * [Gateway API: Replacing TLS Certificates in Routes](https://docs.google.com/document/d/1Cv95XFCL6S_9pIyS0drnsDLsfinWc2tHOFl_x3-_SWI/edit)