Published: Sep 23, 2022

Nginx - Rate Limiting

Introduction

Nginx is a widely used and versatile piece of open source software, best known for it’s high performance when handling many connections. It can be used as a webserver, reverse proxy, load balancer, SSL/TLS terminator and more. At its core Nginx is a collection of modules. To list currently installed modules use:

sudo nginx -V 2>&1 | tr -- - '\n' | grep  _module

This command does not list the modules already included in the source code like ngx_http_limit_req_module which can be found in the /src/http/modules directory.

Goal

Use ngx_http_limit_req_module in combination with fail2ban to add rate limiting to a webserver and temporarily ban users exceeding the limit.

Requirements

  • Nginx
  • fail2ban

Configuring Nginx

The official documentation for ngx_http_limit_req_module contains lots of useful information on configuring the module.

http {
    limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
    ...
    server {
        ...
        location /search/ {
            limit_req zone=one burst=5;
        }

This configuration allows not more than 1 request per second with bursts up to 5 requests and keeps track of the requests in zone “one” using up to 10MB of memory. For most websites those limits will not be sufficient and need to be tweaked. But for now those limits are fine since it will be easy to exceed them and test if fail2ban actually behaves as expected.

Configuring fail2ban

Fail2ban includes the nginx-limit-req jail which reads nginx logs and searches for any entries indicating that a client sent too many requests.

[nginx-limit-req]
port    = http,https
logpath = %(nginx_error_log)s

Limit testing

To test those limits you can manually trigger requests using your browser or use a tool like siege.

siege -d 1 -c 1 http://url

-d controls the delay in seconds, while -c sets the concurrency. Setting both to 1 will result in about 1 request per second. Once the limit is exceeded siege should start reporting errors like:

[error\] socket: unable to connect sock.c:282: Connection refused

To list/unban clients in fail2ban I wrote some short bash scripts.

#!/bin/bash
sudo fail2ban-client banned | tr ' " | jq

if ! [ -z $1 ]
then
        echo ""
        sudo fail2ban-client status $1
        echo ""
        sudo fail2ban-client get $1 banip --with-time
fi
#!/bin/bash
sudo fail2ban-client set nginx-limit-req unbanip $1