Skip to content

Commit

Permalink
Merge pull request #37 from Kuadrant/wasm-configuration-v2
Browse files Browse the repository at this point in the history
Wasm configuration v2
  • Loading branch information
eguzki authored Aug 16, 2023
2 parents 58f807e + 59033b0 commit 9042214
Show file tree
Hide file tree
Showing 11 changed files with 978 additions and 720 deletions.
35 changes: 5 additions & 30 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ default = ["with-serde"]
with-serde = ["protobuf/with-serde"]

[dependencies]
proxy-wasm = "0.2"
proxy-wasm = "0.2.1"
serde_json = "1.0"
log = "0.4"
serde = { version = "1.0", features = ["derive"] }
Expand All @@ -23,7 +23,6 @@ prost-types = "0.11"
protobuf = { version = "2.27", features = ["with-serde"] }
thiserror = "1.0"
regex = "1"
serde_regex = "1.1.0"
radix_trie = "0.2.1"

[dev-dependencies]
Expand Down
156 changes: 133 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,95 @@ A Proxy-Wasm module written in Rust, acting as a shim between Envoy and Limitado
Following is a sample configuration used by the shim.

```yaml
failure_mode_deny: true
rate_limit_policies:
- name: toystore
rate_limit_domain: toystore-app
upstream_cluster: rate-limit-cluster
failureMode: deny
rateLimitPolicies:
- name: rlp-ns-A/rlp-name-A
domain: rlp-ns-A/rlp-name-A
service: rate-limit-cluster
hostnames: ["*.toystore.com"]
gateway_actions:
- rules:
- paths: ["/admin/toy"]
methods: ["GET"]
hosts: ["pets.toystore.com"]
configurations:
- actions:
- generic_key:
descriptor_key: admin
descriptor_value: "1"
```
rules:
- conditions:
- allOf:
- selector: request.url_path
operator: startswith
value: /get
- selector: request.host
operator: eq
value: test.toystore.com
- selector: request.method
operator: eq
value: GET
data:
- selector:
selector: request.headers.My-Custom-Header
- static:
key: admin
value: "1"
```
## Features
#### Condition operators implemented
```Rust
#[derive(Deserialize, PartialEq, Debug, Clone)]
pub enum WhenConditionOperator {
#[serde(rename = "eq")]
EqualOperator,
#[serde(rename = "neq")]
NotEqualOperator,
#[serde(rename = "startswith")]
StartsWithOperator,
#[serde(rename = "endswith")]
EndsWithOperator,
#[serde(rename = "matches")]
MatchesOperator,
}
```

The `matches` operator is a a simple globbing pattern implementation based on regular expressions.
The only characters taken into account are:
* `?`: 0 or 1 characters
* `*`: 0 or more characters
* `+`: 1 or more characters

#### Selectors

Selector of an attribute from the contextual properties provided by kuadrant.
Currently, only some of the
[Envoy Attributes](https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/advanced/attributes)
can be used.

The struct is

```Rust
#[derive(Deserialize, Debug, Clone)]
pub struct SelectorItem {
// Selector of an attribute from the contextual properties provided by kuadrant
// during request and connection processing
pub selector: String,

// If not set it defaults to `selector` field value as the descriptor key.
#[serde(default)]
pub key: Option<String>,

// An optional value to use if the selector is not found in the context.
// If not set and the selector is not found in the context, then no data is generated.
#[serde(default)]
pub default: Option<String>,
}
```

Selectors are tokenized at each non-escaped occurrence of a separator character `.`.
Example:

```
Input: this.is.a.exam\.ple -> Retuns: ["this", "is", "a", "exam.ple"].
```

Some path segments include dot `.` char in them. For instance envoy filter names: `envoy.filters.http.header_to_metadata`.
In that particular cases, the dot chat (separator), needs to be escaped.


## Building

Expand All @@ -44,7 +116,13 @@ Build the WASM module in release mode
make build BUILD=release
```

## Running/Testing locally
## Testing

```
cargo test
```

## Running local development environment

`docker` and `docker-compose` required.

Expand All @@ -56,28 +134,60 @@ make development

Three rate limit policies defined for e2e testing:

* `rlp-a`: Actions should not generate descriptors. Hence, rate limiting service should **not** be called.
* `rlp-a`: Only one data item. Data selector should not generate return any value. Thus, descriptor should be empty and rate limiting service should **not** be called.

```
curl -H "Host: test.a.com" http://127.0.0.1:18000/get
```

* `rlp-b`: Rules do not match. Hence, rate limiting service should **not** be called.
* `rlp-b`: Conditions do not match. Hence, rate limiting service should **not** be called.

```
curl -H "Host: test.b.com" http://127.0.0.1:18000/get
```

* `rlp-c`: Five descriptors from multiple action types should be generated. Hence, rate limiting service should be called.
* `rlp-c`: Four descriptors from multiple rules should be generated. Hence, rate limiting service should be called.

```
curl -H "Host: test.c.com" -H "x-forwarded-for: 127.0.0.1" -H "My-Custom-Header-01: my-custom-header-value-01" -H "My-Custom-Header-02: my-custom-header-value-02" -H "x-dyn-user-id: bob" http://127.0.0.1:18000/get
curl -H "Host: test.c.com" -H "x-forwarded-for: 127.0.0.1" -H "My-Custom-Header-01: my-custom-header-value-01" -H "x-dyn-user-id: bob" http://127.0.0.1:18000/get
```

**Note:** Dynamic metadata can also be set with `user-id` as the key if you add the header `x-dyn-user-id`. This is provided using [Header-To-Metadata filter](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/header_to_metadata_filter#config-http-filters-header-to-metadata).
The expected descriptors:

```
RateLimitDescriptor { entries: [Entry { key: "limit_to_be_activated", value: "1" }], limit: None }
```

```
RateLimitDescriptor { entries: [Entry { key: "source.address", value: "127.0.0.1:0" }], limit: None }
```

```
RateLimitDescriptor { entries: [Entry { key: "request.headers.My-Custom-Header-01", value: "my-custom-header-value-01" }], limit: None }
```

```
RateLimitDescriptor { entries: [Entry { key: "user_id", value: "bob" }], limit: None }
```

**Note:** Using [Header-To-Metadata filter](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/header_to_metadata_filter#config-http-filters-header-to-metadata), `x-dyn-user-id` header value is available in the metadata struct with the `user-id` key.

According to the defined limits:

```yaml
---
- namespace: rlp-ns-C/rlp-name-C
max_value: 2
seconds: 10
conditions:
- "limit_to_be_activated == '1'"
- "user_id == 'bob'"
variables: []
```
The third request in less than 10 seconds should return `429 Too Many Requests`.

Clean up all resources
### Clean up all resources

```
make stop-development
Expand Down
Loading

0 comments on commit 9042214

Please sign in to comment.