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

Add RateLimit policy support #1120

Merged
merged 5 commits into from
Sep 3, 2020
Merged
Show file tree
Hide file tree
Changes from 2 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
29 changes: 25 additions & 4 deletions deployments/common/policy-definition.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,9 @@ spec:
metadata:
type: object
spec:
description: 'PolicySpec is the spec of the Policy resource. The spec includes
multiple fields, where each field represents a different policy. Note:
currently we have only one policy -- AccessControl, but we will support
more in the future. Only one policy (field) is allowed.'
description: PolicySpec is the spec of the Policy resource. The spec includes
multiple fields, where each field represents a different policy. Only
one policy (field) is allowed.
type: object
properties:
accessControl:
Expand All @@ -54,3 +53,25 @@ spec:
type: array
items:
type: string
rateLimit:
description: RateLimit defines a rate limit policy.
type: object
properties:
burst:
type: integer
delay:
type: integer
dryRun:
type: boolean
key:
type: string
logLevel:
type: string
noDelay:
type: boolean
rate:
type: string
rejectCode:
type: integer
zoneSize:
type: string
29 changes: 25 additions & 4 deletions deployments/helm-chart/crds/policy.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,9 @@ spec:
metadata:
type: object
spec:
description: 'PolicySpec is the spec of the Policy resource. The spec includes
multiple fields, where each field represents a different policy. Note:
currently we have only one policy -- AccessControl, but we will support
more in the future. Only one policy (field) is allowed.'
description: PolicySpec is the spec of the Policy resource. The spec includes
multiple fields, where each field represents a different policy. Only
one policy (field) is allowed.
type: object
properties:
accessControl:
Expand All @@ -56,3 +55,25 @@ spec:
type: array
items:
type: string
rateLimit:
description: RateLimit defines a rate limit policy.
type: object
properties:
burst:
type: integer
delay:
type: integer
dryRun:
type: boolean
key:
type: string
logLevel:
type: string
noDelay:
type: boolean
rate:
type: string
rejectCode:
type: integer
zoneSize:
type: string
83 changes: 81 additions & 2 deletions docs-web/configuration/policy-resource.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Policy Resource

The Policy resource allows you to configure features like authentication, rate-limiting, and WAF, which you can add to your [VirtualServer and VirtualServerRoute resources](/nginx-ingress-controller/configuration/virtualserver-and-virtualserverroute-resources/). In the initial release, we are introducing support for access control based on the client IP address.
The Policy resource allows you to configure features like access control and rate-limiting, which you can add to your [VirtualServer and VirtualServerRoute resources](/nginx-ingress-controller/configuration/virtualserver-and-virtualserverroute-resources/)..
Dean-Coakley marked this conversation as resolved.
Show resolved Hide resolved

The resource is implemented as a [Custom Resource](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/).

Expand All @@ -16,6 +16,7 @@ This document is the reference documentation for the Policy resource. An example
- [Policy Specification](#policy-specification)
- [AccessControl](#accesscontrol)
- [AccessControl Merging Behavior](#accesscontrol-merging-behavior)
- [RateLimit](#ratelimit)
- [Using Policy](#using-policy)
- [Validation](#validation)
- [Structural Validation](#structural-validation)
Expand Down Expand Up @@ -50,9 +51,15 @@ spec:
* - ``accessControl``
- The access control policy based on the client IP address.
- `accessControl <#accesscontrol>`_
- Yes
- No*
* - ``rateLimit``
- The rate limit policy controls the rate of processing requests per a defined key.
- `rateLimit <#ratelimit>`_
- No*
```

\* A policy must include exactly one policy.

### AccessControl

The access control policy configures NGINX to deny or allow requests from clients with the specified IP addresses/subnets.
Expand Down Expand Up @@ -109,6 +116,78 @@ Referencing both allow and deny policies, as shown in the example below, is not
- name: allow-policy-two
```

### RateLimit

The rate limit policy configures NGINX to limit the processing rate of requests.

For example, the following policy will limit all subsequent requests coming from a single IP address once a rate of 10 requests per second is exceeded:
```yaml
rateLimit:
rate: 10r/s
zoneSize: 10M
key: ${binary_remote_addr}
```

> Note: The feature is implemented using the NGINX [ngx_http_limit_req_module](https://nginx.org/en/docs/http/ngx_http_limit_req_module.html).

```eval_rst
.. list-table::
:header-rows: 1

* - Field
- Description
- Type
- Required
* - ``rate``
- The rate of requests permitted. The rate is specified in requests per second (r/s) or requests per minute (r/m).
- ``string``
- Yes
* - ``key``
- The key which the rate limit is applied. Can contain text, variables, or a combination of them. Variables must be surrounded by ``${}``. For example: ``${$binary_remote_addr}``. Accepted variables are ``$binary_remote_addr``, ``$request_uri``, ``$url``, ``$http_``, ``$args``, ``$arg_``, ``$cookie_``.
Dean-Coakley marked this conversation as resolved.
Show resolved Hide resolved
- ``string``
- Yes
* - ``zoneSize``
- Size of the shared memory zone. Only positive values are allowed. Allowed suffixes are ``k`` or ``m``, if none are present ``k`` is assumed.
- ``string``
- Yes
* - ``delay``
- The delay parameter specifies a limit at which excessive requests become delayed. If not set all excessive requests are delayed.
- ``int``
- No*
* - ``noDelay``
- Disables the delaying of excessive requests while requests are being limited. Overrides ``delay`` if both are set.
- ``bool``
- No*
* - ``burst``
- Excessive requests are delayed until their number exceeds the ``burst`` size in which case the request is terminated with an error.
Dean-Coakley marked this conversation as resolved.
Show resolved Hide resolved
- ``int``
- No*
* - ``dryRun``
- Enables the dry run mode. In this mode, requests processing rate is not limited, however, in the shared memory zone, the number of excessive requests is accounted as usual.
Dean-Coakley marked this conversation as resolved.
Show resolved Hide resolved
- ``bool``
- No*
* - ``logLevel``
- Sets the desired logging level for cases when the server refuses to process requests due to rate exceeding, or delays request processing. Allowed values are ``info``, ``notice``, ``warn`` or ``error``. Default is ``error``.
- ``string``
- No*
* - ``rejectCode``
- Sets the status code to return in response to rejected requests. Must fall into the range ``400..600``. Default is ``503``.
Dean-Coakley marked this conversation as resolved.
Show resolved Hide resolved
- ``string``
- No*
```

> For each policy referenced in a VirtualServer and/or its VirtualServerRoutes, the Ingress Controller will generate a singe rate limiting zone defined by the [`limit_req_zone`](http://nginx.org/en/docs/http/ngx_http_limit_req_module.html#limit_req_zone) directive. If two VirtualServer resources reference the same policy, the Ingress Controller will generate two different rate limiting zones, one zone per VirtualServer.
Dean-Coakley marked this conversation as resolved.
Show resolved Hide resolved

### RateLimit Merging Behavior
A VirtualServer/VirtualServerRoute can reference multiple rate limit policies. For example, here we reference two policies:
```yaml
policies:
- name: rate-limit-policy-one
- name: rate-limit-policy-two
```

When you reference more than one rate limit policy, the Ingress Controller will configure NGINX to use all referenced rate limits. However, all referenced policy will use the `dryRun`, `logLevel` and `rejectCode` parameters from the first reference (`rate-limit-policy-one` in the example above).
Dean-Coakley marked this conversation as resolved.
Show resolved Hide resolved

## Using Policy

You can use the usual `kubectl` commands to work with Policy resources, just as with built-in Kubernetes resources.
Expand Down
61 changes: 61 additions & 0 deletions examples-of-custom-resources/rate-limit/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Rate Limit

In this example, we deploy a web application, configure load balancing for it via a VirtualServer, and apply a rate limit policy.

## 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:
```
$ IC_IP=XXX.YYY.ZZZ.III
```
1. Save the HTTP port of the Ingress Controller into a shell variable:
```
$ IC_HTTP_PORT=<port number>
```

## Step 1 - Deploy a Web Application

Create the application deployment and service:
```
$ kubectl apply -f webapp.yaml
```

## Step 2 - Deploy the Rate Limit Policy

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

Create the policy:
```
$ kubectl apply -f rate-limit.yaml
```

## Step 3 - Configure Load Balancing

Create a VirtualServer resource for the web application:
```
$ kubectl apply -f virtual-server.yaml
```

Note that the VirtualServer references the policy `rate-limit-policy` created in Step 2.

## Step 4 - Test the Configuration

Let's test the configuration. If you access the application more than one request per second, NGINX will start rejecting your requests:
Dean-Coakley marked this conversation as resolved.
Show resolved Hide resolved
```
$ curl --resolve auth.example.com:$IC_HTTP_PORT:$IC_IP http://auth.example.com:$IC_HTTP_PORT/
Dean-Coakley marked this conversation as resolved.
Show resolved Hide resolved
Server address: 10.8.1.19:8080
Server name: auth-dc88fc766-zr7f8
. . .

$ curl --resolve auth.example.com:$IC_HTTP_PORT:$IC_IP http://auth.example.com:$IC_HTTP_PORT/
<html>
<head><title>503 Service Temporarily Unavailable</title></head>
<body>
<center><h1>503 Service Temporarily Unavailable</h1></center>
<hr><center>nginx/1.19.1</center>
</body>
</html>
```

> Note: The command result is truncated for the clarity of the example.
9 changes: 9 additions & 0 deletions examples-of-custom-resources/rate-limit/rate-limit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
apiVersion: k8s.nginx.org/v1alpha1
kind: Policy
metadata:
name: rate-limit-policy
spec:
rateLimit:
rate: 1r/s
key: ${binary_remote_addr}
zoneSize: 10M
16 changes: 16 additions & 0 deletions examples-of-custom-resources/rate-limit/virtual-server.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
name: webapp
spec:
host: webapp.example.com
policies:
- name: rate-limit-policy
upstreams:
- name: webapp
service: webapp-svc
port: 80
routes:
- path: /
action:
pass: webapp
32 changes: 32 additions & 0 deletions examples-of-custom-resources/rate-limit/webapp.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp
spec:
replicas: 1
selector:
matchLabels:
app: webapp
template:
metadata:
labels:
app: webapp
spec:
containers:
- name: webapp
image: nginxdemos/nginx-hello:plain-text
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: webapp-svc
spec:
ports:
- port: 80
targetPort: 8080
protocol: TCP
name: http
selector:
app: webapp
42 changes: 42 additions & 0 deletions internal/configs/version2/http.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package version2

import "fmt"

// UpstreamLabels describes the Prometheus labels for an NGINX upstream.
type UpstreamLabels struct {
Service string
Expand All @@ -15,6 +17,7 @@ type VirtualServerConfig struct {
SplitClients []SplitClient
Maps []Map
StatusMatches []StatusMatch
LimitReqZones []LimitReqZone
HTTPSnippets []string
SpiffeCerts bool
}
Expand Down Expand Up @@ -61,6 +64,8 @@ type Server struct {
TLSPassthrough bool
Allow []string
Deny []string
LimitReqOptions LimitReqOptions
LimitReqs []LimitReq
PoliciesErrorReturn *Return
}

Expand Down Expand Up @@ -105,6 +110,8 @@ type Location struct {
Allow []string
Deny []string
PoliciesErrorReturn *Return
LimitReqOptions LimitReqOptions
LimitReqs []LimitReq
}

// ReturnLocation defines a location for returning a fixed response.
Expand Down Expand Up @@ -224,3 +231,38 @@ type Queue struct {
Size int
Timeout string
}

// LimitReqZone defines a rate limit shared memory zone.
type LimitReqZone struct {
Key string
ZoneName string
ZoneSize string
Rate string
}

func (rlz LimitReqZone) String() string {
return fmt.Sprintf("{Key %q, ZoneName %q, ZoneSize %v, Rate %q}", rlz.Key, rlz.ZoneName, rlz.ZoneSize, rlz.Rate)
}

// LimitReq defines a rate limit.
type LimitReq struct {
ZoneName string
Burst int
NoDelay bool
Delay int
}

func (rl LimitReq) String() string {
return fmt.Sprintf("{ZoneName %q, Burst %q, NoDelay %v, Delay %q}", rl.ZoneName, rl.Burst, rl.NoDelay, rl.Delay)
}

// LimitReqOptions defines rate limit options.
type LimitReqOptions struct {
DryRun bool
LogLevel string
RejectCode int
}

func (rl LimitReqOptions) String() string {
return fmt.Sprintf("{DryRun %v, LogLevel %q, RejectCode %q}", rl.DryRun, rl.LogLevel, rl.RejectCode)
}
Loading