使用 Cloudflare 时,NGINX 速率限制不起作用。我可以用一个简单的 `ab` 命令关闭我的网站

NGINX rate limiting doesn't work when using Cloudflare. I can bring down my site with a simple `ab` command

我基于此博客实施了一个非常简单但非常有效的速率限制 post:https://www.nginx.com/blog/rate-limiting-nginx/

基本上:

limit_req_zone $binary_remote_addr zone=ip:10m rate=10r/s;

limit_req zone=ip burst=20 nodelay;

效果很好。然而,最近我尝试了 Cloudflare,这不再保护我了。我可以通过以下简单命令自行关闭网站:

ab -k -c 1000 -n 10000 site.com/

发生什么事了?

你可以在你的nginx配置中添加以下内容

real_ip_header CF-Connecting-IP;

详情请看下面的文章

https://support.cloudflare.com/hc/en-us/articles/200170986-How-does-Cloudflare-handle-HTTP-Request-headers-

我建议尝试以下方法:

(不确定你的后端是不是PHP,但你可以重新调整)

# Conection Limit, taking the best from:
# http://serverfault.com/questions/177461/how-to-rate-limit-in-nginx-but-including-excluding-certain-ip-addresses
# http://gadelkareem.com/2015/01/18/limiting-connections-requests-wordpress-nginx/

geo $whitelist {
   default 0;
   # CIDR in the list below are not limited ( 1 )
   127.0.0.1/32 1;

}

map $whitelist $limit {
    0     $binary_remote_addr;
    1     "";
}

# The directives below limit concurrent connections from a 
# non-whitelisted IP address to five

limit_conn_zone      $limit    zone=conlimit:30m;
limit_conn_zone      $limit    zone=conlimit_php:30m;

limit_conn           conlimit 40;
limit_conn_log_level warn;   # logging level when threshold exceeded
limit_conn_status    503;    # the error code to return

# Limit Req Non - PHP 
limit_req_zone       $limit   zone=reqlimit:30m  rate=5r/s; 
limit_req            zone=reqlimit burst=10;

# Limit Req PHP
limit_req_zone       $limit   zone=reqlimit_php:30m  rate=1r/s; 

limit_req_log_level  warn;
limit_req_status     503;

# Location VirtualHost

limit_req            zone=reqlimit_php burst=4;
limit_conn           conlimit_php 10;

对于 CF-Connecting-IP,您可以通过以下方式将 Cloudflare IP 列入白名单:

#!/bin/bash
cloudflare_real_ip_conf='/usr/local/nginx/conf/cloudflare_ip.conf'

echo "#Cloudflare" > ${cloudflare_real_ip_conf};
for i in `curl -sk https://www.cloudflare.com/ips-v4`; do
        echo "set_real_ip_from $i;" >> ${cloudflare_real_ip_conf};
done
for i in `curl -sk https://www.cloudflare.com/ips-v6`; do
        echo "set_real_ip_from $i;" >> ${cloudflare_real_ip_conf};
done

echo "" >> ${cloudflare_real_ip_conf};
echo "# use any of the following two" >> ${cloudflare_real_ip_conf};
echo "real_ip_header CF-Connecting-IP;" >> ${cloudflare_real_ip_conf};
echo "#real_ip_header X-Forwarded-For;" >> ${cloudflare_real_ip_conf};

这将产生 CF 白名单(当前):

#Cloudflare
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;

# use any of the following two
real_ip_header CF-Connecting-IP;
#real_ip_header X-Forwarded-For;

ab -k -c 1000 -n 10000 site.com/ 是 运行 1000 个并行请求,直到总共完成 10000 个请求。

这太残忍了。很可能客户端和服务器都没有调整为在几秒钟内处理数千个连接。

调整nginx配置,做一个温和的测试ab -k -c 5 -n 500 site.com/

limit_req_zone $http_cf_connecting_ip zone=ip:10m rate=3r/s;
limit_req zone=ip;

limit_conn_status 429;
limit_req_status 429;

429 个请求太多

当请求因速率限制而被拒绝时,这会将 nginx 配置为 return 标准状态代码 429 Too Many Requests

nginx return默认情况下有一个 503 错误(错误的默认值),这意味着应用程序正在失败,但它并没有失败,它是速率受限的。适当配置状态代码以区分服务器错误和速率限制很重要。

Cloudflare 和客户端 IP

在cloudflare后面时,nginx看不到客户端的IP,而是cloudflare服务器的IP。有人可能认为它打破了 IP 的速率限制,但它并没有,嗯,只是一点点。

当使用 ab 在本地进行测试时,您的测试计算机仅解析少数 cloudflare 服务器,并且 ab 可能仅使用第一个 IP。所以不,没有很多客户端 IP,速率限制应该可以正常工作。

生产时,会有不同的客户端通过不同的cloudflare服务器访问。尽管如此,地理区域中的 cloudflare 服务器并不多,客户端很可能会解析到相同的 cloudflare 服务器。所以会有一堆不同的 IP 在某种程度上打破了速率限制,但可能不会那么多。

> nslookup mycloudflaresite.com

Name:    mycloudflaresite.com
Addresses:  104.28.14.125
            104.28.15.125
            2606:4700:3037::681c:e7d
            2606:4700:3036::681c:f7d

Cloudflare将原始客户端IP放在CF-Connecting-IPheader。也可以放在X-Forwarded-Forheader或 X-Real-IpTrue-Client-IP 取决于设置和请求。参见 https://support.cloudflare.com/hc/en-us/articles/200170986-How-does-Cloudflare-handle-HTTP-Request-headers-

因此,上述配置使用 CF-Connecting-IP header 通过客户端 IP 进行速率限制。 nginx 变量 $binary_remote_addr 将是 cloudflare 服务器 IP。

不要使用X-Forwarded-For来限制速率

X-Forwarded-For header 可以由客户端控制。它不应该用于速率限制,因为它很容易规避。

IP 为 100.11.22.33 的客户端示例:

  • 在没有 X-Forwarded-For 的请求中 header => Cloudflare 在请求中设置 X-Forwarded-For: 100.11.22.33CF-Connecting-IP: 100.11.22.33
  • 根据已设置 X-Forwarded-For: dummyvalue header 的请求 => CloudFlare 在请求中设置 X-Forwarded-For: dummyvalue,100.11.22.33CF-Connecting-IP: 100.11.22.33

如您所见,客户端为每个请求放置一个随机值并完全规避基于 X-Forwaded-For header 的任何速率限制是微不足道的。