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

Rate limit group example #7393

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
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
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ repos:
rev: v5.0.0
hooks:
- id: trailing-whitespace
exclude: '(\.md|\.snap|\.avdl)$'
exclude: '(\.md|\.jwt|\.snap|\.avdl)$'
- id: end-of-file-fixer
exclude: site/layouts/shortcodes/nic-.*.html
exclude: (site/layouts/shortcodes/nic-.*.html|\.jwt)
- id: check-yaml
args: [--allow-multiple-documents]
exclude: ^(charts/nginx-ingress/templates)
Expand Down
2 changes: 1 addition & 1 deletion examples/custom-resources/rate-limit-jwt-claim/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ kubectl apply -f webapp.yaml
## Step 2 - Deploy the Rate Limit Policy

In this step, we create a policy with the name `rate-limit-jwt` that allows only 1 request per second coming from a
single IP address.
request containing a JWT claim `sub`.

Create the policy:

Expand Down
187 changes: 187 additions & 0 deletions examples/custom-resources/rate-limit-tiered-jwt-claim/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
# Rate Limit JWT claim

In this example, we deploy a web application, configure load balancing for it via a VirtualServer, and apply two rate
limit Policies, grouped in a tier, using a JWT claim `sub` as the key to the rate limit and using another JWT claim
`user_details.level` to determine which rate limit Policy is applied. One rate limit policy will be the default rate
limit for the group.

## Prerequisites

1. Follow the [installation](https://docs.nginx.com/nginx-ingress-controller/installation/installation-with-manifests/)
instructions to deploy the Ingress Controller.
1. Save the public IP address of the Ingress Controller into a shell variable:

```console
IC_IP=XXX.YYY.ZZZ.III
```

1. Save the HTTP port of the Ingress Controller into a shell variable:

```console
IC_HTTP_PORT=<port number>
```

## Step 1 - Deploy a Web Application

Create the application deployments and services:

```console
kubectl apply -f coffee.yaml
```

## Step 2 - Deploy the Rate Limit Policies

In this step, we create two Policies:

- one with the name `rate-limit-jwt-premium`, that allows 10 requests per second coming from a request containing a JWT claim `user_details.level` with a value of `Premium`,
- one with the name `rate-limit-jwt-basic` that allows 1 request per second coming from a request containing a JWT claim `user_details.level` with a value of `Basic`.

The `rate-limit-jwt-basic` Policy is also the default policy if there is not a `user_details.level` JWT claim present.

Create the policies:

```console
kubectl apply -f rate-limit.yaml
```

## Step 3 - Configure Load Balancing

Create a VirtualServer resource for the web application:

```console
kubectl apply -f virtual-server.yaml
```

Note that the VirtualServer references the policies `rate-limit-jwt-premium` & `rate-limit-jwt-basic` created in Step 2.

## Step 4 - Test the Premium Configuration

The Premium JWT payload used in this testing looks like:

```json
{
"user_details": {
"level": "Premium"
},
"sub": "client1",
"name": "John Doe"
}
```

In this test we are relying on the NGINX Plus `ngx_http_auth_jwt_module` to extract the `sub` claim from the JWT payload into the `$jwt_claim_sub` variable and use this as the rate limiting `key`. The NGINX Plus `ngx_http_auth_jwt_module` will also extract the `user_details.level` to select the correct rate limit policy to be applied.

Let's test the configuration. If you access the application at a rate that exceeds 10 requests per second, NGINX will
start rejecting your requests:

```console
curl --resolve cafe.example.com:$IC_HTTP_PORT:$IC_IP http://cafe.example.com:$IC_HTTP_PORT/coffee -H "Authorization: Bearer: `cat premium-token.jwt`"
```

```text
Server address: 10.8.1.19:8080
Server name: coffee-dc88fc766-zr7f8
. . .
```

```console
curl --resolve cafe.example.com:$IC_HTTP_PORT:$IC_IP http://cafe.example.com:$IC_HTTP_PORT/coffee -H "Authorization: Bearer: `cat premium-token.jwt`"
```

```text
<html>
<head><title>503 Service Temporarily Unavailable</title></head>
<body>
<center><h1>503 Service Temporarily Unavailable</h1></center>
</body>
</html>
```

> Note: The command result is truncated for the clarity of the example.

## Step 5 - Test the Basic Configuration

The Basic JWT payload used in this testing looks like:

```json
{
"user_details": {
"level": "Basic"
},
"sub": "client2",
"name": "Jane Doe"
}
```

This test is similar to Step 4, however, this time we will be setting the `user_details.level` JWT claim to `Basic`.

Let's test the configuration. If you access the application at a rate that exceeds 1 request per second, NGINX will
start rejecting your requests:

```console
curl --resolve cafe.example.com:$IC_HTTP_PORT:$IC_IP http://cafe.example.com:$IC_HTTP_PORT/coffee -H "Authorization: Bearer: `cat basic-token.jwt`"
```

```text
Server address: 10.8.1.19:8080
Server name: coffee-dc88fc766-zr7f8
. . .
```

```console
curl --resolve cafe.example.com:$IC_HTTP_PORT:$IC_IP http://cafe.example.com:$IC_HTTP_PORT/coffee -H "Authorization: Bearer: `cat basic-token.jwt`"
```

```text
<html>
<head><title>503 Service Temporarily Unavailable</title></head>
<body>
<center><h1>503 Service Temporarily Unavailable</h1></center>
</body>
</html>
```

> Note: The command result is truncated for the clarity of the example.

## Step 6 - Test the default Configuration

The default JWT payload used in this testing looks like:

```json
{
"sub": "client3",
"name": "Billy Bloggs"
}
```

This test is similar to Step 4 & 5, however, this time we will not be setting the `user_details.level` JWT claim but
will still be seeing the default `rate-limit-jwt-basic` Policy applied.

Let's test the configuration. If you access the application at a rate that exceeds 1 request per second, NGINX will
start rejecting your requests:

```console
curl --resolve cafe.example.com:$IC_HTTP_PORT:$IC_IP http://cafe.example.com:$IC_HTTP_PORT/coffee -H "Authorization: Bearer: `cat default-token.jwt`"
```

```text
Server address: 10.8.1.19:8080
Server name: coffee-dc88fc766-zr7f8
. . .
```

```console
curl --resolve cafe.example.com:$IC_HTTP_PORT:$IC_IP http://cafe.example.com:$IC_HTTP_PORT/coffee -H "Authorization: Bearer: `cat default-token.jwt`"
```

```text
<html>
<head><title>503 Service Temporarily Unavailable</title></head>
<body>
<center><h1>503 Service Temporarily Unavailable</h1></center>
</body>
</html>
```

> Note: The command result is truncated for the clarity of the example.
---
> Note: This example does not validate the JWT token sent in the request, you should use either of the [`JWT Using Local Kubernetes Secret`](https://docs.nginx.com/nginx-ingress-controller/configuration/policy-resource/#jwt-using-local-kubernetes-secret) or [`JWT Using JWKS From Remote Location`](https://docs.nginx.com/nginx-ingress-controller/configuration/policy-resource/#jwt-using-jwks-from-remote-location) for that purpose.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2RldGFpbHMiOnsibGV2ZWwiOiJCYXNpYyJ9LCJzdWIiOiJjbGllbnQyIiwibmFtZSI6IkphbmUgRG9lIn0.WeylllL0g70FQqtuz9HQh8oI7-1y9Qlx1_LVCZxAGLE
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
name: cafe
spec:
host: cafe.example.com
tls:
secret: cafe-secret
upstreams:
- name: coffee
service: coffee-svc
port: 80
policies:
- name: rate-limit-jwt-premium
- name: rate-limit-jwt-basic
routes:
- path: /coffee
action:
pass: coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: coffee
spec:
replicas: 1
selector:
matchLabels:
app: coffee
template:
metadata:
labels:
app: coffee
spec:
containers:
- name: coffee
image: nginxdemos/nginx-hello:plain-text
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: coffee-svc
spec:
ports:
- port: 80
targetPort: 8080
protocol: TCP
name: http
selector:
app: coffee
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJjbGllbnQzIiwibmFtZSI6IkJpbGx5IEJsb2dncyJ9.ARozS58-7UN5enKlH1AXt_QC_tuRoLC1I-lTc0UrJFo
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2RldGFpbHMiOnsibGV2ZWwiOiJQcmVtaXVtIn0sInN1YiI6ImNsaWVudDUiLCJuYW1lIjoiSm9obiBEb2UiLCJpYXQiOjE1MTYyMzkwMjJ9.LxYflVUa01jsoxo4TbC4h5pnqypBamV1XUqKdAGt968
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
apiVersion: k8s.nginx.org/v1
kind: Policy
metadata:
name: rate-limit-jwt-premium
spec:
rateLimit:
rate: 100r/s
key: ${jwt_claim_sub}
zoneSize: 10M
condition:
jwt:
claim: user_details.level
match: Premium
---
apiVersion: k8s.nginx.org/v1
kind: Policy
metadata:
name: rate-limit-jwt-basic
spec:
rateLimit:
rate: 10r/s
key: ${jwt_claim_sub}
zoneSize: 10M
condition:
jwt:
claim: user_details.level
match: Basic
default: true
---