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

source.remote_address well known attribute #111

Merged
merged 11 commits into from
Oct 18, 2024
35 changes: 35 additions & 0 deletions .github/workflows/e2e.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
---
name: e2e tests

on:
push:
branches:
- 'main'
pull_request:
branches:
- '*'
jobs:
remote_address:
name: Remote address integration test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
override: true
target: wasm32-unknown-unknown
- uses: arduino/setup-protoc@v1
with:
version: '3.x'
- uses: actions-rs/cargo@v1
with:
command: build
args: --target wasm32-unknown-unknown
- name: Run docker compose
run: |
docker compose -f ./e2e/remote-address/docker-compose.yaml run start_services
- name: Execute tests in the running services
run: |
make -f ./e2e/remote-address/Makefile test
53 changes: 29 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,7 @@ The only characters taken into account are:
#### 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.
See [Well Known Attributes](#Well-Known-Attributes) for more info about available attributes.

The struct is

Expand Down Expand Up @@ -111,6 +109,13 @@ 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.

### Well Known Attributes

| Attribute | Description |
| --- | --- |
| [Envoy Attributes](https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/advanced/attributes) | Contextual properties provided by Envoy during request and connection processing |
| `source.remote_address` | This attribute evaluates to the `trusted client address` (IP address without port) as it is being defined by [Envoy Doc](https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers#x-forwarded-for) |

## Building

Prerequisites:
Expand Down Expand Up @@ -172,7 +177,7 @@ curl -H "Host: test.a.auth.com" -H "Authorization: APIKEY IAMALICE" http://127.0
# HTTP/1.1 200 OK
```

And three rate limit policies defined for e2e testing:
And some rate limit policies defined for e2e testing:

* `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.
Expand All @@ -187,45 +192,45 @@ curl -H "Host: test.a.rlp.com" http://127.0.0.1:8000/get -i
curl -H "Host: test.b.rlp.com" http://127.0.0.1:8000/get -i
```

* `rlp-c`: Four descriptors from multiple rules should be generated. Hence, rate limiting service should be called.
* `rlp-c`: Descriptor entries from multiple data items should be generated. Hence, rate limiting service should be called.

```sh
curl -H "Host: test.c.rlp.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:8000/get -i
curl -H "Host: test.c.rlp.com" -H "x-forwarded-for: 50.0.0.1" -H "My-Custom-Header-01: my-custom-header-value-01" -H "x-dyn-user-id: bob" http://127.0.0.1:8000/get -i
```

* `multi-a` which defines two actions for authenticated ratelimiting.
The expected descriptor entries:

```sh
curl -H "Host: test.a.multi.com" http://127.0.0.1:8000/get -i
# HTTP/1.1 401 Unauthorized
```

Alice has 5 requests per 10 seconds:
```sh
while :; do curl --write-out '%{http_code}\n' --silent --output /dev/null -H "Authorization: APIKEY IAMALICE" -H "Host: test.a.multi.com" http://127.0.0.1:8000/get | grep -E --color "\b(429)\b|$"; sleep 1; done
Entry { key: "limit_to_be_activated", value: "1" }
```

Bob has 2 requests per 10 seconds:
```sh
while :; do curl --write-out '%{http_code}\n' --silent --output /dev/null -H "Authorization: APIKEY IAMBOB" -H "Host: test.a.multi.com" http://127.0.0.1:8000/get | grep -E --color "\b(429)\b|$"; sleep 1; done
```

The expected descriptors:
Entry { key: "source.address", value: "50.0.0.1:0" }
```

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

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

```
RateLimitDescriptor { entries: [Entry { key: "request.headers.My-Custom-Header-01", value: "my-custom-header-value-01" }], limit: None }
* `multi-a` which defines two actions for authenticated ratelimiting.

```sh
curl -H "Host: test.a.multi.com" http://127.0.0.1:8000/get -i
# HTTP/1.1 401 Unauthorized
```

Alice has 5 requests per 10 seconds:
```sh
while :; do curl --write-out '%{http_code}\n' --silent --output /dev/null -H "Authorization: APIKEY IAMALICE" -H "Host: test.a.multi.com" http://127.0.0.1:8000/get | grep -E --color "\b(429)\b|$"; sleep 1; done
```
RateLimitDescriptor { entries: [Entry { key: "user_id", value: "bob" }], limit: None }

Bob has 2 requests per 10 seconds:
```sh
while :; do curl --write-out '%{http_code}\n' --silent --output /dev/null -H "Authorization: APIKEY IAMBOB" -H "Host: test.a.multi.com" http://127.0.0.1:8000/get | grep -E --color "\b(429)\b|$"; sleep 1; done
```

To rebuild and deploy to the cluster:
Expand Down
23 changes: 23 additions & 0 deletions e2e/remote-address/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
SHELL = /usr/bin/env bash -o pipefail
.SHELLFLAGS = -ec
.DEFAULT_GOAL := gateway
MKFILE_PATH := $(abspath $(lastword $(MAKEFILE_LIST)))
WORKDIR := $(patsubst %/,%,$(dir $(MKFILE_PATH)))
DOCKER ?= $(shell which docker 2> /dev/null || echo "docker")

run:
$(DOCKER) compose -f docker-compose.yaml run start_services

test:
curl --silent --output /dev/null --fail --resolve test.example.com:18000:127.0.0.1 -H "X-Forwarded-For: 40.0.0.1" "http://test.example.com:18000"
curl --silent --output /dev/null --fail --resolve test.example.com:18000:127.0.0.1 -H "X-Forwarded-For: 50.0.0.1" "http://test.example.com:18000"
$(eval TMP := $(shell mktemp -d))
curl --silent --output $(TMP)/counters.json --fail "http://127.0.0.1:18080/counters/ratelimit-source"
# only one counter
NUM_COUNTERS=$$(jq --exit-status 'length' $(TMP)/counters.json) && test $${NUM_COUNTERS} -eq 1
# that counter must belong to 40.0.0.1
VARIABLE_COUNTER=$$(jq -r --exit-status '.[].set_variables."source.remote_address"' $(TMP)/counters.json) && [ "$${VARIABLE_COUNTER}" == "40.0.0.1" ]

clean:
$(DOCKER) compose down --volumes --remove-orphans
$(DOCKER) compose -f docker-compose.yaml down --volumes --remove-orphans
88 changes: 88 additions & 0 deletions e2e/remote-address/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
## Remote address integration test

This is a integration test to validate integration between Envoy and Kuadrant's Wasm module.

The Wasm module defines `source.remote_address` that should generate `trusted client address`
based on envoy configuration. If Envoy changes the contract, this test should fail.

This test is being added to the CI test suite

### Description

The Wasm configuration defines a set of rules for `*.example.com`.

```yaml
{
"name": "ratelimit-source",
"hostnames": [
"*.example.com"
],
"rules": [
{
"conditions": [
{
"allOf": [
{
"selector": "source.remote_address",
"operator": "neq",
"value": "50.0.0.1"
}
]
}
],
"actions": [
{
"extension": "limitador",
"scope": "ratelimit-source",
"data": [
{
"selector": {
"selector": "source.remote_address"
}
}
]
}
]
}
]
}
```

And a new limit configuration

```yaml
- namespace: ratelimit-source
max_value: 2
seconds: 30
conditions: []
variables:
- source.remote_address
```

That configuration enables source based rate limiting on `*.example.com` subdomains,
with only one "privileged" exception: the IP "50.0.0.1" will not be rate limited.

The test will run two requests:
* IP "40.0.0.1" -> the test will verify it is being rate limited inspecting limitador for counters
* IP "50.0.0.1" -> the test will verify it is not being rate limited inspecting limitador for counters

### Run Manually

It requires Wasm module being built at `target/wasm32-unknown-unknown/debug/wasm_shim.wasm`.
Check *Makefile* at the root of the project to build the module.

```
make run
```

Run the test

```
make test
```

### Clean up

```
make clean
```
56 changes: 56 additions & 0 deletions e2e/remote-address/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
---
services:
envoy:
image: envoyproxy/envoy:v1.31-latest
depends_on:
- limitador
- upstream
command:
- /usr/local/bin/envoy
- --config-path
- /etc/envoy.yaml
- --log-level
- info
- --component-log-level
- wasm:debug,http:debug,router:debug
- --service-cluster
- proxy
expose:
- "80"
- "8001"
ports:
- "18000:80"
- "18001:8001"
volumes:
- ./envoy.yaml:/etc/envoy.yaml
- ../../target/wasm32-unknown-unknown/debug/wasm_shim.wasm:/opt/kuadrant/wasm/wasm_shim.wasm
limitador:
image: quay.io/kuadrant/limitador:latest
command: ["limitador-server", "-vvv", "/opt/kuadrant/limits/limits.yaml"]
ports:
- "18080:8080"
- "18081:8081"
expose:
- "8080"
- "8081"
volumes:
- ./limits.yaml:/opt/kuadrant/limits/limits.yaml
upstream:
image: quay.io/kuadrant/authorino-examples:talker-api
environment:
PORT: 3000
expose:
- "3000"
start_services:
image: alpine
depends_on:
- envoy
command: >
/bin/sh -c "
while ! nc -z envoy 80;
do
echo sleeping;
sleep 1;
done;
echo Connected!
"
Loading
Loading