sigs.k8s.io/gateway-api@v1.0.0/geps/gep-724.md (about) 1 # GEP-724: Refresh Route-Gateway Binding 2 3 * Issue: [#724](https://github.com/kubernetes-sigs/gateway-api/issues/724) 4 * Status: Standard 5 6 ## TLDR 7 8 This GEP proposes changes to Route-Gateway binding that will result in Routes 9 attaching to Gateways with direct references. When supporting Routes in multiple 10 namespaces, Gateways will need to specify the namespaces they trust Routes in. 11 These changes will slightly simplify the Route-Gateway relationship and make way 12 for the future addition of Route inclusion (Routes including other Routes). 13 14 ## Goals 15 16 Refactor cross-namespace Route-Gateway binding to: 17 18 * Be more consistent with [cross-namespace references from 19 Routes](/geps/gep-709) 20 * Provide a clear path to enable Route inclusion (Routes including Routes). 21 * Simplify user experience based on initial feedback. 22 * Enable other kinds of Route parents in addition to Gateway, this could include: 23 * Routes (as part of one potential approach to Route inclusion) 24 * Custom Gateway resources 25 * Mesh resources 26 27 ## Out of scope 28 29 * Defining how Route inclusion will work. 30 31 ## Existing Approach 32 33 The existing API already supports cross-namespace references. Gateways configure 34 the following: 35 36 * A Route label selector 37 * Namespaces: Same, Selector, or All 38 39 Routes then have three options as far as which Gateways can bind to them: 40 41 * Same namespace (default) 42 * From a list of Gateways 43 * All 44 45 Although this enables a great deal of flexibility, it can lead to confusion. For 46 example, 2 separate label selectors from Gateway can be challenging for users to 47 compute. It requires users to do to label selector lookups and then compute the 48 intersection of that result. Additionally, the default behavior of selecting all 49 Routes in the same namespace makes it easy to accidentally expose applications 50 (see [#515](https://github.com/kubernetes-sigs/gateway-api/issues/515)). 51 52 ## Proposed Changes 53 54 Although the API changes proposed here are relatively small, this represents a 55 larger conceptual change. Instead of Gateways selecting Routes, Routes would 56 directly reference the Gateways they wanted to attach to. This pattern was 57 already possible with the existing API, but not clearly documented. 58 59 One of the key concepts in the [cross-namespace references from Routes 60 GEP](/geps/gep-709) was that of a handshake for 61 references that cross namespace boundaries. A key part of that handshake was 62 that one direction included a direct reference, while the other direction 63 provided a way to denote trust for a set of Namespaces and kind of resources. 64 65 It seems to make sense to carry that same underlying principle through to the 66 Route - Gateway relationship. Given that each Gateway is likely to support many 67 Routes, it would not be practical to support direct references from Gateways to 68 Routes. Instead, it is simpler if Routes directly reference the Gateways they 69 want to attach to. Gateways can then specify the namespaces they trust Routes 70 to attach from. 71 72  73 74 In the following example, the lb Gateway indicates that it trusts Routes from 75 the foo Namespace, and the HTTPRoute in that namespace attaches directly to the 76 Gateway. 77 78 ```yaml 79 kind: Gateway 80 metadata: 81 name: lb 82 namespace: infra 83 spec: 84 listeners: 85 - name: foo 86 hostname: foo.com 87 port: 80 88 routes: 89 namespaces: 90 from: Selector 91 selector: 92 kubernetes.io/metadata.name: foo 93 --- 94 kind: HTTPRoute 95 metadata: 96 name: foo 97 namespace: foo 98 spec: 99 attachTo: 100 - kind: Gateway 101 namespace: infra 102 name: lb 103 sectionName: foo 104 rules: 105 - name: abc 106 matches: 107 - path: /bar 108 ``` 109 110 ## Rationale 111 112 #### 1. Remove Complexity While it is Still Possible 113 A goal of v1alpha2 is to make any breaking changes we think we'll need to for 114 the lifetime of the API. After this release, we plan to provide a fully 115 convertible, and hopefully also fully backwards compatible, API. If we really 116 do need more advanced functionality in the future, it will be fairly 117 straightforward to add in a future release. On the other hand, it will be near 118 impossible to remove this complexity post-v1alpha2 if we were to leave it as is. 119 120 #### 2. Route Selection Added Confusion and Did Not Enhance Security 121 Although it was easy to look at the selector from Gateway -> Route as providing 122 Gateway admins some form of control over the Routes attached to their Gateway, 123 it was nothing but security theater. Route owners still had ultimate control by 124 deciding how their Routes were labeled. This also made it difficult to document 125 how Gateway and Route owners should interact. One possible explanation was that 126 Gateway owners should provide Route owners with a set of labels that they should 127 add to their Route to bind to a given Gateway. At that point, we were still 128 ultimately relying on Route owners to attach a Route to a Gateway, just making 129 it a rather clunky process. 130 131 It should be noted that this proposal does still retain the ability for Gateways 132 to restrict the namespaces they trust Routes to attach from. This was the only 133 real control Gateway admins had before this proposed change. 134 135 #### 3. The Existing Defaults Were Too Permissive 136 One of the common complaints about the existing API was that the defaults made 137 it far too easy to accidentally expose a Route. By default, a Gateway would be 138 bound to all Routes in the same namespace. Although that made the getting 139 started guide simple, it would inevitably lead to some unfortunate mistakes in 140 the future. As we've already learned with Kubernetes, it's very difficult to 141 recover from insecure defaults. Instead, it's much safer to start with more 142 explicit configuration that demonstrates clear intent to connect resources. 143 144 #### 4. We Need to Support non-Gateway Route Parents 145 With the expansion of this API, it's clear that a Route may have non-Gateway 146 parents. This may be other Routes, mesh implementations, or custom Gateways. 147 Although none of these concepts are well specified at this point, making this 148 change now will give us more flexibility in the future. 149 150 #### 5. Users Want Control over the Gateways Their Routes Are Attached To 151 Initial feedback we've received has shown that users want to have very clear 152 control over the Gateways their Routes are attached to. Even in the case of 153 Gateway replacement, many Route owners would prefer to be involved in the 154 process. 155 156 As we get more feedback and usage of the API, we may identify more users that 157 are interested in the more advanced capabilities that some form of selection may 158 enable, but at this point it's clear that a large portion of users value an 159 explicit way to attach a Route to a Gateway. 160 161 #### 6. We Need to Maintain a Handshake Between Gateways and Routes 162 Of course we do still need a handshake that will enable cross-namespace 163 references between Routes and Gateways. This proposal leaves in the core 164 capabilities of the v1alpha1 API for this. Gateways can specify the namespaces 165 they trust Routes to bind from, and Routes directly reference the Gateways they 166 want to attach to. This is largely similar to the ReferenceGrant model proposed 167 for Route->Service references, but is embedded within the Route and Gateway 168 resources. The alternatives below explore what this could look like with 169 ReferenceGrant. 170 171 ## API Changes 172 173 The proposed changes here can be summarized as: 174 175 * Remove Route selector from the RouteBindingSelector in Gateway listeners. 176 * Replace Route kind and group with optional list of accepted kinds and groups 177 in RouteBindingSelector. 178 * Rename RouteBindingSelector to ListenerRoutes. 179 * Replace the 3 options from Route -> Gateway (All, FromList, SameNamespace) 180 with a reference list that supports arbitrary kinds. 181 * Add a name to Gateway listeners. 182 * Restructure listener status to include name, routeRefs, and supportedKinds 183 fields. 184 185 ### Gateway Spec 186 187 In Gateway spec, the only change involves removing the Route selector field. 188 Everything else remains the same. 189 190 #### Removed 191 The following fields would be removed from RouteBindingSelector: 192 193 ```go 194 // Selector specifies a set of route labels used for selecting 195 // routes to associate with the Gateway. If this Selector is defined, 196 // only routes matching the Selector are associated with the Gateway. 197 // An empty Selector matches all routes. 198 // 199 // Support: Core 200 // 201 // +optional 202 Selector *metav1.LabelSelector `json:"selector,omitempty"` 203 // Group is the group of the route resource to select. Omitting the value or specifying 204 // the empty string indicates the gateway.networking.k8s.io API group. 205 // For example, use the following to select an HTTPRoute: 206 // 207 // routes: 208 // kind: HTTPRoute 209 // 210 // Otherwise, if an alternative API group is desired, specify the desired 211 // group: 212 // 213 // routes: 214 // group: acme.io 215 // kind: FooRoute 216 // 217 // Support: Core 218 // 219 // +optional 220 // +kubebuilder:default=gateway.networking.k8s.io 221 // +kubebuilder:validation:MinLength=1 222 // +kubebuilder:validation:MaxLength=253 223 Group *string `json:"group,omitempty"` 224 // Kind is the kind of the route resource to select. 225 // 226 // Kind MUST correspond to kinds of routes that are compatible with the 227 // application protocol specified in the Listener's Protocol field. 228 // 229 // If an implementation does not support or recognize this 230 // resource type, it SHOULD set the "ResolvedRefs" condition to false for 231 // this listener with the "InvalidRoutesRef" reason. 232 // 233 // Support: Core 234 Kind string `json:"kind"` 235 ``` 236 237 #### Added 238 Note: The ListMapKey annotation for listeners would also have to change to name 239 for this. 240 241 ```go 242 type Listener struct { 243 // Name is the name of the Listener. If more than one Listener is present 244 // each Listener MUST specify a name. The names of Listeners MUST be unique 245 // within a Gateway. 246 // 247 // Support: Core 248 // 249 // +kubebuilder:validation:MinLength=1 250 // +kubebuilder:validation:MaxLength=253 251 // +optional 252 Name *string `json:"name,omitempty"` 253 // ... 254 } 255 ``` 256 257 The RouteBindingSelector struct would be renamed to ListenerRoutes, and a Kinds 258 field would be added. Note that the Selector, Group, and Kind field would be 259 removed from this struct as described above. 260 261 ```go 262 type ListenerRoutes struct { 263 // ... 264 // Kinds specifies the groups and kinds of Routes that are allowed to bind to 265 // this Gateway listener. When unspecified or empty, the only limitation on 266 // the kinds of Routes supported is the Listener protocol. Kind MUST 267 // correspond to kinds of Routes that are compatible with the application 268 // protocol specified in the Listener's Protocol field. If an implementation 269 // does not support or recognize this resource type, it SHOULD set the 270 // "ResolvedRefs" condition to false for this listener with the 271 // "InvalidRoutesRef" reason. 272 // 273 // Support: Core 274 // 275 // +optional 276 // +kubebuilder:validation:MaxItems=10 277 Kinds []RouteGroupKind `json:"kinds,omitempty"` 278 } 279 280 type RouteGroupKind struct { 281 // Group is the group of the Route. 282 // 283 // Support: Core 284 // 285 // +optional 286 // +kubebuilder:default=gateway.networking.k8s.io 287 // +kubebuilder:validation:MaxLength=253 288 Group *string `json:"group,omitempty"` 289 // Kind is the kind of the Route. 290 // 291 // Support: Core 292 // 293 // +kubebuilder:validation:MinLength=1 294 // +kubebuilder:validation:MaxLength=253 295 Kind string `json:"kind"` 296 } 297 ``` 298 299 ### Gateway Status 300 The most significant addition to the Gateway resource is in status. It may be 301 helpful to share a sample of what the YAML would look like: 302 303 ```yaml 304 status: 305 listeners: 306 - name: foo 307 supportedKinds: 308 - group: gateway.networking.k8s.io 309 kind: HTTPRoute 310 attachedRoutes: 1 311 conditions: 312 - ... 313 ``` 314 315 The key changes here all involve Listener status: 316 317 * Replace the `port`, `protocol`, and `hostname` field with `name` to take 318 advantage of the new Listener name concept. 319 * Add a new `supportedKinds` field. This will be most useful when the 320 corresponding field in the spec is left blank or when a user specifies kinds 321 that a controller does not support. 322 323 Note: The ListMapKey annotation for listener status would also have to change to 324 name for this. 325 326 ```go 327 // ListenerStatus is the status associated with a Listener. 328 type ListenerStatus struct { 329 // Name is the name of the Listener. 330 // 331 // +kubebuilder:validation:MinLength=1 332 // +kubebuilder:validation:MaxLength=253 333 // +optional 334 Name *string `json:"name,omitempty"` 335 336 // SupportedKinds is the list indicating the Kinds supported by this 337 // listener. When this is not specified on the Listener, this MUST represent 338 // the kinds an implementation supports for the specified protocol. When 339 // there are kinds specified on the Listener, this MUST represent the 340 // intersection of those kinds and the kinds supported by the implementation 341 // for the specified protocol. 342 // 343 // +kubebuilder:validation:MaxItems=10 344 // +optional 345 SupportedKinds []RouteGroupKind `json:"supportedKinds,omitempty"` 346 347 // AttachedRoutes represents the total number of Routes that have been 348 // successfully attached to this Listener. 349 AttachedRoutes int32 `json:"attachedRoutes"` 350 351 // Conditions... 352 } 353 ``` 354 355 ### Routes 356 357 On Routes, we remove the `RouteGateways` struct and replace it with a list of 358 parent references to attach to. 359 360 #### Removed 361 From Route Specs: 362 ```go 363 // Gateways defines which Gateways can use this Route. 364 // 365 // +optional 366 // +kubebuilder:default={allow: "SameNamespace"} 367 Gateways *RouteGateways `json:"gateways,omitempty"` 368 ``` 369 370 And the structs that references: 371 ```go 372 // RouteGateways defines which Gateways will be able to use a route. If this 373 // field results in preventing the selection of a Route by a Gateway, an 374 // "Admitted" condition with a status of false must be set for the Gateway on 375 // that Route. 376 type RouteGateways struct { 377 // Allow indicates which Gateways will be allowed to use this route. 378 // Possible values are: 379 // * All: Gateways in any namespace can use this route. 380 // * FromList: Only Gateways specified in GatewayRefs may use this route. 381 // * SameNamespace: Only Gateways in the same namespace may use this route. 382 // 383 // +optional 384 // +kubebuilder:validation:Enum=All;FromList;SameNamespace 385 // +kubebuilder:default=SameNamespace 386 Allow *GatewayAllowType `json:"allow,omitempty"` 387 388 // GatewayRefs must be specified when Allow is set to "FromList". In that 389 // case, only Gateways referenced in this list will be allowed to use this 390 // route. This field is ignored for other values of "Allow". 391 // 392 // +optional 393 GatewayRefs []GatewayReference `json:"gatewayRefs,omitempty"` 394 } 395 ``` 396 397 #### Added 398 To Route Specs: 399 ```go 400 // ParentRefs references the resources that can attach to this Route. The only 401 // kind of parent resource with "Core" support is Gateway. This API may be 402 // extended in the future to support additional kinds of parent resources such 403 // as one of the route kinds. It is invalid to reference an identical parent 404 // more than once. It is valid to reference multiple distinct sections within 405 // the same parent resource, such as 2 Listeners within a Gateway. 406 // 407 // It is possible to separately reference multiple distinct objects that may 408 // be collapsed by an implementation. For example, some implementations may 409 // choose to merge compatible Gateway Listeners together. If that is the case, 410 // the list of routes attached to those resources should also be merged. 411 // 412 // +optional 413 // +kubebuilder:validation:MaxItems=16 414 ParentRefs []ParentRef `json:"parentRefs,omitempty"` 415 ``` 416 417 And the struct that references: 418 ```go 419 // ParentRef identifies an API object that should be considered a parent of this 420 // resource. The only kind of parent resource with "Core" support is Gateway. 421 // This API may be extended in the future to support additional kinds of parent 422 // resources, such as HTTPRoute. 423 type ParentRef struct { 424 // Group is the group of the referent. 425 // 426 // Support: Core 427 // 428 // +kubebuilder:validation:MinLength=1 429 // +kubebuilder:validation:MaxLength=253 430 // +kubebuilder:default=gateway.networking.k8s.io 431 Group string `json:"group"` 432 433 // Kind is kind of the referent. 434 // 435 // Support: Core (Gateway) 436 // Support: Extended (Other Resources) 437 // 438 // +kubebuilder:validation:MinLength=1 439 // +kubebuilder:validation:MaxLength=253 440 // +kubebuilder:default=Gateway 441 // +optional 442 Kind *string `json:"kind,omitempty"` 443 444 // Namespace is the namespace of the referent. When unspecified (empty 445 // string), this will either be: 446 // 447 // * local namespace of the target is a namespace scoped resource 448 // * no namespace (not applicable) if the target is cluster-scoped. 449 // 450 // Support: Extended 451 // 452 // +kubebuilder:validation:MinLength=1 453 // +kubebuilder:validation:MaxLength=253 454 // +optional 455 Namespace *string `json:"namespace,omitempty"` 456 457 // Scope represents if this refers to a cluster or namespace scoped resource. 458 // This may be set to "Cluster" or "Namespace". 459 // 460 // Support: Core (Namespace) 461 // Support: Extended (Cluster) 462 // 463 // +kubebuilder:validation:Enum=Cluster;Namespace 464 // +kubebuilder:default=Namespace 465 // +optional 466 Scope *string `json:"scope,omitempty"` 467 468 // Name is the name of the referent. 469 // 470 // Support: Core 471 // 472 // +kubebuilder:validation:MinLength=1 473 // +kubebuilder:validation:MaxLength=253 474 Name string `json:"name"` 475 476 // SectionName is the name of a section within the target resource. In the 477 // following resources, SectionName is interpreted as the following: 478 // 479 // * Gateway: Listener Name 480 // 481 // Implementations MAY choose to support attaching Routes to other resources. 482 // If that is the case, they MUST clearly document how SectionName is 483 // interpreted. 484 // 485 // When unspecified (empty string), this will reference the entire resource. 486 // For the purpose of status, an attachment is considered successful if at 487 // least one section in the parent resource accepts it. For example, Gateway 488 // listeners can restrict which Routes can bind to them by Route kind, 489 // namespace, or hostname. If 1 of 2 Gateway listeners accept attachment from 490 // the referencing Route, the Route MUST be considered successfully 491 // attached. If no Gateway listeners accept attachment from this Route, the 492 // Route MUST be considered detached from the Gateway. 493 // 494 // Support: Core 495 // 496 // +kubebuilder:validation:MinLength=1 497 // +kubebuilder:validation:MaxLength=253 498 // +optional 499 SectionName *string `json:"sectionName,omitempty"` 500 } 501 ``` 502 503 #### Changed 504 505 To accommodate Routes with arbitrary types of parents, `RouteGatewayStatus` would 506 be renamed to `RouteParentStatus`. Similarly, the `GatewayRef` inside that 507 struct would be replaced with the `ParentRef` struct included above. 508 509 ### Advantages 510 511 * Simplifies the API by providing a single way to attach Routes to Gateway. 512 * Assigns clear responsibilities to Gateway and Route owners. 513 * Consistent with pattern of direct references from Routes to all associated 514 resources. 515 * Enables attaching Routes to arbitrary parents, such as custom Gateways, other 516 Routes (to be defined), or Meshes. 517 * Prevents accidental exposure of Routes. 518 * Easy to understand which Gateways/parents a Route is attached to. 519 * Further simplifies path to Route inclusion. 520 * Follows pattern of direct reference in one direction with a broader trust 521 reference in the other direction. 522 * Aligns with initial user feedback. 523 524 ### Disadvantages 525 526 * Attaching a Route to a named listener with SectionName may be a bit confusing. 527 * Does not utilize existing ReferenceGrant mechanism. 528 * May be more difficult to understand which Routes are attached to a Gateway. 529 * Adding/replacing a Gateway requires changes to Routes. 530 531 ### Potential Expansion 532 In the future, it may be useful to add a selector from Route -> Parent. Although 533 this would enable greater flexibility, it also significantly increases 534 complexity. 535 536 ## Alternatives 537 538 ### 1. ReferenceGrant with Gateways selecting Routes 539 540  541 542 A compelling alternative to this proposal would involve retaining the Route 543 selector in Gateway and replacing the trust concept in Routes with 544 ReferenceGrant. To represent the same example as above, we'd use a Route 545 selector on Gateway, a corresponding label on the HTTPRoute, and a 546 ReferenceGrant that allowed it: 547 548 ```yaml 549 kind: Gateway 550 metadata: 551 name: xlb 552 namespace: infra 553 spec: 554 listeners: 555 - name: foo 556 hostname: foo.com 557 port: 80 558 routes: 559 kind: HTTPRoute 560 selector: 561 gateway: xlb 562 namespaces: 563 from: Selector 564 selector: 565 kubernetes.io/metadata.name: foo 566 --- 567 kind: HTTPRoute 568 metadata: 569 name: foo 570 namespace: foo 571 labels: 572 gateway: xlb 573 spec: 574 rules: 575 - name: abc 576 matches: 577 - path: /bar 578 --- 579 kind: ReferenceGrant 580 metadata: 581 name: infra-gateways 582 namespace: foo 583 spec: 584 from: 585 - group: gateway.networking.k8s.io 586 kind: Gateway 587 namespace: infra 588 to: 589 - group: gateway.networking.k8s.io 590 kind: HTTPRoute 591 ``` 592 593 #### Advantages 594 595 * Consistent use of ReferenceGrant throughout the API. 596 * Provides a single way of binding Gateways to Routes. 597 598 #### Disadvantages 599 600 * Even the simplest cross-namespace reference from Gateway -> Route would 601 require a ReferenceGrant in each target namespace. 602 * Existing demos and examples would become significantly more verbose. 603 * Does not support attaching Routes to arbitrary parents. 604 * Does not prevent accidental exposure of Routes. 605 * Route owners have limited control in terms of which Gateways their Route is 606 attached to. 607 608 ### 2. ReferenceGrant with Routes referencing Gateways 609 610  611 612 The other way we could use ReferenceGrant would be with Routes referencing 613 Gateways. Unfortunately the nested structure of Gateways makes this nearly 614 impossible to do effectively. A core concept for Gateways is that each listener 615 should be able to attach to an entirely different set of Routes. For example, 616 a Gateway may want to delegate foo.com to the foo namespace and bar.com to the 617 bar namespace. Unfortunately that would be very difficult to recreate with 618 ReferenceGrant. 619 620 ReferenceGrant is fundamentally about trusting references from resource of kind 621 Foo in to resources of kind Bar. Names and section names are intentionally 622 excluded. If we added both of those concepts to ReferenceGrant, this would be 623 possible, but quite complex and verbose. This is what the example from above 624 would look like with this approach: 625 626 ```yaml 627 kind: Gateway 628 metadata: 629 name: lb 630 namespace: infra 631 spec: 632 listeners: 633 - name: foo 634 hostname: foo.com 635 port: 80 636 --- 637 kind: ReferenceGrant 638 metadata: 639 name: foo-lb 640 namespace: infra 641 spec: 642 from: 643 - group: gateway.networking.k8s.io 644 kind: HTTPRoute 645 namespace: foo 646 to: 647 - group: gateway.networking.k8s.io 648 kind: Gateway 649 name: lb 650 sectionName: foo 651 --- 652 kind: HTTPRoute 653 metadata: 654 name: foo 655 namespace: foo 656 spec: 657 parentRefs: 658 - kind: Gateway 659 namespace: infra 660 name: lb 661 sectionName: foo 662 rules: 663 - name: abc 664 matches: 665 - path: /bar 666 ``` 667 668 #### Advantages 669 670 * Consistent use of ReferenceGrant throughout the API. 671 * Provides a single way of binding Gateways to Routes. 672 * Supports attaching Routes to arbitrary parents. 673 * Prevents accidental exposure of Routes. 674 675 #### Disadvantages 676 677 * In most cases, each listener in a Gateway would require a unique 678 ReferenceGrant resource. 679 * Even the simplest cross-namespace reference from Route -> Gateway would 680 require a ReferenceGrant in each target namespace. This could either rule 681 out or significantly complicate self-service use-cases. 682 * Existing demos and examples would become significantly more verbose. 683 * ReferenceGrant would become more complex for all other use cases. 684 685 ## References 686 687 **GEPs** 688 689 * [GEP 709: ReferenceGrant + Cross Namespace References from Routes](/geps/gep-709) 690 691 **Docs:** 692 693 * [Gateway API References Doc](https://docs.google.com/document/d/18MoabVA-fr5XL9cYdf6cxclqRwFpOvHUXV_UYzSiooY/edit) 694 * [Simplifying Gateway Route Binding](https://docs.google.com/document/d/1YVyB2dizACWrn8Rj31hQFBwCYqgyFKsxKeeGlv-iQCs/edit) 695 696 **Issues:** 697 698 * [Simplify Gateway Route Binding](https://github.com/kubernetes-sigs/gateway-api/issues/594) 699 * [Route selector improvements on Gateway](https://github.com/kubernetes-sigs/gateway-api/issues/515)