Project: Kuma (Orchestration & Management / Service Mesh)
File: 012-rate-limit-policy.md

Status: accepted Technical Story: https://github.com/kumahq/kuma/issues/4740 We want to create a resource for managing rate limiting. Rate Limiting in Kuma is implemented with Envoy's [rate limiting support](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/httpfilters/localratelimitfilter). There is an issue with the configuration of TCP rate limiting because works on listener level which can only allow limiting connections on the specific listener but without checking the origin of the request. Create a MeshRateLimit with basic TCP rate limit support Create a MeshHTTPRateLimit and MeshTCPRateLimit with basic TCP rate limit support Create advanced TCP rate limiting per requested service Chosen option: create a MeshRateLimit with basic TCP rate limit support Replace existing policies with new policy-matching compliant resources Add support for TCP connection limiting at the basic level Possibility to add global rate limiting in the future MeshRateLimit MeshHTTPRateLimit and MeshTCPRateLimit Below is a sample rate-limiting configuration. ```yaml spec: sources: match: kuma.io/service: "rediskuma-demosvc_6379" destinations: match: kuma.io/service: "demo-appkuma-demosvc_5000" conf: http: requests: 5 interval: 10s onRateLimit: status: 423 headers: key: "x-Kuma-rate-limited" value: "true" append: true ``` We are using which supports only HTTP configuration. The configuration translates to the Envoy configuration on the route and the listener filter: ```yaml { "match": { "prefix": "/", "headers": [ { "name": "x-kuma-tags", "saferegexmatch": { "google_re2": {}, "regex": ".&kuma.io/service=[^&]rediskuma-demosvc_6379[,&].*" } } ] }, "route": { "cluster": "localhost:5000", "timeout": "0s" }, "typedperfilter_config": { "envoy.filters.http.local_ratelimit": { "@type": "type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit", ... } } } ``` The differences between TCP and HTTP rate limiting in `targetRef` and `from` requires different validation of objects. We create one policy which can configure both TCP and HTTP rate limiting. Because, we can't identify incoming traffic when using TCP users can only set the TCP section when using the `Mesh` type of targetRef in the `from` section. For http these types of targetRef can be used: `Mesh|MeshSubset|MeshService|MeshServiceSubset`. Rate limiting can be configured on both HTTP connection managers and routes. Thanks to the header `x-kuma-tags`, which is propagated and set by Envoy, we can recognize the origin of the HTTP request. Top-level targetRef can have all available kinds: ```yaml targetRef: kind: Mesh|MeshSubset|MeshService|MeshServiceSubset|MeshGatewayRoute|MeshHTTPRoute name: ... ``` Rate limiting is an inbound policy so only `from` should be configured. ```yaml from: targetRef: kind: Mesh|MeshSubset|MeshService|MeshServiceSubset name: ... ``` Matching on MeshGatewayRoute and MeshHTTPRoute does not make sense (there isn't a route that a request originates from). ```yaml default: local: http: requests: 5 interval: 10s onRateLimit: status: 423 headers: key: "x-kuma-rate-limited" value: "true" append: true ``` `ExternalServices` are services running outside of the mesh, hence we are not able to configure their inbounds. We are going to support rate limiting of `ExternalServices` only when `ZoneEgress` is enabled. Rate limiting on TCP connection is more complicated because there is no easy way to match only for specific

Based on alternatives we decided to make it only available for all the clients. ```yaml targetRef: kind: Mesh|MeshSubset|MeshService|MeshServiceSubset name: ... ``` Matching on MeshGatewayRoute and MeshHTTPRoute does not make sense because we are configuring TCP listeners. Rate limiting is an inbound policy so only `from` should be configured. ```yaml from: targetRef: kind: Mesh name: ... ``` There is no easy way to match requesting services with the specific filter chain that's why we decided to support only configuring TCP rate limiting from all services. ```yaml default: local: tcp: connections: 100 interval: 10s ``` ```yaml type: MeshRateLimit mesh: example name: example-rate-limit spec: targetRef: kind: Mesh|MeshSubset|MeshService|MeshServiceSubset|MeshGatewayRoute|MeshHTTPRoute # or Mesh|MeshSubset|MeshService|MeshServiceSubset for TCP configuration name: example-rate-limit from: targetRef: kind: Mesh|MeshSubset|MeshService|MeshServiceSubset # or Mesh for TCP configuration name: backend mesh: example default: local: http: disabled: false requests: 5 interval: 10s onRateLimit: status: 423 headers: key: "x-kuma-rate-limited" value: "true" append: true tcp: disabled: false connections: 100 interval: 10s ``` Configuration of connection limits is done on listeners. In case of matching from which service a request arrived, we need to get some information about the request. There are two options: use SourceIP filter matching use SNI filter matching In this case, we need to configure a listener filter matcher with a list of all possible IPs from which requests can arrive. In the case of one service, it can be many IPs. Matching on many ips might not be the most efficient way and each change of IP requires listener reload. It requires a change of SNI that is set on cluster `servicename{mesh=name}` to something different `servicename{mesh=name}{origin=requesting_service}` to later match it in the filter chain. That is not the best idea and can cause issues in multi-zone setups. Apart from that, it requires mTLS to be enabled. ```yaml type: MeshRateLimit mesh: default name: default-rate-limit spec: targetRef: kind: MeshService name: backend from: targetRef: kind: MeshService name: frontend default: local: http: requests: 5 interval: 10s onRateLimit: status: 423 headers: key: "x-kuma-rate-limited" value: "true" append: true ``` ```yaml type: MeshRateLimit mesh: default name: default-rate-limit spec: targetRef: kind: MeshService name: backend from: targetRef: kind: Mesh name: default default: local: http: disabled: false requests: 5 interval: 10s onRateLimit: status: 423 headers: key: "x-kuma-rate-limited" value: "true" append: true ``` ```yaml type: MeshRateLimit mesh: default name: default-rate-limit spec: targetRef: kind: MeshService name: backend from: targetRef: kind: Mesh name: default default: local: tcp: disabled: false connections: 5 interval: 10s ``` ```yaml type: MeshRateLimit mesh: default name: default-rate-limit spec: targetRef: kind: MeshService name: backend from: targetRef: kind: Mesh name: default default: local: tcp: disabled: false connections: 5 interval: 10s http: false: true requests: 5 interval: 10s onRateLimit: status: 423 headers: key: "x-kuma-rate-limited" value: "true" append: true ``` ```yaml type: MeshRateLimit mesh: default name: default-rate-limit spec: targetRef: kind: MeshService name: backend from: targetRef: kind: MeshService name: frontend default: local: http: disabled: true ```