使用 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;
详情请看下面的文章
我建议尝试以下方法:
(不确定你的后端是不是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-IP
header。也可以放在X-Forwarded-For
header或 X-Real-Ip
或 True-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.33
和 CF-Connecting-IP: 100.11.22.33
。
- 根据已设置
X-Forwarded-For: dummyvalue
header 的请求 => CloudFlare 在请求中设置 X-Forwarded-For: dummyvalue,100.11.22.33
和 CF-Connecting-IP: 100.11.22.33
。
如您所见,客户端为每个请求放置一个随机值并完全规避基于 X-Forwaded-For
header 的任何速率限制是微不足道的。
我基于此博客实施了一个非常简单但非常有效的速率限制 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;
详情请看下面的文章
我建议尝试以下方法:
(不确定你的后端是不是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-IP
header。也可以放在X-Forwarded-For
header或 X-Real-Ip
或 True-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.33
和CF-Connecting-IP: 100.11.22.33
。 - 根据已设置
X-Forwarded-For: dummyvalue
header 的请求 => CloudFlare 在请求中设置X-Forwarded-For: dummyvalue,100.11.22.33
和CF-Connecting-IP: 100.11.22.33
。
如您所见,客户端为每个请求放置一个随机值并完全规避基于 X-Forwaded-For
header 的任何速率限制是微不足道的。