nginx 不能获取到真实ip

背景

公司多个客户端现场的访问日志,大面积出现了访问者的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。
代码类似如下:

[[email protected] 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。

发表评论

电子邮件地址不会被公开。 必填项已用*标注