add per minute rate limiting
This commit is contained in:
parent
d5f11007bb
commit
40a9a54082
5 changed files with 50 additions and 11 deletions
|
@ -193,13 +193,15 @@ Please check the [rewrite](/examples/rewrite/nginx/README.md) example.
|
|||
|
||||
### Rate limiting
|
||||
|
||||
The annotations `ingress.kubernetes.io/limit-connections` and `ingress.kubernetes.io/limit-rps` define a limit on the connections that can be opened by a single client IP address. This can be used to mitigate [DDoS Attacks](https://www.nginx.com/blog/mitigating-ddos-attacks-with-nginx-and-nginx-plus).
|
||||
The annotations `ingress.kubernetes.io/limit-connections`, `ingress.kubernetes.io/limit-rps`, and `ingress.kubernetes.io/limit-rpm` define a limit on the connections that can be opened by a single client IP address. This can be used to mitigate [DDoS Attacks](https://www.nginx.com/blog/mitigating-ddos-attacks-with-nginx-and-nginx-plus).
|
||||
|
||||
`ingress.kubernetes.io/limit-connections`: number of concurrent connections allowed from a single IP address.
|
||||
|
||||
`ingress.kubernetes.io/limit-rps`: number of connections that may be accepted from a given IP each second.
|
||||
|
||||
If you specify both annotations in a single Ingress rule, `limit-rps` takes precedence.
|
||||
`ingress.kubernetes.io/limit-rpm`: number of connections that may be accepted from a given IP each minute.
|
||||
|
||||
If you specify multiple annotations in a single Ingress rule, `limit-rpm`, and then `limit-rps` takes precedence.
|
||||
|
||||
|
||||
### SSL Passthrough
|
||||
|
|
|
@ -349,6 +349,17 @@ func buildRateLimitZones(variable string, input interface{}) []string {
|
|||
}
|
||||
}
|
||||
|
||||
if loc.RateLimit.RPM.Limit > 0 {
|
||||
zone := fmt.Sprintf("limit_req_zone %v zone=%v:%vm rate=%vr/m;",
|
||||
variable,
|
||||
loc.RateLimit.RPM.Name,
|
||||
loc.RateLimit.RPM.SharedSize,
|
||||
loc.RateLimit.RPM.Limit)
|
||||
if !zones.Has(zone) {
|
||||
zones.Insert(zone)
|
||||
}
|
||||
}
|
||||
|
||||
if loc.RateLimit.RPS.Limit > 0 {
|
||||
zone := fmt.Sprintf("limit_req_zone %v zone=%v:%vm rate=%vr/s;",
|
||||
variable,
|
||||
|
@ -366,7 +377,7 @@ func buildRateLimitZones(variable string, input interface{}) []string {
|
|||
}
|
||||
|
||||
// buildRateLimit produces an array of limit_req to be used inside the Path of
|
||||
// Ingress rules. The order: connections by IP first and RPS next.
|
||||
// Ingress rules. The order: connections by IP first, then RPS, and RPM last.
|
||||
func buildRateLimit(input interface{}) []string {
|
||||
limits := []string{}
|
||||
|
||||
|
@ -387,6 +398,12 @@ func buildRateLimit(input interface{}) []string {
|
|||
limits = append(limits, limit)
|
||||
}
|
||||
|
||||
if loc.RateLimit.RPM.Limit > 0 {
|
||||
limit := fmt.Sprintf("limit_req zone=%v burst=%v nodelay;",
|
||||
loc.RateLimit.RPM.Name, loc.RateLimit.RPM.Burst)
|
||||
limits = append(limits, limit)
|
||||
}
|
||||
|
||||
return limits
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ import (
|
|||
const (
|
||||
limitIP = "ingress.kubernetes.io/limit-connections"
|
||||
limitRPS = "ingress.kubernetes.io/limit-rps"
|
||||
limitRPM = "ingress.kubernetes.io/limit-rpm"
|
||||
|
||||
// allow 5 times the specified limit as burst
|
||||
defBurst = 5
|
||||
|
@ -45,6 +46,8 @@ type RateLimit struct {
|
|||
Connections Zone `json:"connections"`
|
||||
// RPS indicates a limit with the number of connections per second
|
||||
RPS Zone `json:"rps"`
|
||||
|
||||
RPM Zone `json:"rpm"`
|
||||
}
|
||||
|
||||
// Equal tests for equality between two RateLimit types
|
||||
|
@ -58,6 +61,9 @@ func (rt1 *RateLimit) Equal(rt2 *RateLimit) bool {
|
|||
if !(&rt1.Connections).Equal(&rt2.Connections) {
|
||||
return false
|
||||
}
|
||||
if !(&rt1.RPM).Equal(&rt2.RPM) {
|
||||
return false
|
||||
}
|
||||
if !(&rt1.RPS).Equal(&rt2.RPS) {
|
||||
return false
|
||||
}
|
||||
|
@ -111,13 +117,15 @@ func NewParser() parser.IngressAnnotation {
|
|||
// rule used to rewrite the defined paths
|
||||
func (a ratelimit) Parse(ing *extensions.Ingress) (interface{}, error) {
|
||||
|
||||
rpm, _ := parser.GetIntAnnotation(limitRPM, ing)
|
||||
rps, _ := parser.GetIntAnnotation(limitRPS, ing)
|
||||
conn, _ := parser.GetIntAnnotation(limitIP, ing)
|
||||
|
||||
if rps == 0 && conn == 0 {
|
||||
if rpm == 0 && rps == 0 && conn == 0 {
|
||||
return &RateLimit{
|
||||
Connections: Zone{},
|
||||
RPS: Zone{},
|
||||
RPM: Zone{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -136,5 +144,11 @@ func (a ratelimit) Parse(ing *extensions.Ingress) (interface{}, error) {
|
|||
Burst: rps * defBurst,
|
||||
SharedSize: defSharedSize,
|
||||
},
|
||||
RPM: Zone{
|
||||
Name: fmt.Sprintf("%v_rpm", zoneName),
|
||||
Limit: rpm,
|
||||
Burst: rpm * defBurst,
|
||||
SharedSize: defSharedSize,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -75,6 +75,7 @@ func TestBadRateLimiting(t *testing.T) {
|
|||
data := map[string]string{}
|
||||
data[limitIP] = "0"
|
||||
data[limitRPS] = "0"
|
||||
data[limitRPM] = "0"
|
||||
ing.SetAnnotations(data)
|
||||
|
||||
_, err := NewParser().Parse(ing)
|
||||
|
@ -85,6 +86,7 @@ func TestBadRateLimiting(t *testing.T) {
|
|||
data = map[string]string{}
|
||||
data[limitIP] = "5"
|
||||
data[limitRPS] = "100"
|
||||
data[limitRPM] = "10"
|
||||
ing.SetAnnotations(data)
|
||||
|
||||
i, err := NewParser().Parse(ing)
|
||||
|
@ -101,4 +103,7 @@ func TestBadRateLimiting(t *testing.T) {
|
|||
if rateLimit.RPS.Limit != 100 {
|
||||
t.Errorf("expected 100 in limit by rps but %v was returend", rateLimit.RPS)
|
||||
}
|
||||
if rateLimit.RPM.Limit != 10 {
|
||||
t.Errorf("expected 10 in limit by rpm but %v was returend", rateLimit.RPM)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,13 +49,14 @@ Key:
|
|||
|
||||
| Name | Meaning
|
||||
| --- | ---
|
||||
| `configuration-snippet` | Arbitrary text to put in the generated configuration file. (nginx)
|
||||
| `enable-cors` | Enable CORS headers in response. (nginx)
|
||||
| `limit-connections` | Limit concurrent connections per IP address[1]. (nginx)
|
||||
| `limit-rps` | Limit requests per second per IP address[1]. (nginx)
|
||||
| `affinity` | Specify a method to stick clients to origins across requests. Found in `nginx`, where the only supported value is `cookie`. (nginx)
|
||||
| `session-cookie-name` | When `affinity` is set to `cookie`, the name of the cookie to use. (nginx)
|
||||
| `session-cookie-hash` | When `affinity` is set to `cookie`, the hash algorithm used: `md5`, `sha`, `index`. (nginx)
|
||||
| `configuration-snippet` | Arbitrary text to put in the generated configuration file. (nginx)
|
||||
| `enable-cors` | Enable CORS headers in response. (nginx)
|
||||
| `limit-connections` | Limit concurrent connections per IP address[1]. (nginx)
|
||||
| `limit-rps` | Limit requests per second per IP address[1]. (nginx)
|
||||
| `limit-rpm` | Limit requests per minute per IP address. (nginx)
|
||||
| `affinity` | Specify a method to stick clients to origins across requests. Found in `nginx`, where the only supported value is `cookie`. (nginx)
|
||||
| `session-cookie-name` | When `affinity` is set to `cookie`, the name of the cookie to use. (nginx)
|
||||
| `session-cookie-hash` | When `affinity` is set to `cookie`, the hash algorithm used: `md5`, `sha`, `index`. (nginx)
|
||||
| `proxy-body-size` | Maximum request body size. (nginx, haproxy)
|
||||
| `follow-redirects` | Follow HTTP redirects in the response and deliver the redirect target to the client. (trafficserver)
|
||||
|
||||
|
|
Loading…
Reference in a new issue