Skip to content

Commit

Permalink
Merge pull request #67 from Kuadrant/auth-dev-env
Browse files Browse the repository at this point in the history
Integration development environment
  • Loading branch information
adam-cattermole authored Sep 18, 2024
2 parents 312710e + cb74375 commit 40ce002
Show file tree
Hide file tree
Showing 8 changed files with 1,369 additions and 10 deletions.
18 changes: 18 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,21 @@ chmod a+x $(1)/bin/protoc ;\
rm -rf $$TMP_DIR ;\
}
endef

##@ Util

# go-install-tool will 'go install' any package $2 and install it to $1.
PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST))))
define go-install-tool
@[ -f $(1) ] || { \
set -e ;\
TMP_DIR=$$(mktemp -d) ;\
cd $$TMP_DIR ;\
go mod init tmp ;\
echo "Downloading $(2)" ;\
GOBIN=$(PROJECT_DIR)/bin go install $(2) ;\
rm -rf $$TMP_DIR ;\
}
endef

include ./make/*.mk
116 changes: 106 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,23 @@
A Proxy-Wasm module written in Rust, acting as a shim between Envoy and Limitador.

## Sample configuration

Following is a sample configuration used by the shim.

```yaml
failureMode: deny
rateLimitPolicies:
extensions:
auth-ext:
type: auth
endpoint: auth-cluster
failureMode: deny
ratelimit-ext:
type: ratelimit
endpoint: ratelimit-cluster
failureMode: deny
policies:
- name: rlp-ns-A/rlp-name-A
domain: rlp-ns-A/rlp-name-A
service: rate-limit-cluster
hostnames: ["*.toystore.com"]
hostnames: [ "*.toystore.com" ]
rules:
- conditions:
- allOf:
Expand All @@ -33,6 +41,12 @@ rateLimitPolicies:
- static:
key: admin
value: "1"
actions:
- extension: ratelimit-ext
data:
static:
key: host
value: rlp-ns-A/rlp-name-A
```
## Features
Expand All @@ -57,6 +71,7 @@ pub enum WhenConditionOperator {

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
Expand Down Expand Up @@ -95,10 +110,10 @@ 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`.
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

Prerequisites:
Expand Down Expand Up @@ -127,7 +142,85 @@ make build BUILD=release
cargo test
```

## Running local development environment
## Running local development environment (kind)

`docker` is required.

Run local development environment

```sh
make local-setup
```

This deploys a local kubernetes cluster using kind, with the local build of wasm-shim mapped to the envoy container. An
echo API as well as limitador, authorino, and some test policies are configured.

To expose the envoy endpoint run the following:

```sh
kubectl port-forward --namespace default deployment/envoy 8000:8000
```

There is then a single auth policy defined for e2e testing:

* `auth-a` which defines auth is required for requests to `/get` for the `AuthConfig` with `effective-route-1`

```sh
curl -H "Host: test.a.auth.com" http://127.0.0.1:8000/get -i
```

And three 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.

```sh
curl -H "Host: test.a.rlp.com" http://127.0.0.1:8000/get -i
```

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

```sh
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.

```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
```

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 }
```

To rebuild and deploy to the cluster:

```sh
make build local-rollout
```

Stop and clean up resources:

```sh
make local-cleanup
```

## Running local development environment (docker-compose legacy)

`docker` and `docker-compose` required.

Expand All @@ -139,7 +232,8 @@ make development

Three 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.
* `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
Expand Down Expand Up @@ -175,7 +269,9 @@ RateLimitDescriptor { entries: [Entry { key: "request.headers.My-Custom-Header-0
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.
**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:

Expand All @@ -187,7 +283,7 @@ According to the defined limits:
conditions:
- "limit_to_be_activated == '1'"
- "user_id == 'bob'"
variables: []
variables: [ ]
```
The third request in less than 10 seconds should return `429 Too Many Requests`.
Expand Down
126 changes: 126 additions & 0 deletions make/deploy.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
##@ Kind

.PHONY: kind kind-create-cluster kind-delete-cluster

KIND = $(PROJECT_PATH)/bin/kind
KIND_VERSION = v0.23.0
$(KIND):
$(call go-install-tool,$(KIND),sigs.k8s.io/kind@$(KIND_VERSION))

kind: $(KIND) ## Download kind locally if necessary.

KIND_CLUSTER_NAME ?= wasm-auth-local

kind-create-cluster: BUILD?=debug
kind-create-cluster: WASM_PATH=$(subst /,\/,$(PROJECT_PATH)/target/wasm32-unknown-unknown/$(BUILD))
kind-create-cluster: kind ## Create the "wasm-auth-local" kind cluster.
@{ \
TEMP_FILE=/tmp/kind-cluster-$$(openssl rand -hex 4).yaml ;\
cp $(PROJECT_PATH)/utils/kind/cluster.yaml $$TEMP_FILE ;\
$(SED) -i "s/\$$(WASM_PATH)/$(WASM_PATH)/g" $$TEMP_FILE ;\
KIND_EXPERIMENTAL_PROVIDER=$(CONTAINER_ENGINE) $(KIND) create cluster --name $(KIND_CLUSTER_NAME) --config $$TEMP_FILE ;\
rm -rf $$TEMP_FILE ;\
}

kind-delete-cluster: ## Delete the "wasm-auth-local" kind cluster.
- KIND_EXPERIMENTAL_PROVIDER=$(CONTAINER_ENGINE) $(KIND) delete cluster --name $(KIND_CLUSTER_NAME)


##@ Authorino

.PHONY: install-authorino-operator certs deploy-authorino

AUTHORINO_IMAGE ?= quay.io/kuadrant/authorino:latest
AUTHORINO_OPERATOR_NAMESPACE ?= authorino-operator
install-authorino-operator: ## Installs Authorino Operator and dependencies into the Kubernetes cluster configured in ~/.kube/config
curl -sL https://raw.githubusercontent.com/Kuadrant/authorino-operator/main/utils/install.sh | bash -s -- --git-ref main
kubectl patch deployment/authorino-webhooks -n $(AUTHORINO_OPERATOR_NAMESPACE) -p '{"spec":{"template":{"spec":{"containers":[{"name":"webhooks","image":"$(AUTHORINO_IMAGE)","imagePullPolicy":"IfNotPresent"}]}}}}'
kubectl -n $(AUTHORINO_OPERATOR_NAMESPACE) wait --timeout=300s --for=condition=Available deployments --all

TLS_ENABLED ?= true
AUTHORINO_INSTANCE ?= authorino
NAMESPACE ?= default
certs: sed ## Requests TLS certificates for the Authorino instance if TLS is enabled, cert-manager.io is installed, and the secret is not already present
ifeq (true,$(TLS_ENABLED))
ifeq (,$(shell kubectl -n $(NAMESPACE) get secret/authorino-oidc-server-cert 2>/dev/null))
curl -sl https://raw.githubusercontent.com/kuadrant/authorino/main/deploy/certs.yaml | $(SED) "s/\$$(AUTHORINO_INSTANCE)/$(AUTHORINO_INSTANCE)/g;s/\$$(NAMESPACE)/$(NAMESPACE)/g" | kubectl -n $(NAMESPACE) apply -f -
else
echo "tls cert secret found."
endif
else
echo "tls disabled."
endif

deploy-authorino: certs sed ## Deploys an instance of Authorino into the Kubernetes cluster configured in ~/.kube/config
@{ \
set -e ;\
TEMP_FILE=/tmp/authorino-deploy-$$(openssl rand -hex 4).yaml ;\
curl -sl https://raw.githubusercontent.com/kuadrant/authorino/main/deploy/authorino.yaml > $$TEMP_FILE ;\
$(SED) -i "s/\$$(AUTHORINO_INSTANCE)/$(AUTHORINO_INSTANCE)/g;s/\$$(TLS_ENABLED)/$(TLS_ENABLED)/g" $$TEMP_FILE ;\
kubectl -n $(NAMESPACE) apply -f $$TEMP_FILE ;\
kubectl patch -n $(NAMESPACE) authorino/$(AUTHORINO_INSTANCE) --type='merge' -p '{"spec":{"image": "$(AUTHORINO_IMAGE)"}}' ;\
rm -rf $$TEMP_FILE ;\
}


##@ Limitador

deploy-limitador:
kubectl create configmap limits --from-file=$(PROJECT_PATH)/utils/docker-compose/limits.yaml
kubectl -n $(NAMESPACE) apply -f $(PROJECT_PATH)/utils/deploy/limitador.yaml


##@ User Apps

.PHONY: user-apps


ifeq (true,$(TLS_ENABLED))
ENVOY_OVERLAY = tls
else
ENVOY_OVERLAY = notls
endif
user-apps: ## Deploys talker API and envoy
kubectl -n $(NAMESPACE) apply -f https://raw.githubusercontent.com/kuadrant/authorino-examples/main/talker-api/talker-api-deploy.yaml
kubectl -n $(NAMESPACE) apply -f $(PROJECT_PATH)/utils/deploy/envoy-$(ENVOY_OVERLAY).yaml
kubectl -n $(NAMESPACE) apply -f $(PROJECT_PATH)/utils/deploy/authconfig.yaml


##@ Util

.PHONY: local-setup local-env-setup local-cleanup local-rollout sed

local-setup: local-env-setup
kubectl -n $(NAMESPACE) wait --timeout=300s --for=condition=Available deployments --all
@{ \
echo "Now you can export the envoy service by doing:"; \
echo "kubectl port-forward --namespace $(NAMESPACE) deployment/envoy 8000:8000"; \
echo "After that, you can curl -H \"Host: myhost.com\" localhost:8000"; \
}

local-env-setup:
$(MAKE) kind-delete-cluster
$(MAKE) kind-create-cluster
$(MAKE) install-authorino-operator
$(MAKE) deploy-authorino
$(MAKE) deploy-limitador
$(MAKE) user-apps

local-cleanup: kind ## Delete the "wasm-auth-local" kind cluster.
$(MAKE) kind-delete-cluster

local-rollout:
$(MAKE) user-apps
kubectl rollout restart -n $(NAMESPACE) deployment/envoy
kubectl -n $(NAMESPACE) wait --timeout=300s --for=condition=Available deployments --all

ifeq ($(shell uname),Darwin)
SED=$(shell which gsed)
else
SED=$(shell which sed)
endif
sed: ## Checks if GNU sed is installed
ifeq ($(SED),)
@echo "Cannot find GNU sed installed."
exit 1
endif
28 changes: 28 additions & 0 deletions utils/deploy/authconfig.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
apiVersion: authorino.kuadrant.io/v1beta2
kind: AuthConfig
metadata:
name: talker-api-protection
spec:
hosts:
- effective-route-1
authentication:
"friends":
apiKey:
selector:
matchLabels:
group: friends
credentials:
authorizationHeader:
prefix: APIKEY
---
apiVersion: v1
kind: Secret
metadata:
name: api-key-1
labels:
authorino.kuadrant.io/managed-by: authorino
group: friends
stringData:
api_key: "ndyBzreUzF4zqDQsqSPMHkRhriEOtcRx"
type: Opaque
Loading

0 comments on commit 40ce002

Please sign in to comment.