From 26dec1c44d3c48387d782ad9acd5fad7cf496230 Mon Sep 17 00:00:00 2001 From: Shane Utt Date: Tue, 18 Jan 2022 10:11:39 -0500 Subject: [PATCH 1/2] docs: tweak GEP 735 for PR reviews --- site-src/geps/gep-735.md | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/site-src/geps/gep-735.md b/site-src/geps/gep-735.md index 323f6ce4f7..9291803756 100644 --- a/site-src/geps/gep-735.md +++ b/site-src/geps/gep-735.md @@ -1,7 +1,7 @@ # GEP-735: TCP and UDP addresses matching * Issue: [#735](https://github.com/kubernetes-sigs/gateway-api/issues/735) -* Status: Provisional +* Status: Implemented ## TLDR @@ -32,7 +32,7 @@ level of tuning options for L4 traffic routing at a level below the `Gateway`. The API changes include the following new types: - `AddressMatch` to indicate the IP for address matching -- `TrafficMatches` to configure matching according to network address +- `AddressRouteMatches` to configure matching according to network address These types enable the address matching required, with some active considerations about how to leave these open ended for later expansion. @@ -78,13 +78,13 @@ type AddressMatch struct { } ``` -### TrafficMatches Type +### AddressRouteMatches Type Using the new `AddressMatch` type matches can be expressed in topical lists on -`TCPRoute` and `UDPRoute` using the new `TrafficMatches` type: +`TCPRoute` and `UDPRoute` using the new `AddressRouteMatches` type: ```go -type TrafficMatches struct { +type AddressRouteMatches struct { // SourceAddresses indicates the originating (source) network // addresses which are valid for routing traffic. // @@ -96,20 +96,25 @@ type TrafficMatches struct { // // Support: Core DestinationAddresses []AddressMatch `json:"destinationAddresses"` +} ``` This type becomes an optional field and shared by both `TCPRouteRule` and -`UDPRouteRule`: +`UDPRouteRule` as a list: ```go type TCPRouteRule struct { // Matches add rules for filtering traffic to backends based on addresses. // // +optional - Matches *TrafficMatches `json:"matches"` + Matches []AddressRouteMatches `json:"matches,omitempty"` } ``` +Each element in `[]AddressRouteMatches` should be implemented as an `OR` style +match (e.g. the inbound traffic matches as long as at least one of the separate +`AddressRouteMatches` rules is matched). + The above would make the following YAML examples possible: ```yaml @@ -120,7 +125,7 @@ metadata: spec: rules: - matches: - sourceAddresses: + - sourceAddresses: - value: "192.168.1.1" - value: "FE80::0202:B3FF:FE1E:8329" - type: NamedAddress From 5f448f55bb08f3bb857fed145c336e8716969b45 Mon Sep 17 00:00:00 2001 From: Shane Utt Date: Tue, 18 Jan 2022 10:12:12 -0500 Subject: [PATCH 2/2] feat: implement L4 traffic matching (GEP-735) --- apis/v1alpha2/gateway_types.go | 35 +------- apis/v1alpha2/shared_types.go | 83 +++++++++++++++++ apis/v1alpha2/tcproute_types.go | 7 ++ apis/v1alpha2/udproute_types.go | 7 ++ apis/v1alpha2/zz_generated.deepcopy.go | 63 +++++++++++++ .../gateway.networking.k8s.io_tcproutes.yaml | 88 +++++++++++++++++++ .../gateway.networking.k8s.io_udproutes.yaml | 88 +++++++++++++++++++ .../v1alpha2/traffic-matching-tcp.yaml | 15 ++++ hack/verify-examples-kind.sh | 6 ++ site-src/v1alpha2/guides/tcp.md | 22 +++++ 10 files changed, 380 insertions(+), 34 deletions(-) create mode 100644 examples/experimental/v1alpha2/traffic-matching-tcp.yaml diff --git a/apis/v1alpha2/gateway_types.go b/apis/v1alpha2/gateway_types.go index 54ba0924f1..7b16bdce54 100644 --- a/apis/v1alpha2/gateway_types.go +++ b/apis/v1alpha2/gateway_types.go @@ -449,6 +449,7 @@ type GatewayAddress struct { // Type of the address. // // +optional + // +kubebuilder:validation:Enum=IPAddress;Hostname;NamedAddress // +kubebuilder:default=IPAddress Type *AddressType `json:"type,omitempty"` @@ -462,40 +463,6 @@ type GatewayAddress struct { Value string `json:"value"` } -// AddressType defines how a network address is represented as a text string. -// -// If the requested address is unsupported, the controller -// should raise the "Detached" listener status condition on -// the Gateway with the "UnsupportedAddress" reason. -// -// +kubebuilder:validation:Enum=IPAddress;Hostname;NamedAddress -type AddressType string - -const ( - // A textual representation of a numeric IP address. IPv4 - // addresses must be in dotted-decimal form. IPv6 addresses - // must be in a standard IPv6 text representation - // (see [RFC 5952](https://tools.ietf.org/html/rfc5952)). - // - // Support: Extended - IPAddressType AddressType = "IPAddress" - - // A Hostname represents a DNS based ingress point. This is similar to the - // corresponding hostname field in Kubernetes load balancer status. For - // example, this concept may be used for cloud load balancers where a DNS - // name is used to expose a load balancer. - // - // Support: Extended - HostnameAddressType AddressType = "Hostname" - - // A NamedAddress provides a way to reference a specific IP address by name. - // For example, this may be a name or other unique identifier that refers - // to a resource on a cloud provider such as a static IP. - // - // Support: Implementation-Specific - NamedAddressType AddressType = "NamedAddress" -) - // GatewayStatus defines the observed state of Gateway. type GatewayStatus struct { // Addresses lists the IP addresses that have actually been diff --git a/apis/v1alpha2/shared_types.go b/apis/v1alpha2/shared_types.go index 78b457293e..a10715adb4 100644 --- a/apis/v1alpha2/shared_types.go +++ b/apis/v1alpha2/shared_types.go @@ -399,3 +399,86 @@ type AnnotationKey string // +kubebuilder:validation:MinLength=0 // +kubebuilder:validation:MaxLength=4096 type AnnotationValue string + +// AddressRouteMatches defines AddressMatch rules for inbound traffic according to +// source and/or destination address of a packet or connection. +type AddressRouteMatches struct { + // SourceAddresses indicates the originating (source) network + // addresses which are valid for routing traffic. + // + // Support: Extended + SourceAddresses []AddressMatch `json:"sourceAddresses"` + + // DestinationAddresses indicates the destination network addresses + // which are valid for routing traffic. + // + // Support: Extended + DestinationAddresses []AddressMatch `json:"destinationAddresses"` +} + +// AddressMatch defines matching rules for network addresses by type. +type AddressMatch struct { + // Type of the address, either IPAddress or NamedAddress. + // + // If NamedAddress is used this is a custom and specific value for each + // implementation to handle (and add validation for) according to their + // own needs. + // + // For IPAddress the implementor may expect either IPv4 or IPv6. + // + // Support: Core (IPAddress) + // Support: Custom (NamedAddress) + // + // +optional + // +kubebuilder:validation:Enum=IPAddress;NamedAddress + // +kubebuilder:default=IPAddress + Type *AddressType `json:"type,omitempty"` + + // Value of the address. The validity of the values will depend + // on the type and support by the controller. + // + // If implementations support proxy-protocol (see: + // https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) they + // must respect the connection metadata from proxy-protocol + // in the match logic implemented for these address values. + // + // Examples: `1.2.3.4`, `128::1`, `my-named-address`. + // + // Support: Core + // + // +kubebuilder:validation:MinLength=1 + // +kubebuilder:validation:MaxLength=253 + Value string `json:"value"` +} + +// AddressType defines how a network address is represented as a text string. +type AddressType string + +const ( + // A textual representation of a numeric IP address. IPv4 + // addresses must be in dotted-decimal form. IPv6 addresses + // must be in a standard IPv6 text representation + // (see [RFC 5952](https://tools.ietf.org/html/rfc5952)). + // + // This type is intended for specific addresses. Address ranges are not + // supported (e.g. you can not use a CIDR range like 127.0.0.0/24 as an + // IPAddress). + // + // Support: Extended + IPAddressType AddressType = "IPAddress" + + // A Hostname represents a DNS based ingress point. This is similar to the + // corresponding hostname field in Kubernetes load balancer status. For + // example, this concept may be used for cloud load balancers where a DNS + // name is used to expose a load balancer. + // + // Support: Extended + HostnameAddressType AddressType = "Hostname" + + // A NamedAddress provides a way to reference a specific IP address by name. + // For example, this may be a name or other unique identifier that refers + // to a resource on a cloud provider such as a static IP. + // + // Support: Implementation-Specific + NamedAddressType AddressType = "NamedAddress" +) diff --git a/apis/v1alpha2/tcproute_types.go b/apis/v1alpha2/tcproute_types.go index 60d805787d..7cac3b160c 100644 --- a/apis/v1alpha2/tcproute_types.go +++ b/apis/v1alpha2/tcproute_types.go @@ -59,6 +59,13 @@ type TCPRouteStatus struct { // TCPRouteRule is the configuration for a given rule. type TCPRouteRule struct { + // Matches are rules for routing traffic to backends based on addresses. + // + // +optional + // +kubebuilder:validation:MaxItems=16 + // + Matches []AddressRouteMatches `json:"matches,omitempty"` + // BackendRefs defines the backend(s) where matching requests should be // sent. If unspecified or invalid (refers to a non-existent resource or a // Service with no endpoints), the underlying implementation MUST actively diff --git a/apis/v1alpha2/udproute_types.go b/apis/v1alpha2/udproute_types.go index 361713afb2..99d7134568 100644 --- a/apis/v1alpha2/udproute_types.go +++ b/apis/v1alpha2/udproute_types.go @@ -59,6 +59,13 @@ type UDPRouteStatus struct { // UDPRouteRule is the configuration for a given rule. type UDPRouteRule struct { + // Matches add rules for filtering traffic to backends based on addresses. + // + // +optional + // +kubebuilder:validation:MaxItems=16 + // + Matches []AddressRouteMatches `json:"matches,omitempty"` + // BackendRefs defines the backend(s) where matching requests should be // sent. If unspecified or invalid (refers to a non-existent resource or a // Service with no endpoints), the underlying implementation MUST actively diff --git a/apis/v1alpha2/zz_generated.deepcopy.go b/apis/v1alpha2/zz_generated.deepcopy.go index 302f2e80eb..846c6d0bfc 100644 --- a/apis/v1alpha2/zz_generated.deepcopy.go +++ b/apis/v1alpha2/zz_generated.deepcopy.go @@ -26,6 +26,55 @@ import ( "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AddressMatch) DeepCopyInto(out *AddressMatch) { + *out = *in + if in.Type != nil { + in, out := &in.Type, &out.Type + *out = new(AddressType) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AddressMatch. +func (in *AddressMatch) DeepCopy() *AddressMatch { + if in == nil { + return nil + } + out := new(AddressMatch) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AddressRouteMatches) DeepCopyInto(out *AddressRouteMatches) { + *out = *in + if in.SourceAddresses != nil { + in, out := &in.SourceAddresses, &out.SourceAddresses + *out = make([]AddressMatch, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.DestinationAddresses != nil { + in, out := &in.DestinationAddresses, &out.DestinationAddresses + *out = make([]AddressMatch, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AddressRouteMatches. +func (in *AddressRouteMatches) DeepCopy() *AddressRouteMatches { + if in == nil { + return nil + } + out := new(AddressRouteMatches) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *AllowedRoutes) DeepCopyInto(out *AllowedRoutes) { *out = *in @@ -1317,6 +1366,13 @@ func (in *TCPRouteList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *TCPRouteRule) DeepCopyInto(out *TCPRouteRule) { *out = *in + if in.Matches != nil { + in, out := &in.Matches, &out.Matches + *out = make([]AddressRouteMatches, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } if in.BackendRefs != nil { in, out := &in.BackendRefs, &out.BackendRefs *out = make([]BackendRef, len(*in)) @@ -1562,6 +1618,13 @@ func (in *UDPRouteList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *UDPRouteRule) DeepCopyInto(out *UDPRouteRule) { *out = *in + if in.Matches != nil { + in, out := &in.Matches, &out.Matches + *out = make([]AddressRouteMatches, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } if in.BackendRefs != nil { in, out := &in.BackendRefs, &out.BackendRefs *out = make([]BackendRef, len(*in)) diff --git a/config/crd/experimental/gateway.networking.k8s.io_tcproutes.yaml b/config/crd/experimental/gateway.networking.k8s.io_tcproutes.yaml index 3bea659623..567c6a7090 100644 --- a/config/crd/experimental/gateway.networking.k8s.io_tcproutes.yaml +++ b/config/crd/experimental/gateway.networking.k8s.io_tcproutes.yaml @@ -218,6 +218,94 @@ spec: maxItems: 16 minItems: 1 type: array + matches: + description: "Matches are rules for routing traffic to backends + based on addresses. \n " + items: + description: AddressRouteMatches defines AddressMatch rules + for inbound traffic according to source and/or destination + address of a packet or connection. + properties: + destinationAddresses: + description: "DestinationAddresses indicates the destination + network addresses which are valid for routing traffic. + \n Support: Extended" + items: + description: AddressMatch defines matching rules for + network addresses by type. + properties: + type: + default: IPAddress + description: "Type of the address, either IPAddress + or NamedAddress. \n If NamedAddress is used this + is a custom and specific value for each implementation + to handle (and add validation for) according to + their own needs. \n For IPAddress the implementor + may expect either IPv4 or IPv6. \n Support: Core + (IPAddress) Support: Custom (NamedAddress)" + enum: + - IPAddress + - NamedAddress + type: string + value: + description: "Value of the address. The validity + of the values will depend on the type and support + by the controller. \n If implementations support + proxy-protocol (see: https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) + they must respect the connection metadata from + proxy-protocol in the match logic implemented + for these address values. \n Examples: `1.2.3.4`, + `128::1`, `my-named-address`. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string + required: + - value + type: object + type: array + sourceAddresses: + description: "SourceAddresses indicates the originating + (source) network addresses which are valid for routing + traffic. \n Support: Extended" + items: + description: AddressMatch defines matching rules for + network addresses by type. + properties: + type: + default: IPAddress + description: "Type of the address, either IPAddress + or NamedAddress. \n If NamedAddress is used this + is a custom and specific value for each implementation + to handle (and add validation for) according to + their own needs. \n For IPAddress the implementor + may expect either IPv4 or IPv6. \n Support: Core + (IPAddress) Support: Custom (NamedAddress)" + enum: + - IPAddress + - NamedAddress + type: string + value: + description: "Value of the address. The validity + of the values will depend on the type and support + by the controller. \n If implementations support + proxy-protocol (see: https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) + they must respect the connection metadata from + proxy-protocol in the match logic implemented + for these address values. \n Examples: `1.2.3.4`, + `128::1`, `my-named-address`. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string + required: + - value + type: object + type: array + required: + - destinationAddresses + - sourceAddresses + type: object + maxItems: 16 + type: array type: object maxItems: 16 minItems: 1 diff --git a/config/crd/experimental/gateway.networking.k8s.io_udproutes.yaml b/config/crd/experimental/gateway.networking.k8s.io_udproutes.yaml index 5c6e5a1bd0..a8bb16104b 100644 --- a/config/crd/experimental/gateway.networking.k8s.io_udproutes.yaml +++ b/config/crd/experimental/gateway.networking.k8s.io_udproutes.yaml @@ -218,6 +218,94 @@ spec: maxItems: 16 minItems: 1 type: array + matches: + description: "Matches add rules for filtering traffic to backends + based on addresses. \n " + items: + description: AddressRouteMatches defines AddressMatch rules + for inbound traffic according to source and/or destination + address of a packet or connection. + properties: + destinationAddresses: + description: "DestinationAddresses indicates the destination + network addresses which are valid for routing traffic. + \n Support: Extended" + items: + description: AddressMatch defines matching rules for + network addresses by type. + properties: + type: + default: IPAddress + description: "Type of the address, either IPAddress + or NamedAddress. \n If NamedAddress is used this + is a custom and specific value for each implementation + to handle (and add validation for) according to + their own needs. \n For IPAddress the implementor + may expect either IPv4 or IPv6. \n Support: Core + (IPAddress) Support: Custom (NamedAddress)" + enum: + - IPAddress + - NamedAddress + type: string + value: + description: "Value of the address. The validity + of the values will depend on the type and support + by the controller. \n If implementations support + proxy-protocol (see: https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) + they must respect the connection metadata from + proxy-protocol in the match logic implemented + for these address values. \n Examples: `1.2.3.4`, + `128::1`, `my-named-address`. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string + required: + - value + type: object + type: array + sourceAddresses: + description: "SourceAddresses indicates the originating + (source) network addresses which are valid for routing + traffic. \n Support: Extended" + items: + description: AddressMatch defines matching rules for + network addresses by type. + properties: + type: + default: IPAddress + description: "Type of the address, either IPAddress + or NamedAddress. \n If NamedAddress is used this + is a custom and specific value for each implementation + to handle (and add validation for) according to + their own needs. \n For IPAddress the implementor + may expect either IPv4 or IPv6. \n Support: Core + (IPAddress) Support: Custom (NamedAddress)" + enum: + - IPAddress + - NamedAddress + type: string + value: + description: "Value of the address. The validity + of the values will depend on the type and support + by the controller. \n If implementations support + proxy-protocol (see: https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt) + they must respect the connection metadata from + proxy-protocol in the match logic implemented + for these address values. \n Examples: `1.2.3.4`, + `128::1`, `my-named-address`. \n Support: Core" + maxLength: 253 + minLength: 1 + type: string + required: + - value + type: object + type: array + required: + - destinationAddresses + - sourceAddresses + type: object + maxItems: 16 + type: array type: object maxItems: 16 minItems: 1 diff --git a/examples/experimental/v1alpha2/traffic-matching-tcp.yaml b/examples/experimental/v1alpha2/traffic-matching-tcp.yaml new file mode 100644 index 0000000000..0d5095640a --- /dev/null +++ b/examples/experimental/v1alpha2/traffic-matching-tcp.yaml @@ -0,0 +1,15 @@ +apiVersion: gateway.networking.k8s.io/v1alpha2 +kind: TCPRoute +metadata: + name: traffic-matching-example +spec: + rules: + - matches: + - sourceAddresses: + - value: "192.168.1.1" + - value: "FE80::0202:B3FF:FE1E:8329" + destinationAddresses: + - value: "10.96.0.1" + backendRefs: + - name: my-service + port: 8080 diff --git a/hack/verify-examples-kind.sh b/hack/verify-examples-kind.sh index 80a2434841..7ab198a0da 100755 --- a/hack/verify-examples-kind.sh +++ b/hack/verify-examples-kind.sh @@ -62,6 +62,12 @@ for CHANNEL in experimental stable; do # Install all example gateway-api resources. kubectl apply --kubeconfig "${KUBECONFIG}" --recursive -f examples/v1alpha2 || res=$? + # Install all experimental example gateway-api resources when experimental mode is enabled + if [[ "${CHANNEL}" == "experimental" ]] ; then + echo "Experimental mode enabled: deploying experimental examples" + kubectl apply --kubeconfig "${KUBECONFIG}" --recursive -f examples/experimental/v1alpha2 || res=$? + fi + # Install invalid gateway-api resources. # None of these examples should be successfully configured # This is very hacky, sorry. diff --git a/site-src/v1alpha2/guides/tcp.md b/site-src/v1alpha2/guides/tcp.md index 74ecb898d0..2009a0ae9f 100644 --- a/site-src/v1alpha2/guides/tcp.md +++ b/site-src/v1alpha2/guides/tcp.md @@ -43,6 +43,28 @@ In this way each `TCPRoute` "attaches" itself to a different port on the `Gateway` so that the service `my-foo-service` is taking traffic for port `8080` from outside the cluster and `my-bar-service` takes the port `8090` traffic. +## Alternatives: Routing Using Traffic Matching + +!!! info "Experimental Channel" + + This functionality is currently only included in the "Experimental" channel + of Gateway API. For more information on release channels, refer to the + [related documentation](https://gateway-api.sigs.k8s.io/concepts/versioning/#adding-experimental-fields). + +In addition to simply matching traffic based on a Gateway's listeners (as seen +in the above examples) a `TCPRoute` can also match further on the traffic bound +for that listener: + +``` +{% include 'experimental/v1alpha2/traffic-matching-tcp.yaml' %} +``` + +In the above example the `matches` option for the `TCPRoute` rules enables the +route to only serve the traffic that matches specified destination and/or source +address patterns. In this way separate `TCPRoute` objects can be responsible +for routing different traffic on the same `Gateway` listener and can enable +significant flexibility for pure TCP routing. + ## Alternatives: TCP Traffic Routing Using Metadata While in the above examples we mainly focused on routing by port, it is also