Skip to content

Commit 52d3176

Browse files
committed
Add provisional GEP-4748: EgressGateway Resource
Proposes a dedicated `EgressGateway` resource derived from `Gateway` for L7 reverse-proxy egress. Companion to GEP-4747. A coding assistant (Opus 4.6) was used to review grammar, citations, and alignment with upstream GEP formatting and terminology. It was also used to fact-check prior art claims against implementation documentation.
1 parent 8f39040 commit 52d3176

File tree

2 files changed

+411
-0
lines changed

2 files changed

+411
-0
lines changed

geps/gep-4748/index.md

Lines changed: 386 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,386 @@
1+
# GEP-4748: EgressGateway Resource
2+
3+
* Issue: [#4748](https://github.com/kubernetes-sigs/gateway-api/issues/BBBB)
4+
* Status: Provisional
5+
6+
(See [status definitions](../overview.md#gep-states).)
7+
8+
[Chihiro]: https://gateway-api.sigs.k8s.io/concepts/roles-and-personas/#key-roles-and-personas
9+
[Ian]: https://gateway-api.sigs.k8s.io/concepts/roles-and-personas/#key-roles-and-personas
10+
[Ana]: https://gateway-api.sigs.k8s.io/concepts/roles-and-personas/#key-roles-and-personas
11+
12+
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD",
13+
"SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this
14+
document are to be interpreted as described in BCP 14 ([RFC8174]) when, and
15+
only when, they appear in all capitals, as shown here.
16+
17+
[RFC8174]: https://www.rfc-editor.org/rfc/rfc8174
18+
19+
> **Note**: This GEP is Provisional. It proposes a dedicated `EgressGateway`
20+
> resource as an alternative to reusing the existing `Gateway` resource for
21+
> egress (see companion [GEP-4747]). Community feedback on both approaches is
22+
> explicitly requested.
23+
24+
[GEP-4747]: ../gep-4747/index.md
25+
26+
## TLDR
27+
28+
Introduce a dedicated `EgressGateway` resource for L7 reverse-proxy egress
29+
traffic management. While Gateway semantics are largely identical for ingress
30+
and egress at the field level, this GEP argues that a separate resource
31+
provides clearer user experience, stronger guardrails, and design space for
32+
egress-specific concerns that may emerge as the pattern matures.
33+
34+
## User Stories
35+
36+
### Platform Operator
37+
38+
> **[Ian] needs to deploy an egress gateway that is structurally distinct from
39+
> ingress gateways, preventing misconfiguration and making egress
40+
> infrastructure immediately identifiable in cluster inventory.**
41+
42+
### Application Developer
43+
44+
> **[Ana] needs to understand at a glance whether a gateway handles inbound or
45+
> outbound traffic, without inspecting GatewayClass parameters or controller
46+
> documentation.**
47+
48+
### Cluster Administrator
49+
50+
> **[Chihiro] needs to apply RBAC policies that distinguish between who can
51+
> create ingress gateways vs egress gateways, using standard Kubernetes
52+
> resource-level permissions rather than GatewayClass-specific admission
53+
> control.**
54+
55+
## Goals
56+
57+
* Introduce `EgressGateway` as a dedicated resource for egress traffic
58+
* Reuse Gateway API types maximally -- share listener, address, and route
59+
attachment semantics with Gateway
60+
* Provide structural guardrails against ingress/egress misconfiguration
61+
* Reserve design space for egress-specific fields as the pattern matures
62+
* Enable simple RBAC separation between ingress and egress gateway creation
63+
64+
## Non-Goals
65+
66+
* Define the Backend resource (see [PR #4488](https://github.com/kubernetes-sigs/gateway-api/pull/4488))
67+
* Define significantly different listener or routing semantics from Gateway
68+
* Address forward-proxy egress, L3/L4 egress, or mesh-attached egress
69+
* Deprecate or replace Gateway for any existing use case including in
70+
existing egress implementations.
71+
* Prescribe a single egress architecture (this resource supports both
72+
Endpoint and Parent routing modes)
73+
74+
## Introduction
75+
76+
### The Case for a Separate Resource
77+
78+
[GEP-4747] demonstrates that Gateway field semantics are largely equivalent for ingress
79+
and egress, though some fields carry different contextual meanings. This GEP does not dispute that analysis. Instead, it argues that
80+
equivalent semantics are necessary but not sufficient -- user experience, RBAC,
81+
and future extensibility warrant a dedicated resource.
82+
83+
#### 1. User Clarity
84+
85+
Gateway API serves multiple personas ([Chihiro], [Ian], [Ana]). When [Ian]
86+
runs `kubectl get gateways`, a mix of ingress and egress gateways appears with
87+
no structural distinction. Labels and GatewayClass names are conventions, not
88+
guarantees. A dedicated `EgressGateway` resource makes the distinction
89+
first-class:
90+
91+
```bash
92+
$ kubectl get gateways
93+
NAME CLASS ADDRESS READY
94+
ingress-gateway nginx 203.0.113.10 True
95+
96+
$ kubectl get egressgateways
97+
NAME CLASS ADDRESS READY
98+
egress-gateway egress 10.96.100.50 True
99+
```
100+
101+
#### 2. RBAC Separation
102+
103+
With a single Gateway resource, controlling who can create egress vs ingress
104+
gateways requires admission webhooks or policy engines that inspect
105+
GatewayClass references. A dedicated resource enables standard Kubernetes RBAC:
106+
107+
```yaml
108+
# Allow team to create egress gateways but not ingress gateways
109+
apiVersion: rbac.authorization.k8s.io/v1
110+
kind: Role
111+
rules:
112+
- apiGroups: ["gateway.networking.k8s.io"]
113+
resources: ["egressgateways"]
114+
verbs: ["create", "get", "list", "watch"]
115+
# No rule for "gateways" -- team cannot create ingress gateways
116+
```
117+
118+
#### 3. Structural Guardrails
119+
120+
Certain Gateway configurations are valid for ingress but problematic for
121+
egress. A dedicated resource can structurally prevent these:
122+
123+
- **Address types**: Egress gateways need ClusterIP, not LoadBalancer.
124+
An EgressGateway could default or restrict address types.
125+
- **TLS mode**: Egress listeners may have different default TLS
126+
expectations than ingress listeners.
127+
128+
#### 4. Design Space
129+
130+
Even if no egress-specific fields are needed today, reserving a resource
131+
allows future additions without modifying the Gateway resource:
132+
133+
- Default DNS resolver configuration for external FQDNs
134+
- Egress-specific status conditions (e.g., upstream reachability)
135+
- Source identity requirements (which ServiceAccounts can use this gateway)
136+
- Proxy mode (explicit vs transparent -- if transparent egress is added later)
137+
138+
## API
139+
140+
### EgressGateway Resource
141+
142+
`EgressGateway` is derived from `Gateway`, re-using all of the same
143+
underlying types. The exact field set, validation, and defaults will be
144+
finalized if the community selects this approach. The key design
145+
principle is maximal type reuse: `EgressGateway` SHOULD re-use
146+
types that already exist in Gateway API where applicable.
147+
148+
### Key Differences From Gateway
149+
150+
| Aspect | Gateway | EgressGateway |
151+
|--------|---------|---------------|
152+
| Default address type | Implementation-specific (often LoadBalancer) | SHOULD default to ClusterIP |
153+
| Typical listener count | Multiple (one per vhost) | One (wildcard) |
154+
| Route attachment | HTTPRoute parentRef targets Gateway | HTTPRoute parentRef targets EgressGateway |
155+
| RBAC resource | `gateways` | `egressgateways` |
156+
157+
### HTTPRoute Attachment
158+
159+
HTTPRoute already supports heterogeneous parentRef kinds. Attaching to an
160+
EgressGateway requires only setting the `kind` field:
161+
162+
```yaml
163+
apiVersion: gateway.networking.k8s.io/v1
164+
kind: HTTPRoute
165+
metadata:
166+
name: openai-route
167+
namespace: app-team
168+
spec:
169+
parentRefs:
170+
- name: egress-gateway
171+
namespace: gateway-system
172+
kind: EgressGateway
173+
group: gateway.networking.k8s.io
174+
hostnames:
175+
- "api.openai.com"
176+
rules:
177+
- backendRefs:
178+
- group: gateway.networking.k8s.io
179+
kind: Backend
180+
name: openai-backend
181+
```
182+
183+
### Full Example
184+
185+
```yaml
186+
apiVersion: gateway.networking.k8s.io/v1
187+
kind: GatewayClass
188+
metadata:
189+
name: egress
190+
spec:
191+
controllerName: example.com/egress-controller
192+
---
193+
apiVersion: gateway.networking.k8s.io/v1alpha1
194+
kind: EgressGateway
195+
metadata:
196+
name: egress-gw
197+
namespace: gateway-system
198+
spec:
199+
gatewayClassName: egress
200+
listeners:
201+
- name: proxy
202+
port: 8443
203+
protocol: HTTPS
204+
tls:
205+
mode: Terminate
206+
certificateRefs:
207+
- name: egress-gw-cert
208+
allowedRoutes:
209+
namespaces:
210+
from: All
211+
---
212+
apiVersion: gateway.networking.k8s.io/v1
213+
kind: HTTPRoute
214+
metadata:
215+
name: openai
216+
namespace: ml-team
217+
spec:
218+
parentRefs:
219+
- name: egress-gw
220+
namespace: gateway-system
221+
kind: EgressGateway
222+
group: gateway.networking.k8s.io
223+
hostnames:
224+
- "api.openai.com"
225+
rules:
226+
- backendRefs:
227+
- group: gateway.networking.k8s.io
228+
kind: Backend
229+
name: openai-api
230+
---
231+
apiVersion: gateway.networking.k8s.io/v1alpha1
232+
kind: Backend
233+
metadata:
234+
name: openai-api
235+
namespace: ml-team
236+
spec:
237+
destination:
238+
type: Hostname
239+
hostname:
240+
address: api.openai.com
241+
ports:
242+
- number: 443
243+
protocol: HTTP2
244+
tls:
245+
mode: Simple
246+
validation:
247+
hostname: api.openai.com
248+
```
249+
250+
## Trade-offs
251+
252+
This section honestly presents the trade-offs of this approach. See [GEP-4747]
253+
for the alternative.
254+
255+
### Costs of a Separate Resource
256+
257+
1. **API surface area**: A new CRD, new RBAC resources, new conformance tests.
258+
More for implementations to support.
259+
260+
2. **Type duplication**: Even though `EgressGatewaySpec` reuses underlying
261+
types, it is a new top-level type that must be maintained.
262+
263+
3. **Ecosystem fragmentation**: Implementations must decide whether to support
264+
Gateway-for-egress, EgressGateway, or both.
265+
266+
4. **No existing API divergence**: Every Gateway field has identical
267+
or equivalent semantics for ingress and egress. The case for a
268+
separate resource is UX, API design space, and RBAC, not semantic
269+
necessity.
270+
271+
### Benefits of a Separate Resource
272+
273+
1. **Clear UX**: `kubectl get egressgateways` immediately distinguishes egress.
274+
275+
2. **Native RBAC**: Standard Kubernetes RBAC without admission webhooks.
276+
277+
3. **Structural defaults**: Sensible egress defaults (ClusterIP, wildcard
278+
listeners) without special-casing Gateway behavior based on GatewayClass.
279+
280+
4. **Design space**: Future egress-specific fields can be added without
281+
modifying Gateway.
282+
283+
## Conformance
284+
285+
EgressGateway uses the same `Egress` conformance profile defined in
286+
[GEP-4747]. Whether the community chooses Gateway reuse or EgressGateway,
287+
the conformance tests verify the same egress behavior. The only difference
288+
is which resource types the tests target.
289+
290+
If EgressGateway is chosen, conformance tests MUST verify:
291+
292+
- EgressGateway creation and status reporting
293+
- HTTPRoute attachment via `parentRef.kind: EgressGateway`
294+
- Routing to Backend resources ([PR #4488](https://github.com/kubernetes-sigs/gateway-api/pull/4488))
295+
- ClusterIP addressing by default
296+
- All core and extended egress conformance features from [GEP-4747]
297+
298+
## Security Considerations
299+
300+
Same as [GEP-4747], plus:
301+
302+
- **RBAC boundary**: EgressGateway provides a natural RBAC boundary.
303+
Organizations can grant `egressgateways` permissions to platform teams
304+
without granting `gateways` permissions, reducing blast radius.
305+
306+
## Open Questions
307+
308+
### 1. Should EgressGateway Share GatewayClass?
309+
310+
Should `EgressGateway.spec.gatewayClassName` reference the same `GatewayClass`
311+
resource as `Gateway`? Or should there be a separate `EgressGatewayClass`?
312+
313+
**Recommendation**: Reuse `GatewayClass`. The controller distinction is
314+
already expressed via `controllerName`. Adding `EgressGatewayClass` would
315+
be excessive fragmentation.
316+
317+
### 2. How Much Type Reuse?
318+
319+
Should `EgressGatewaySpec` literally embed `GatewaySpec` (and add/remove
320+
fields via validation) or define its own parallel struct with shared
321+
sub-types?
322+
323+
**Recommendation**: Own struct with shared sub-types (Listener, GatewaySpecAddress,
324+
etc.). This allows egress-specific defaults and documentation without import
325+
cycles.
326+
327+
## Alternatives Considered
328+
329+
### Reuse Existing Gateway (GEP-4747)
330+
331+
[GEP-4747] proposes using existing Gateway with GatewayClass to distinguish
332+
egress. It argues no new resource is needed because field semantics are
333+
identical.
334+
335+
**This GEP's response**: Identical semantics are necessary but not sufficient.
336+
UX, RBAC, and design space justify a dedicated resource.
337+
338+
### EgressRoute (Prior GEP #1971)
339+
340+
A [previous attempt](https://github.com/kubernetes-sigs/gateway-api/pull/1971)
341+
proposed an `EgressRoute` resource. This GEP takes a different approach: the
342+
new resource is the gateway, not the route. Routes (HTTPRoute, GRPCRoute)
343+
are reused unchanged.
344+
345+
## Dependencies
346+
347+
| Dependency | Status | Impact |
348+
|---|---|---|
349+
| [PR #4488: Backend Resource](https://github.com/kubernetes-sigs/gateway-api/pull/4488) | PR open | Required -- egress routes need Backend destinations |
350+
| [#1651: Gateway Routability](https://github.com/kubernetes-sigs/gateway-api/issues/1651) | Issue open | Nice-to-have -- EgressGateway could default to ClusterIP addressing |
351+
| [GEP-4747: Egress Gateway Support](../gep-4747/index.md) | Companion | Community must choose between this GEP and GEP-4747 |
352+
353+
## References
354+
355+
* [WG AI Gateway egress proposal](https://github.com/kubernetes-sigs/wg-ai-gateway/blob/main/proposals/10-egress-gateways.md)
356+
* [EgressGateway prototype (wg-ai-gateway PR #45)](https://github.com/kubernetes-sigs/wg-ai-gateway/pull/45)
357+
* [PR #4488: Backend Resource](https://github.com/kubernetes-sigs/gateway-api/pull/4488)
358+
* [GEP-4747: Egress Gateway Support](../gep-4747/index.md)
359+
360+
## Graduation Criteria
361+
362+
### Provisional -> Implementable
363+
364+
- [ ] Community decision on EgressGateway (this GEP) vs Gateway reuse ([GEP-4747])
365+
- [ ] [PR #4488](https://github.com/kubernetes-sigs/gateway-api/pull/4488) (Backend) reaches at least Provisional status
366+
- [ ] Open questions resolved (GatewayClass reuse, type structure)
367+
- [ ] EgressGateway CRD schema finalized
368+
369+
### Alpha (Experimental)
370+
371+
- [ ] EgressGateway CRD in gateway-api repository
372+
- [ ] Conformance tests for EgressGateway
373+
- [ ] At least one implementation
374+
375+
### Beta
376+
377+
- [ ] At least two implementations
378+
- [ ] Production usage reports
379+
- [ ] No major API changes for 3+ months
380+
381+
### GA (Standard)
382+
383+
- [ ] Three implementations
384+
- [ ] Stable for 6+ months
385+
- [ ] Security review complete
386+

0 commit comments

Comments
 (0)