From 9ec4c84478997bbd469a8a079e999052a3c711b7 Mon Sep 17 00:00:00 2001 From: Alex Snaps Date: Thu, 12 Dec 2024 09:15:18 -0500 Subject: [PATCH] Updated docs to the new CEL conditions & variables Signed-off-by: Alex Snaps --- doc/how-it-works.md | 40 ++++++++++++++++++------------------ doc/migrations/conditions.md | 4 +++- doc/server/configuration.md | 16 +++++++-------- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/doc/how-it-works.md b/doc/how-it-works.md index 96f6d44e..3bca6809 100644 --- a/doc/how-it-works.md +++ b/doc/how-it-works.md @@ -1,26 +1,27 @@ ## How it works -Limitador ensures that *the most restrictive limit configuration will apply*. +Limitador will increment counters for all `Limit`s that apply, if any of these counter is above its `Limit`'s +`max_value` the request will be considered to be _rate limited_. So think of it as if *the most restrictive limit +configuration will apply*. -Limitador will try to match each incoming descriptor with the same *namespaced* -counter's conditions and variables. -The namespace for the descriptors is defined by the `domain` field -whereas for the rate limit configuration the `namespace` field is being used. -For each matching counter, the counter is increased and the limits checked. +Limitador will evaluate whether a `Limit` applies against its *namespace*, its conditions and whether all variables are +resolvable. The namespace for the descriptors is defined by the `domain` field from the [ +`service.ratelimit.v3.RateLimitRequest`](https://www.envoyproxy.io/docs/envoy/latest/api-v3/service/ratelimit/v3/rls.proto#service-ratelimit-v3-ratelimitrequest). +For each matching `Limit`, its counter is increased and checked against the `Limit` `max_value`. One example to illustrate: -Let's say we have 1 rate limit configuration (one counter per config): +Let's say we have one rate limit: ```yaml -conditions: ["KEY_A == 'VALUE_A'"] +conditions: [ "descriptors[0].KEY_A == 'VALUE_A'" ] max_value: 1 seconds: 60 variables: [] namespace: example.org ``` -Limitador receives one descriptor with two entries: +Limitador Server receives a request with one descriptor with two entries: ```yaml domain: example.org @@ -30,20 +31,19 @@ descriptors: - OTHER_KEY: OTHER_VALUE ``` -The counter's condition will match. Then, the counter will be increased and the limit checked. +The counter's condition all match. Then, the counter will be increased and the limit checked. If the limit is exceeded, the request will be rejected with `429 Too Many Requests`, otherwise accepted. Note that the counter is being activated even though it does not match *all* the entries of the descriptor. The same rule applies for the *variables* field. -Currently, the implementation of *condition* only allow for *equal* (`==`) and *not equal* (`!=`) operators. -More operators will be implemented based off the use cases for them. +Conditions are [CEL](https://cel.dev) expressions evaluating to a `bool` value. The *variables* field is a list of keys. The matching rule is defined just as the existence of the list of descriptor entries with the -same key values. If *variables* is `variables: [A, B, C]`, -one descriptor matches if it has *at least* three entries with the same A, B, C keys. +same key values. If *variables* is `variables: ["descriptors[0].A", "descriptors[0].B", "descriptors[0].C]"`, +the limit will match if the first descriptor has *at least* three entries with the same A, B, C keys. Few examples to illustrate. @@ -60,7 +60,7 @@ descriptors: the following counters would **not** be activated. ```yaml -conditions: ["KEY_B == 'VALUE_B'"] +conditions: [ "descriptors[0].KEY_B == 'VALUE_B'" ] max_value: 1 seconds: 60 variables: [] @@ -70,8 +70,8 @@ Reason: conditions key does not exist ```yaml conditions: - - "KEY_A == 'VALUE_A'" - - "OTHER_KEY == 'WRONG_VALUE'" + - "descriptors[0].KEY_A == 'VALUE_A'" + - "descriptors[0].OTHER_KEY == 'WRONG_VALUE'" max_value: 1 seconds: 60 variables: [] @@ -83,16 +83,16 @@ Reason: not all the conditions match conditions: [] max_value: 1 seconds: 60 -variables: ["MY_VAR"] +variables: [ "descriptors[0].MY_VAR" ] namespace: example.org ``` Reason: the variable name does not exist ```yaml -conditions: ["KEY_B == 'VALUE_B'"] +conditions: [ "descriptors[0].KEY_B == 'VALUE_B'" ] max_value: 1 seconds: 60 -variables: ["MY_VAR"] +variables: [ "descriptors[0].MY_VAR" ] namespace: example.org ``` Reason: Both variables and conditions must match. In this particular case, only conditions match diff --git a/doc/migrations/conditions.md b/doc/migrations/conditions.md index ffd4bb49..225fc0d4 100644 --- a/doc/migrations/conditions.md +++ b/doc/migrations/conditions.md @@ -3,7 +3,9 @@ With `limitador-server` version `1.0.0` (and the `limitador` crate version `0.3.0`), the syntax for `condition`s within `limit` definitions has changed. -## Changes +# Note! This synthax has been deprecated as of version `2.0.0` + +## Changes when working with Limitador Server versions `1.x` ### The new syntax diff --git a/doc/server/configuration.md b/doc/server/configuration.md index 3e1bc519..7998768d 100644 --- a/doc/server/configuration.md +++ b/doc/server/configuration.md @@ -94,9 +94,9 @@ Here is an example of such a limit definition: max_value: 10 seconds: 60 conditions: - - "req_method == 'GET'" + - "descriptors[0].req_method == 'GET'" variables: - - user_id + - descriptors[0].user_id ``` - `namespace` namespaces the limit, will generally be the domain, [see here](../how-it-works.md) @@ -112,13 +112,11 @@ Here is an example of such a limit definition: Each `condition` is an expression producing a boolean value (`true` or `false`). All `conditions` _must_ evaluate to `true` for the `limit` to be applied on a request. -Expressions follow the following syntax: `$IDENTIFIER $OP $STRING_LITERAL`, where: - - - `$IDENTIFIER` will be used to resolve the value at evaluation time, e.g. `role` - - `$OP` is an operator, either `==` or `!=` - - `$STRING_LITERAL` is a literal string value, `"` or `'` demarcated, e.g. `"admin"` - -So that `role != "admin"` would apply the limit on request from all users, but `admin`'s. +These predicates are [CEL](https://cel.dev/) Expressions that operate on the context provided by the `Limit` itself +(it's `id` and `name`fields), along with the `descriptors` from Envoy's +[ +`service.ratelimit.v3.RateLimitRequest`](https://www.envoyproxy.io/docs/envoy/latest/api-v3/service/ratelimit/v3/rls.proto#service-ratelimit-v3-ratelimitrequest), +each of which being exposed a `List` of `Map` with both keys and values as `String`. ### Counter storages