Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nginx check if Cloudflare forward or direct IP and limit accordingly

I am aware of the headers CF-Connecting-IP, $binary_remote_addr, http_x_forwarded_for

I want to make a setting:

limit_req_zone $http_x_forwarded_for zone=k_request_limit_per_ip:10m rate=400r/s;
limit_conn_zone $http_x_forwarded_for zone=k_connection_limit_per_ip:10m;

But Cloudflare isn't the only place that this machine is going to be accessed, so I want to limit direct access too. Is there a way to write something like:

if(header == `X-Forwarded-For`) {
  limit_req_zone $http_x_forwarded_for zone=k_request_limit_per_ip:10m rate=400r/s;
} else {
  limit_req_zone $binary_remote_addr zone=k_request_limit_per_ip:10m rate=400r/s;
}

Or would something like this work:

  limit_req_zone $http_x_forwarded_for zone=http_zone:10m rate=400r/s;
  limit_req_zone $binary_remote_addr zone=binary_zone:10m rate=400r/s;

An alternative would be to fully allow all Cloudflare IP addresses. And limit the Non-Cloudflare IP addresses.

Good source:

  • NGINX rate limiting doesn't work when using Cloudflare. I can bring down my site with a simple `ab` command
like image 468
Esqarrouth Avatar asked Jan 24 '23 10:01

Esqarrouth


1 Answers

You can make use of the ngx_real_ip_module. http://nginx.org/en/docs/http/ngx_http_realip_module.html

With this you can specify the Cloudflare CIDRs to be allowed to override the binary_remote_addr with the value from X-Forwarded-For. Make sure you have this check in place. Config could look like:

set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 104.16.0.0/12;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 131.0.72.0/22;
set_real_ip_from 2400:cb00::/32;
set_real_ip_from 2606:4700::/32;
set_real_ip_from 2803:f800::/32;
set_real_ip_from 2405:b500::/32;
set_real_ip_from 2405:8100::/32;
set_real_ip_from 2a06:98c0::/29;
set_real_ip_from 2c0f:f248::/32;
real_ip_header X-Forwarded-For;
real_ip_recursive off;

Cloudflare IPs can change, this command will automatically update them into cloudflare_ips.conf:

cat /dev/null > cloudflare_ips.conf && curl -s https://www.cloudflare.com/ips-v4 | while read ip; do echo "set_real_ip_from $ip;" >> cloudflare_ips.conf; done && curl -s https://www.cloudflare.com/ips-v6 | while read ip; do echo "set_real_ip_from $ip;" >> cloudflare_ips.conf; done && printf "real_ip_header X-Forwarded-For;\nreal_ip_recursive off;\n" >> cloudflare_ips.conf

Your rate limit config can use the binary_remote_addr variable. If the client comes via cloudflare, CFs IP will be replaced with the IP from the Header. If the client connects directly the client IP will be used. If a client tries to send a X-Forwarded-For Header that one will not be accepted as the clients IP does not match any CIDR from your cloudflare_ips.conf file.

like image 108
Timo Stark Avatar answered Feb 01 '23 17:02

Timo Stark