背景
公司多个客户端现场的访问日志,大面积出现了访问者的ip为一个内网IP,最终这个问题交给了我来处理。
寻找原因
找到获取ip的地方,仔细的查看了几个地方的获取方法之后,确认获取方式没有问题,相关代码如下(类似的写法)
/*** 获取用户真实IP地址,不使用request.getRemoteAddr();的原因是有可能用户使用了代理软件方式避免真实IP地址,* 参考文章: http://developer.51cto.com/art/201111/305181.htm** 可是,如果通过了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP值,究竟哪个才是真正的用户端的真实IP呢?* 答案是取X-Forwarded-For中第一个非unknown的有效IP字符串。** 如:X-Forwarded-For:192.168.1.110, 192.168.1.120, 192.168.1.130,* 192.168.1.100** 用户真实IP为: 192.168.1.110** @param request* @return*/public static String getIpAddress(HttpServletRequest request) {String ip = request.getHeader(“x-forwarded-for”);if (ip == null || ip.length() == 0 || “unknown”.equalsIgnoreCase(ip)) {ip = request.getHeader(“Proxy-Client-IP”);}if (ip == null || ip.length() == 0 || “unknown”.equalsIgnoreCase(ip)) {ip = request.getHeader(“WL-Proxy-Client-IP”);}if (ip == null || ip.length() == 0 || “unknown”.equalsIgnoreCase(ip)) {ip = request.getHeader(“HTTP_CLIENT_IP”);}if (ip == null || ip.length() == 0 || “unknown”.equalsIgnoreCase(ip)) {ip = request.getHeader(“HTTP_X_FORWARDED_FOR”);}if (ip == null || ip.length() == 0 || “unknown”.equalsIgnoreCase(ip)) {ip = request.getRemoteAddr();}return ip;}
确认了不是后端的获取问题之后,向上游查找,就到了nginx这端,查看了相关的代理配置,配置信息类似于:
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
确定也没有问题,然后查询是否安装了相关的模块,–with-http_realip_module,通过指令查询:
nginx -V
注意这里试用大写的V,查看配置里面是否包含有–with-http_realip_module。经过确认没有该模块,于是重新下载编译,添加了–with-http_realip_module。
代码类似如下:
[root@bogon nginx-1.6.2]# ./configure –prefix=/usr/local/webserver/nginx –with-http_stub_status_module –with-http_ssl_module –with-pcre=/usr/local/src/pcre-8.35 –with-http_realip_module
重启nginx之后发现,还是存在问题,在日志中发现nginx获取的remote ip为 路由器的ip,为了确认是nginx上游的问题,于是决定通过修改nginx日志数据来打印相关的参数。
具体信息类似如下:
nginx.conf 的http中添加
log_format main ‘$host – $remote_addr – $proxy_add_x_forwarded_for – [$time_local] “$request” ‘
‘$status $upstream_response_time $request_time “$http_referer”‘
‘”$http_user_agent” “$http_x_forwarded_for” $body_bytes_sent ‘;
通过重启后,查询日志,发现都是路由的IP,无法获取到客户端的真实IP。
初步结论
初步怀疑为nginx的前端可能过滤了相关的IP。
明天再跟踪之后,补充后续。。。。
最终结论
经过排查之后,发现是端口转发的问题,具体询问了DLINK厂家(这边一个场景的路由器使用的这个品牌),需要更新到最新的固件,在端口转发存在一个模式选项,分别为模式1、模式2,其中模式1就是会只获取到网关的ip,模式2则为真实用户ip。
最终通过升级路由器固件,修改了选项之后,成功获得了ip.
端口转发
SNAT
Source Network Address Translation 源网络地址转换,路由器接收到请求后,将源地址修改为路由器地址,路由器来做中间的交换,估计默认使用了这个规则,导致获取到的就是路由器ip。
DNAT
Destination Network Address Translation 目的网络地址转换,路由器接收到请求后,将请求的目的地址进行转换后转发,后面修改的模式估计是这个规则,所以能正确识别真实客户ip。