Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated docs to the new CEL conditions & variables #408

Merged
merged 1 commit into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 20 additions & 20 deletions doc/how-it-works.md
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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.

Expand All @@ -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: []
Expand All @@ -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: []
Expand All @@ -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
4 changes: 3 additions & 1 deletion doc/migrations/conditions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
16 changes: 7 additions & 9 deletions doc/server/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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

Expand Down
Loading