Skip to content

Commit 2ed3cd6

Browse files
committed
GEP-2643: TLSRoute
1 parent 1da7c37 commit 2ed3cd6

File tree

2 files changed

+251
-0
lines changed

2 files changed

+251
-0
lines changed

geps/gep-2643/index.md

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
2+
# GEP-2643: TLS based passthrough Route / TLSRoute
3+
4+
* Issue: [#2643](https://github.com/kubernetes-sigs/gateway-api/issues/2643)
5+
* Status: Standard
6+
7+
## TLDR
8+
9+
This GEP documents the implementation of a route type that uses the Server Name
10+
Indication attribute (aka SNI) of a TLS handshake to make the routing decision.
11+
12+
This is also known as TLS passthrough, where after the server name is identified,
13+
the gateway does a full encrypted passthrough of the communication.
14+
15+
## Goals
16+
17+
* Provide a TLS passthrough route type, based on the SNI identification.
18+
* Provide a load balancing of a passthrough’d route, allowing a user to define
19+
a route, based on the SNI identification that should pass the traffic to N load
20+
balanced backends
21+
22+
## Longer Term Goals
23+
* Implement capabilities for [ESNI](https://www.cloudflare.com/learning/ssl/what-is-encrypted-sni/)
24+
25+
## Non-Goals
26+
* Provide an interface for users to define different listeners or ports for the
27+
`TLSRoute` - This will be covered by the `ListenerSet` enhancement.
28+
* Replace `TCPRoute` for cases where terminated frontend encryption is not needed.
29+
* When using `TLSRoute` passthrough, support `PROXY` protocol on the gateway listener.
30+
* When using `TLSRoute` passthrough, support `PROXY` protocol on the communication
31+
between the Gateway and the backend.
32+
* Allow a `TLSRoute` to have the encryption termination on the `Gateway` side and
33+
do the unencrypted passthrough. This case is covered by using a `TCPRoute`.
34+
* The feature of TLSRoute termination is tracked on its own [issue](https://github.com/kubernetes-sigs/gateway-api/issues/2111)
35+
and should be covered on a specific GEP.
36+
* Support TLS over UDP or UDP-based protocols such as `QUIC`.
37+
* Support attributes other than the SNI/hostname for the route selection
38+
39+
## Introduction
40+
41+
While many application routing cases can be implemented using HTTP/L7 matching
42+
(the tuple protocol:hostname:port:path), there are some specific cases where direct,
43+
encrypted communication to the backend may be required, without further assertion. For example:
44+
* A backend that is TLS based but not HTTP based (e.g., a Kafka service, or a
45+
Postgres service, with its listener being TLS enabled).
46+
* Some WebRTC solutions.
47+
* Backends that can require direct client-certificate authentication (e.g., OAuth).
48+
49+
For the example cases above, it is desired that the routing is made as a passthrough
50+
mode, where the `Gateway` passes the packets to the backend without terminating TLS.
51+
52+
While this routing is possible using other mechanisms such as a `TCPRoute` or a
53+
Kubernetes service of type `LoadBalancer`, it may be desired to have a single point
54+
of traffic convergence (like one single `LoadBalancer`) for reasons like cost,
55+
traffic control, etc.
56+
57+
An implementation of `TLSRoute` achieves this end using the
58+
[server_name TLS attribute](https://datatracker.ietf.org/doc/html/rfc6066#section-3)
59+
to determine what backend should be used for a given request. `TLSRoute` thereby
60+
enables the use of a single gateway listener to handle traffic for multiple routes.
61+
62+
## API
63+
64+
```golang
65+
type TLSRouteSpec struct {
66+
// Hostnames defines a set of SNI hostnames that should match against the
67+
// SNI attribute of TLS ClientHello message in TLS handshake. This matches
68+
// the RFC 1123 definition of a hostname with 2 notable exceptions:
69+
//
70+
// 1. IPs are not allowed in SNI hostnames per RFC 6066.
71+
// 2. A hostname may be prefixed with a wildcard label (`*.`). The wildcard
72+
// label must appear by itself as the first label.
73+
//
74+
// If a hostname is specified by both the Listener and TLSRoute, there
75+
// must be at least one intersecting hostname for the TLSRoute to be
76+
// attached to the Listener. For example:
77+
//
78+
// * A Listener with `test.example.com` as the hostname matches TLSRoutes
79+
// that have specified at least one of `test.example.com` or
80+
// `*.example.com`.
81+
// * A Listener with `*.example.com` as the hostname matches TLSRoutes
82+
// that have specified at least one hostname that matches the Listener
83+
// hostname. For example, `test.example.com` and `*.example.com` would both
84+
// match. On the other hand, `example.com` and `test.example.net` would not
85+
// match.
86+
//
87+
// If both the Listener and TLSRoute have specified hostnames, any
88+
// TLSRoute hostnames that do not match the Listener hostname MUST be
89+
// ignored. For example, if a Listener specified `*.example.com`, and the
90+
// TLSRoute specified `test.example.com` and `test.example.net`,
91+
// `test.example.net` must not be considered for a match.
92+
//
93+
// If both the Listener and TLSRoute have specified hostnames, and none
94+
// match with the criteria above, then the TLSRoute is not accepted. The
95+
// implementation must raise an 'Accepted' Condition with a status of
96+
// `False` in the corresponding RouteParentStatus.
97+
// +required
98+
// +kubebuilder:validation:MinItems=1
99+
// +kubebuilder:validation:MaxItems=16
100+
Hostnames []Hostname `json:"hostnames,omitempty"`
101+
// Rules are a list of actions.
102+
//
103+
// +required
104+
// +kubebuilder:validation:MinItems=1
105+
// +kubebuilder:validation:MaxItems=1
106+
Rules []TLSRouteRule `json:"rules,omitempty"`
107+
}
108+
109+
type TLSRouteRule struct {
110+
// Name is the name of the route rule. This name MUST be unique within a Route if it is set.
111+
//
112+
// +optional
113+
Name *SectionName `json:"name,omitempty"`
114+
// BackendRefs (same as other BackendRef here)
115+
// +required
116+
// +kubebuilder:validation:MinItems=1
117+
// +kubebuilder:validation:MaxItems=16
118+
BackendRefs []BackendRef `json:"backendRefs,omitempty"`
119+
}
120+
```
121+
122+
123+
## Request flow
124+
125+
A simple `TLSRoute` implementation manifest would look like:
126+
127+
```yaml
128+
apiVersion: gateway.networking.k8s.io/v1
129+
kind: Gateway
130+
metadata:
131+
name: gateway-tlsroute
132+
spec:
133+
gatewayClassName: "my-class"
134+
listeners:
135+
- name: somelistener
136+
port: 443
137+
protocol: TLS
138+
hostname: "*.example.com"
139+
allowedRoutes:
140+
namespaces:
141+
from: Same
142+
kinds:
143+
- kind: TLSRoute
144+
tls:
145+
mode: Passthrough
146+
---
147+
apiVersion: gateway.networking.k8s.io/v1alpha2
148+
kind: TLSRoute
149+
metadata:
150+
name: my-tls-route
151+
spec:
152+
parentRefs:
153+
- name: gateway-tlsroute
154+
hostnames:
155+
- abc.example.com
156+
rules:
157+
- backendRefs:
158+
- name: tls-backend
159+
port: 443
160+
```
161+
162+
A typical [north/south](https://gateway-api.sigs.k8s.io/concepts/glossary/#northsouth-traffic)
163+
API request flow for a gateway implemented using a `TLSRoute` is:
164+
* A client makes a request to https://foo.example.com.
165+
* DNS resolves the name to a `Gateway` address.
166+
* The reverse proxy receives the request on a `Listener` and uses the
167+
[Server Name Indication](https://datatracker.ietf.org/doc/html/rfc6066#section-3)
168+
attribute to match an `TLSRoute`.
169+
* The reverse proxy passes through the request directly to one or more objects,
170+
i.e. `Service`, in the cluster based on `backendRefs` rules of the `TLSRoute`.
171+
172+
## Conflict management and precedences
173+
174+
The following conflict situations are covered by TLSRoute and TLS passthrough cases:
175+
176+
* When a Gateway contains a listener with `protocol=TLS`, the Gateway MUST NOT
177+
allow any other kind of listener on the same port and the Gateway SHOULD be
178+
marked as `Accepted=False` in case there are different kinds of listeners on
179+
the same port.
180+
* When a Gateway contains a listener with `protocol=TLS` and `tls.mode=Passthrough`,
181+
the `Gateway` MUST NOT allow another listener on the same port with a different
182+
`tls.mode` and the `Gateway` SHOULD be marked as `Accepted=False`.
183+
* Any violating Listener should have a Condition `Conflicted=True`.
184+
* If a hostname is specified by both the `Listener` and `TLSRoute`, there must
185+
be at least one intersecting hostname for the `TLSRoute` to be attached to the
186+
`Listener`.
187+
* A `Gateway listener` with `test.example.com` as the hostname matches a `TLSRoute` that
188+
specifies `test.example.com` but does not match a `TLSRoute` that specifies `*.example.com`
189+
* A `Gateway listener` with `*.example.com` as the hostname matches a `TLSRoute` that
190+
specifies at least one hostname that matches the `Gateway listener` hostname.
191+
For example, `test.example.com` and `*.example.com` would both match. On the
192+
other hand, `example.com` and `test.example.net` would not match.
193+
* If both the `Gateway listener` and `TLSRoute` specify hostnames, any `TLSRoute`
194+
hostnames that do not match the `Gateway listener` hostname MUST be ignored
195+
for that Listener. For example, if a `Gateway listener` specified `*.example.com`,
196+
and the `TLSRoute` specified `test.example.com` and `test.example.net`,
197+
the later must not be considered for a match.
198+
* In any of the cases above, the `TLSRoute` should have a `Condition` of `Accepted=True`.
199+
200+
## Conformance Details
201+
202+
### Feature Names
203+
204+
* TLSRoute
205+
206+
### Conformance tests
207+
208+
| Description | Outcome | Notes |
209+
| :---- | :---- | :---- |
210+
| A single TLSRoute in the gateway-conformance-infra namespace attaches to a Gateway in the same namespace | A request to a hostname served by the TLSRoute should be passthrough directly to the backend. Check if the termination happened, if no additional Gateway header was added | Already [implemented](https://github.com/kubernetes-sigs/gateway-api/blob/main/conformance/tests/tlsroute-simple-same-namespace.go) needs review |
211+
| A single TLSRoute in the gateway-conformance-infra namespace, with a backendRef in another namespace without valid ReferenceGrant, should have the ResolvedRefs condition set to False | TLSRoute conditions must have a Not Permitted status | Already [implemented](https://github.com/kubernetes-sigs/gateway-api/blob/main/conformance/tests/tlsroute-invalid-reference-grant.go) needs review |
212+
| A TLSRoute trying to attach to a gateway without a “tls/passthrough” listener should be rejected | Condition on the TLSRoute that it was rejected (discuss with community the right condition to be used here) | [https://github.com/kubernetes-sigs/gateway-api/issues/1579](https://github.com/kubernetes-sigs/gateway-api/issues/1579) |
213+
| A TLSRoute with a hostname that does not match the Gateway hostname should be rejected (eg.: route with hostname [www.example.com](http://www.example.com), gateway with hostname www1.example.com) | Condition on the TLSRoute that it was rejected | |
214+
| A TLSRoute with an IP on its hostname should be rejected | Condition on the TLSRoute that it was rejected | |
215+
| (impl specific) A Gateway containing a Listener of type TLS/Passthrough and a Listener of type HTTPS/Terminate should be accepted, and should multiplex the requests to TLSRoute and HTTPRoute correctly | Being able to do a request to a HTTP route being terminated on gateway (eg.: terminated.example.tld/xpto) and to a TLS Passthrough route on the same gateway, but different host (passthrough.example.tld) | |
216+
| A Gateway with \*.example.tld on a TLS listener should allow a TLSRoute with hostname some.example.tld to be attached to it (and the same, but with a non wildcard hostname) | TLSRoute should be able to attach to the Gateway using the matching hostname, a request should succeed | [https://github.com/kubernetes-sigs/gateway-api/issues/1579](https://github.com/kubernetes-sigs/gateway-api/issues/1579) |
217+
| A Gateway with something.example.tld on a TLS listener hostname should not allow a TLSRoute of \*.example.tld to be attached | TLSRoute should be rejected with invalid hostname (we should NOT support wildcard hostnames on a TLSRoute spec) | [https://github.com/kubernetes-sigs/gateway-api/issues/1579](https://github.com/kubernetes-sigs/gateway-api/issues/1579) |
218+
| Invalid TLSRoute with invalid BackendObjectReference performs no default forwarding | | [https://github.com/kubernetes-sigs/gateway-api/issues/1579](https://github.com/kubernetes-sigs/gateway-api/issues/1579) |
219+
| For a [Listener](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io%2fv1beta1.ListenerStatus) setting mode: "terminate", TLSRoute should not be present in [ListenerStatus.SupportedKinds](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io%2fv1beta1.ListenerStatus) | | [https://github.com/kubernetes-sigs/gateway-api/issues/1579](https://github.com/kubernetes-sigs/gateway-api/issues/1579) |
220+
| Attempting to attach a TLSRoute to a [Listener](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io%2fv1beta1.ListenerStatus) setting mode: "terminate" should yield [RouteConditionType](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io%2fv1beta1.ListenerStatus) Accepted with status: false and [RouteConditionReason](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io%2fv1beta1.ListenerStatus) NotAllowedByListeners on the TLSRoute | | [https://github.com/kubernetes-sigs/gateway-api/issues/1579](https://github.com/kubernetes-sigs/gateway-api/issues/1579) |
221+
| For a [Listener](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io%2fv1beta1.ListenerStatus) setting mode: "terminate", attempting to set TLSRoute in [AllowedRoutes.kinds](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io%2fv1beta1.AllowedRoutes) would yield a [ListenerConditionType](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io%2fv1beta1.ListenerStatus) Accepted with status: false and [ListenerConditionReason](https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io%2fv1beta1.ListenerStatus) InvalidRouteKinds | | [https://github.com/kubernetes-sigs/gateway-api/issues/1579](https://github.com/kubernetes-sigs/gateway-api/issues/1579) |
222+
223+
Pending conformance verifications:
224+
225+
* [https://github.com/kubernetes-sigs/gateway-api/issues/3466](https://github.com/kubernetes-sigs/gateway-api/issues/3466)
226+
* [https://github.com/kubernetes-sigs/gateway-api/issues/2153](https://github.com/kubernetes-sigs/gateway-api/issues/2153)
227+
228+
### References
229+
* [Existing API](https://github.com/kubernetes-sigs/gateway-api/blob/main/apis/v1alpha3/tlsroute_types.go)
230+
* [TLS Terminology - Needs update](https://github.com/kubernetes-sigs/gateway-api/blob/d28cd59d37887be07b879f098cff7b14a87c0080/geps/gep-2907/index.md?plain=1#L29)
231+
* [TLSRoute promotion issue](https://github.com/kubernetes-sigs/gateway-api/issues/3165)
232+
* [TLS Multiplexing issue discussion](https://github.com/kubernetes-sigs/gateway-api/issues/623)
233+
* [TLSRoute intersecting hostnames issue](https://github.com/kubernetes-sigs/gateway-api/issues/3541)
234+
* [TLSRoute termination feature request](https://github.com/kubernetes-sigs/gateway-api/issues/2111)
235+
* [GatewayAPI TLS Use Cases](https://docs.google.com/document/d/17sctu2uMJtHmJTGtBi_awGB0YzoCLodtR6rUNmKMCs8)

geps/gep-2643/metadata.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
apiVersion: internal.gateway.networking.k8s.io/v1alpha1
2+
kind: GEPDetails
3+
number: 2643
4+
name: TLS based passthrough Route / TLSRoute
5+
status: Provisional
6+
# Note: The feature already exists, the GEP is a retroactive collection of information
7+
authors:
8+
- candita
9+
- Miciah
10+
- rikatz
11+
relationships:
12+
seeAlso:
13+
- number: 2907
14+
name: TLS Configuration Placement and Terminology
15+
references:
16+
- https://datatracker.ietf.org/doc/html/rfc6066#section-3

0 commit comments

Comments
 (0)