开篇语:开涛新作《亿级流量网站架构核心技术》出版计划公布以来,博文视点遭受到一波又一波读者询问面世时间的 DDos 攻击。面对亿级流量的热情,感激之余,我们也很庆幸——这部作品质量的确过硬,不会辜负拥趸厚望,更有开涛的高度负责和体贴周到加持,让她绝对物超所值、长久流芳。下面,看一篇来自这位技术暖男的售前支持。
——本书策划编辑 侠少
本文作者张开涛。为保障《亿级流量网站架构核心技术》一书内容的连续性,有些需要读者了解的内容,或者书的补充和引申内容,会通过二维码嵌入的方式引导读者阅读学习。大家可以关注作者公众号“开涛的博客”,并从菜单栏“我的新书”中查阅相关内容。
本文是「4.4 接入层限流」节中的「按照 IP 限制并发连接数配置示例」部分需要了解的内容。
当我们访问互联网上的服务时,大多数时,客户端并不是直接访问到服务端的,而是客户端首先请求到反向代理,反向代理再转发到服务端实现服务访问,通过反向代理实现路由/负载均衡等策略。这样一来在服务端拿到的客户端 IP 将是反向代理 IP,而不是真实客户端 IP,因此需要一种办法来获取到真实客户端 IP。
如下图所示,客户端通过 Nginx Proxy1 和 Nginx Proxy2 两层反向代理才访问到具体服务 Nginx Backend(或如 Tomcat 服务)。那 Nginx Backend 如何才能拿到真实客户端 IP 呢?
接下来我们来看看如何才能获取到客户端真实 IP。
场景 1
场景 1 是很简单的场景,Nginx Proxy 直接把请求往后转发,没有做任何处理。
Nginx Proxy
192.168.107.107 nginx.conf
location /test {
proxy_pass http://192.168.107.112:8080;
}
192.168.107.112 nginx.conf
location /test {
proxy_pass http://192.168.107.114:8080;
}
Nginx Proxy 就是简单的把请求往后转发。
Nginx Backend
192.168.107.114 nginx.conf
location /test {
default_type text/html;
charset gbk;
echo “$remote_addr || $http_x_forwarded_for”;
}
Nginx Backend 输出客户端 IP($remote_addr)和X-Forwarded-For请求头($http_x_forwarded_for),当访问服务时输出结果如下所示:
192.168.107.112 ||
分析
1.$remote_addr 代表客户端 IP,当前配置的输出结果为最后一个代理服务器的 IP,并不是真实客户端 IP;
2.在没有特殊配置情况下,X-Forwarded-For请求头不会自动添加到请求头中,即 Nginx Backend 的$http_x_forwarded_for 输出为空。
场景 2
场景 2 通过添加 X-Real-IP和X-Forwarded-For捕获客户端真实 IP。
Nginx Proxy
192.168.107.107 nginx.conf
location /test {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://192.168.107.112:8080;
}
192.168.107.112 nginx.conf
location /test {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://192.168.107.114:8080;
}
Nginx Backend
192.168.107.114 nginx.conf
location /test {
default_type text/html;
charset gbk;
echo “$remote_addr ||$http_x_real_ip ||$http_x_forwarded_for”;
}
当访问服务时,输出结果为:
192.168.107.112 || 192.168.162.16 || 192.168.162.16, 192.168.107.107
分析
1.在离用户最近的反向代理 NginxProxy 1,通过“proxy_set_header X-Real-IP $remote_addr”把真实客户端 IP 写入到请求头 X-Real-IP,在 NginxBackend 输出$http_x_real_ip 获取到了真实客户端 IP;而 Nginx Backend 的“$remote_addr”输出为最后一个反向代理的 IP;
2.“proxy_set_headerX-Forwarded-For $proxy_add_x_forwarded_for”的是把请求头中的 X-Forwarded-For 与$remote_addr 用逗号合起来,如果请求头中没有 X-Forwarded-For 则$proxy_add_x_forwarded_for 为$remote_addr。
X-Forwarded-For 代表了客户端 IP,反向代理如 Nginx 通过$proxy_add_x_forwarded_for 添加此项,X-Forwarded-For 的格式为 X-Forwarded-For:real client ip, proxy ip 1, proxy ip N,每经过一个反向代理就在请求头 X-Forwarded-For 后追加反向代理 IP。
到此我们可以使用请求头 X-Real-IP和 X-Forwarded-For 来获取客户端 IP 及客户端到服务端经过的反向代理 IP 了。这种方式还是很麻烦,$remote_addr 并不是真实客户端 IP。
场景 3
为了更方便的获取真实客户端 IP,可以使用 nginx http_realip_module 模块解决,在安装 nginx 时通过–with-http_realip_module 安装该模块。NginxProxy 配置和场景 2 一样。
Nginx Backend
192.168.107.114 nginx.conf
real_ip_header X-Forwarded-For;
set_real_ip_from 192.168.0.0/16;
real_ip_recursive on;
location /test {
default_type text/html;
charset gbk;
echo “$remote_addr || $http_x_real_ip ||$http_x_forwarded_for”;
}
当访问服务时,输出结果为:
192.168.162.16 || 192.168.162.16 || 192.168.162.16, 192.168.107.107
分析
1.X-Real-IP和 X-Forwarded-For 和场景 2 一样;
2.使用 realip 模块后,$remote_addr 输出结果为真实客户端 IP,可以使用$realip_remote_addr 获取最后一个反向代理的 IP;
3.real_ip_headerX-Forwarded-For:告知 Nginx 真实客户端 IP 从哪个请求头获取,如 X-Forwarded-For;
4.set_real_ip_from192.168.0.0/16:告知 Nginx 哪些是反向代理 IP,即排除后剩下的就是真实客户端 IP,支持配置具体 IP 地址、CIDR 地址;
5.real_ip_recursive on:是否递归解析,当 real_ip_recursive 配置为 off 时,Nginx 会把 real_ip_header 指定的请求头中的最后一个 IP 作为真实客户端 IP;当 real_ip_recursive 配置为 on 时,Nginx 会递归解析 real_ip_header 指定的请求头,最后一个不匹配 set_real_ip_from 的 IP 作为真实客户端 IP。
如果配置“real_ip_recursive off; ”,则输出结果为:
192.168.107.107 || 192.168.162.16 ||192.168.162.16, 192.168.107.107
总结
1.通过“proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for” 把从真实客户端 IP 和反向代理 IP 通过逗号分隔,添加到请求头中;
2.可以在第一个反向代理上配置“proxy_set_header X-Real-IP $remote_addr” 获取真实客户端 IP;
3.配合 realip 模块从 X-Forwarded-For 也可以获取到真实客户端 IP。
在整个反向代理链条的第一个反向代理可以不配置“proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for”,否则客户端可以伪造 X-Forwarded-For 从而伪造客户端真实 IP,如果服务端使用 X-Forwarded-For 第一个 IP 作为真实客户端 IP,则就出问题了。如果通过配置 X-Real-IP 请求头或者配合 realip 模块也不会出现该问题。如果自己解析 X-Forwarded-For 的话,记得使用 realip 算法解析,而不是取第一个。
当我们进行限流时一定注意限制的是真实客户端 IP,而不是反向代理 IP,我遇到过很多同事在这块出问题的。
作者:博文视点
来源:CSDN
原文:https://blog.csdn.net/broadview2006/article/details/54570943
版权声明:本文为博主原创文章,转载请附上博文链接!