Rate Limiting with the HAProxy Kubernetes Ingress Controller

Add IP-by-IP rate limiting to the HAProxy Kubernetes Ingress Controller. 

DDoS (distributed denial of service) events occur when an attacker or group of attackers flood your application or API with disruptive traffic, hoping to exhaust its resources and prevent it from functioning properly. Bots and scrapers, too, can misbehave, making far more requests than is reasonable.

In this blog, we’ve covered several ways that you can use overall rate limiting to mitigate the effects of these kinds of events, but the HAProxy Kubernetes Ingress Controller offers even more fine-grained control to fend off DDoS attacks using several annotations that can help you build a powerful first line of defense on an IP-by-IP basis.

Rate Limit Requests

The most important annotation to understand is rate-limit-requests. This setting is an integer that defines the maximum number of requests that will be accepted from a source IP address during the rate-limit-period, which defaults to one second.

This is accomplished with the following annotation:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: web-ingress
namespace: default
annotations:
  haproxy.org/rate-limit-requests: 10

By adding this annotation to your config, any single IP address is limited to 10 requests per second, after which their requests would be denied with a 403 status code.

Rate Limit Period

Adding an annotation for rate-limit-period lets you specify a custom time period for your rate limits. The default is 1 second, which you could explicitly set with a string annotation of 1s. A period of one minute is set using the string 1m.

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: web-ingress
namespace: default
annotations:
  haproxy.org/rate-limit-requests: 10
  haproxy.org/rate-limit-period: "1s"

Behind the scenes, a stick-table is created to track the rate of requests. The stick table name is composed of the string Ratelimit- plus the rate-limit-period expressed in milliseconds. For example, if the rate-limit-period is set to two seconds, the name of the table will be Ratelimit-2000.

Custom Status Codes

To return a status code other than 403, add an annotation for rate-limit-status-code.

This sets the status code to return when rate limiting has been triggered. It’s a standard HTTP status code and defaults to HTTP 403. Here, we set it to the HTTP 429 Too Many Requests response status code:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: web-ingress
namespace: default
annotations:
  haproxy.org/rate-limit-requests: 10
  haproxy.org/rate-limit-period: "1s"
  haproxy.org/rate-limit-status-code: "429"

Rate Limit Size

One final annotation we’ll take a look at is rate-limit-size, which is the number of IP address entries in the stick table. By default, the rate limit stick table is limited to 100,000 entries, meaning that 100,000 individual IP addresses are being tracked at any time. When this value is exceeded, the oldest entries in the table are removed, and new addresses are added.

The rate-limit-size annotation is expressed as an integer.

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: web-ingress
namespace: default
annotations:
  haproxy.org/rate-limit-requests: 10
  haproxy.org/rate-limit-period: "1s"
  haproxy.org/rate-limit-status-code: "429"
  haproxy.org/rate-limit-size: 1000000

Conclusion

In this blog post, you learned that you can fine tune your HAProxy Kubernetes Ingress Controller’s configuration to leverage some powerful annotations to protect your services and APIs.

Want to know when more content like this is published? Subscribe to our blog or follow us on Twitter. You can also join the conversation on Slack.

Subscribe to our blog. Get the latest release updates, tutorials, and deep-dives from HAProxy experts.