Skip to content

Commit dfe4b0e

Browse files
authored
Tiered rate limits with variables (#7884)
1 parent 2f3091c commit dfe4b0e

File tree

50 files changed

+4971
-319
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+4971
-319
lines changed

config/crd/bases/k8s.nginx.org_policies.yaml

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,7 @@ spec:
188188
default:
189189
description: sets the rate limit in this policy to be the
190190
default if no conditions are met. In a group of policies
191-
with the same JWT condition, only one policy can be the
192-
default.
191+
with the same condition, only one policy can be the default.
193192
type: boolean
194193
jwt:
195194
description: defines a JWT condition to rate limit against.
@@ -207,6 +206,26 @@ spec:
207206
- claim
208207
- match
209208
type: object
209+
variables:
210+
description: defines a Variables condition to rate limit against.
211+
items:
212+
description: VariableCondition defines a condition to rate
213+
limit by a variable.
214+
properties:
215+
match:
216+
description: the value of the variable to match against.
217+
pattern: ^([^\s"'])*$
218+
type: string
219+
name:
220+
description: the name of the variable to match against.
221+
pattern: ^([^\s"'])*$
222+
type: string
223+
required:
224+
- match
225+
- name
226+
type: object
227+
maxItems: 1
228+
type: array
210229
type: object
211230
delay:
212231
type: integer

deploy/crds.yaml

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -350,8 +350,7 @@ spec:
350350
default:
351351
description: sets the rate limit in this policy to be the
352352
default if no conditions are met. In a group of policies
353-
with the same JWT condition, only one policy can be the
354-
default.
353+
with the same condition, only one policy can be the default.
355354
type: boolean
356355
jwt:
357356
description: defines a JWT condition to rate limit against.
@@ -369,6 +368,26 @@ spec:
369368
- claim
370369
- match
371370
type: object
371+
variables:
372+
description: defines a Variables condition to rate limit against.
373+
items:
374+
description: VariableCondition defines a condition to rate
375+
limit by a variable.
376+
properties:
377+
match:
378+
description: the value of the variable to match against.
379+
pattern: ^([^\s"'])*$
380+
type: string
381+
name:
382+
description: the name of the variable to match against.
383+
pattern: ^([^\s"'])*$
384+
type: string
385+
required:
386+
- match
387+
- name
388+
type: object
389+
maxItems: 1
390+
type: array
372391
type: object
373392
delay:
374393
type: integer
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
# Tiered Rate Limits with API Keys
2+
3+
In this example, we deploy a web application, configure load balancing for it via a VirtualServer, and apply two rate
4+
limit Policies, grouped in a tier, using the API Key client name as the key to the rate limit and using a regex of the client name to determine which rate limit Policy is applied. One rate limit policy will be the default ratelimit for the group.
5+
6+
> Note: This example makes use of the NGINX variables `$apikey_auth_token` & `apikey_client_name` which are made available by applying an API Key authentication Policy to your VirtualServer resource.
7+
8+
## Prerequisites
9+
10+
1. Follow the [installation](https://docs.nginx.com/nginx-ingress-controller/installation/installation-with-manifests/)
11+
instructions to deploy NGINX Ingress Controller.
12+
2. Save the public IP address of NGINX Ingress Controller into a shell variable:
13+
14+
```shell
15+
IC_IP=XXX.YYY.ZZZ.III
16+
```
17+
<!-- markdownlint-disable MD029 -->
18+
3. Save the HTTP port of NGINX Ingress Controller into a shell variable:
19+
<!-- markdownlint-enable MD029 -->
20+
```shell
21+
IC_HTTP_PORT=<port number>
22+
```
23+
24+
## Deploy a web application
25+
26+
Create the application deployments and services:
27+
28+
```shell
29+
kubectl apply -f coffee.yaml
30+
```
31+
32+
## Deploy the rate limit Policies
33+
34+
In this step, we create three Policies:
35+
36+
- `api-key-policy` which defines the API Key Policy
37+
- `rate-limit-apikey-premium`, that allows 5 requests per second coming from a request containing an API Key with a client name that ends with `premium`
38+
- `rate-limit-apikey-basic` that allows 1 request per second coming from a request containing an API Key with a client name that ends with `basic`
39+
40+
The `rate-limit-apikey-basic` Policy is also the default policy if the API Key client name does not match a tier.
41+
42+
Create the Policies:
43+
44+
```shell
45+
kubectl apply -f api-key-policy.yaml
46+
kubectl apply -f rate-limits.yaml
47+
```
48+
49+
## Deploy the API key authentication Secret
50+
51+
Create a Secret of type `nginx.org/apikey` with the name `api-key-client-secret` that will be used for authorization on the server level.
52+
53+
This Secret will contain a mapping of client names to base64 encoded API Keys.
54+
55+
```shell
56+
kubectl apply -f api-key-secret.yaml
57+
```
58+
59+
## Configure load balancing
60+
61+
Create a VirtualServer resource for the web application:
62+
63+
```shell
64+
kubectl apply -f cafe-virtual-server.yaml
65+
```
66+
67+
Note that the VirtualServer references the policies `api-key-policy`, `rate-limit-apikey-premium` & `rate-limit-apikey-basic` created in Step 2.
68+
69+
## Test the premium configuration
70+
71+
Let's test the configuration. If you access the application with an API Key in an expected header at a rate that exceeds 5 requests per second, NGINX will
72+
start rejecting your requests:
73+
74+
```shell
75+
while true; do
76+
curl --resolve cafe.example.com:$IC_HTTP_PORT:$IC_IP -H "X-header-name: client1premium" http://cafe.example.com:$IC_HTTP_PORT/coffee;
77+
sleep 0.1;
78+
done
79+
```
80+
81+
```text
82+
Server address: 10.8.1.19:8080
83+
Server name: coffee-dc88fc766-zr7f8
84+
85+
. . .
86+
87+
<html>
88+
<head><title>429 Too Many Requests</title></head>
89+
<body>
90+
<center><h1>429 Too Many Requests</h1></center>
91+
<hr><center>nginx/1.27.5</center>
92+
</body>
93+
</html>
94+
```
95+
96+
> Note: The command result is truncated for the clarity of the example.
97+
98+
## Test the basic configuration
99+
100+
This test is similar to the previous step, however, this time we will be setting the API Key in the header to a value that maps to the `client1-basic` client name.
101+
102+
Let's test the configuration. If you access the application at a rate that exceeds 1 request per second, NGINX will
103+
start rejecting your requests:
104+
105+
```shell
106+
while true; do
107+
curl --resolve cafe.example.com:$IC_HTTP_PORT:$IC_IP -H "X-header-name: client1basic" http://cafe.example.com:$IC_HTTP_PORT/coffee;
108+
sleep 0.5;
109+
done
110+
```
111+
112+
```text
113+
Server address: 10.8.1.19:8080
114+
Server name: coffee-dc88fc766-zr7f8
115+
116+
. . .
117+
118+
<html>
119+
<head><title>429 Too Many Requests</title></head>
120+
<body>
121+
<center><h1>429 Too Many Requests</h1></center>
122+
<hr><center>nginx/1.27.5</center>
123+
</body>
124+
</html>
125+
```
126+
127+
> Note: The command result is truncated for the clarity of the example.
128+
129+
## Test the default configuration
130+
131+
This test is similar to the previous two steps, however, this time we will setting the API Key in the header to a value that maps to the `random` client name, which matches neither of the regex patterns configured in the Policies. However, we will still be seeing the default `rate-limit-apikey-basic` Policy applied.
132+
133+
Let's test the configuration. If you access the application at a rate that exceeds 1 request per second, NGINX will
134+
start rejecting your requests:
135+
136+
```shell
137+
while true; do
138+
curl --resolve cafe.example.com:$IC_HTTP_PORT:$IC_IP -H "X-header-name: random" http://cafe.example.com:$IC_HTTP_PORT/coffee;
139+
sleep 0.5;
140+
done
141+
```
142+
143+
```text
144+
Server address: 10.8.1.19:8080
145+
Server name: coffee-dc88fc766-zr7f8
146+
147+
. . .
148+
149+
<html>
150+
<head><title>429 Too Many Requests</title></head>
151+
<body>
152+
<center><h1>429 Too Many Requests</h1></center>
153+
<hr><center>nginx/1.27.5</center>
154+
</body>
155+
</html>
156+
```
157+
158+
> Note: The command result is truncated for the clarity of the example.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
apiVersion: k8s.nginx.org/v1
2+
kind: Policy
3+
metadata:
4+
name: api-key-policy
5+
spec:
6+
apiKey:
7+
suppliedIn:
8+
header:
9+
- "X-header-name"
10+
clientSecret: api-key-client-secret
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
apiVersion: v1
2+
kind: Secret
3+
metadata:
4+
name: api-key-client-secret
5+
type: nginx.org/apikey
6+
data:
7+
client1-premium: Y2xpZW50MXByZW1pdW0= # client1premium
8+
client2-premium: Y2xpZW50MnByZW1pdW0= # client2premium
9+
client1-basic: Y2xpZW50MWJhc2lj # client1basic
10+
client2-basic: Y2xpZW50MmJhc2lj # client2basic
11+
random: cmFuZG9t # random
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
apiVersion: k8s.nginx.org/v1
2+
kind: VirtualServer
3+
metadata:
4+
name: cafe
5+
spec:
6+
host: cafe.example.com
7+
upstreams:
8+
- name: coffee
9+
service: coffee-svc
10+
port: 80
11+
policies:
12+
- name: api-key-policy
13+
- name: rate-limit-apikey-premium
14+
- name: rate-limit-apikey-basic
15+
routes:
16+
- path: /coffee
17+
action:
18+
pass: coffee
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: coffee
5+
spec:
6+
replicas: 1
7+
selector:
8+
matchLabels:
9+
app: coffee
10+
template:
11+
metadata:
12+
labels:
13+
app: coffee
14+
spec:
15+
containers:
16+
- name: coffee
17+
image: nginxdemos/nginx-hello:plain-text
18+
ports:
19+
- containerPort: 8080
20+
---
21+
apiVersion: v1
22+
kind: Service
23+
metadata:
24+
name: coffee-svc
25+
spec:
26+
ports:
27+
- port: 80
28+
targetPort: 8080
29+
protocol: TCP
30+
name: http
31+
selector:
32+
app: coffee
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
apiVersion: k8s.nginx.org/v1
2+
kind: Policy
3+
metadata:
4+
name: rate-limit-apikey-premium
5+
spec:
6+
rateLimit:
7+
rate: 5r/s
8+
key: ${apikey_auth_token}
9+
zoneSize: 10M
10+
condition:
11+
variables:
12+
- match: "~^.*-premium$"
13+
name: $apikey_client_name
14+
rejectCode: 429
15+
---
16+
apiVersion: k8s.nginx.org/v1
17+
kind: Policy
18+
metadata:
19+
name: rate-limit-apikey-basic
20+
spec:
21+
rateLimit:
22+
rate: 1r/s
23+
key: ${apikey_auth_token}
24+
zoneSize: 10M
25+
condition:
26+
variables:
27+
- match: "~^.*-basic$"
28+
name: $apikey_client_name
29+
default: true
30+
rejectCode: 429

0 commit comments

Comments
 (0)