|
| 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