转载

【问题排查】tomcat无法获取客户端IP

问题描述

58站点在使用HTTP协议访问时,后端tomcat使用X-Forwarded-For这个参数来获取客户端真实IP。 但改用HTTPS协议后,后端tomcat无法通过X-Forwarded-For来获取客户端IP,X-Forwarded-For的值为null 。

问题排查

客户端和后端服务中间是运维统一接入的Nginx代理层,而HTTPS只到Nginx层即卸载证书,Nginx向后端转发请求使用HTTP协议,如下图所示

【问题排查】tomcat无法获取客户端IP

客户端请求无论是HTTP还是HTTPS协议都会以相同的方式把X-Forwarded-For的值传递给后端(HTTP和HTTPS的Nginx配置相同,只是多加了证书相关配置),实际测试发现,部分后端获取X-Forwarded-For值为null。

经过对比发现,获取不到X-Forwarded-For是因为tomcat的server.xml配置文件中多了以下配置。

Tomcat-server.xml配置:

在Engine段添加:

<span style="">&lt;<span>Valve</span> <span>className</span>=<span>&quot;org.apache.catalina.valves.RemoteIpValve&quot;</span> </span>

<span><span>remoteIpHeader</span>=<span>&quot;X-Forwarded-For&quot;</span> </span>

<span><span>protocolHeader</span>=<span>&quot;X-Forwarded-Proto&quot;</span> </span>

<span><span>protocolHeaderHttpsValue</span>=<span>&quot;https&quot;</span></span>

<span>/&gt;</span>

该配置的作用是为了将用户真实的访问协议(HTTPS)通过X-Forwarded-Proto传给给tomcat,以保证后端处理完用户请求返回给用户的协议也是HTTPS,而不是获取Nginx访问的HTTP协议。

为配合此配置的传递功能,Nginx增加如下配置:

location 段:

<span><span>proxy_set_header</span> HTTPS-Tag <span>&quot;HTTPS&quot;</span>; <span>#设置HTTPS_TAG标识,方便程序认读</span></span>

<span><span>proxy_set_header</span> X-Forwarded-Proto <span>$scheme</span>; <span>#设置协议头传递变量</span></span>

在58站点进行HTTPS改造项目中,为了让后端都可以正常获取用户协议,因此在默认模板中统一添加了此配置。 在上线时,如果使用的是非全量包,上线过程会自动从模板中拉取server.xml配置文件。 所以导致了这部分站点获取X-Forwarded-Proto值时出现了问题。

问题原因

参考tomcat官方解释:

https://tomcat.apache.org/tomcat-7.0-doc/api/org/apache/catalina/valves/RemoteIpValve.html

【问题排查】tomcat无法获取客户端IP

例如:

客户端IP: 140.211.11.130

nginx内网IP: 192.168.0.10

此配置将X-Forwarded-For里的值排除内网或者可信任IP后,把最原始的请求IP赋值给了tomcat的remoteaddr变量,所以导致X-Forwarded-For的值为null,可以理解为tomcat更智能的为我们把用户IP转换到了remoteaddr变量了,而不再是直接访问tomcat的Nginx IP。

【问题排查】tomcat无法获取客户端IP

解决方案

1. 用户可以根据服务自身需求自定义tomcat的server.xml文件,并将配置放到全量包中上线,避免每次拉取默认配置。

2. tomcat默认模板添加此标准配置:

<span style="">&lt;<span>Valve</span> <span>className</span>=<span>&quot;org.apache.catalina.valves.RemoteIpValve&quot;</span> </span>

<span><span>remoteIpHeader</span>=<span>&quot;X-Forwarded-For&quot;</span></span>

<span><span>protocolHeader</span>=<span>&quot;X-Forwarded-Proto&quot;</span></span>

<span><span>protocolHeaderHttpsValue</span>=<span>&quot;https&quot;</span></span>

<span>/&gt;</span>

3. Nginx改进更通用的传递方式。 除了X-Forwarded-For变量外,增加x-Real-IP变量,同时将两个变量的值都传递给后端,后端tomcat可以根据具体需求选择不同的变量来获取客户端真实IP。 具体配置如下:


 

#### 获取用户IP ####

set $remote_address $http_x_forwarded_for;

if ( $remote_address !~ "[0-9]" ) {

set $remote_address $remote_addr;

}

#### ----------- ####

location / {

proxy_pass http://$host_pass;

proxy_set_header Host $host;

proxy_set_header X-Forwarded-For $remote_address;

proxy_set_header X-Real-IP $remote_address;

proxy_set_header HTTPS-Tag "HTTP";

proxy_set_header X-Forwarded-Proto $scheme;

}

后续思考

X-Forwarded-For  真的可以获取到用户的真实IP吗?

客户端IP是站点日志中最为重要的部分,很多网站通过客户端IP分析用户分布,进行数据统计。

HTTP协议下获取客户端真实IP是通过X-Forwarded-For变量(以下简称XFF)。

X-Forwarded-For 格式:

X-Forwarded-For:client, proxy1, proxy2

XFF中记录的每个IP是用户从自己出口IP到服务器间,网络上经过的各种代理,最开始的是离服务端最远的设备IP,然后是每一级代理IP。

XFF的IP是可以随意伪造的:

curl http://10.9.XXX.XXX/api/v1/request/ip/address/ -H 'host:xff.58.com' -H 'x-forwarded-for:1.1.1.1,2.2.2.2,101.126.1.1'

后端返回的结果都是获取我伪造的IP

【问题排查】tomcat无法获取客户端IP

因此,要获取客户端IP,不能盲目的相信请求头里传递过来的值。

后端对客户端IP的具体需求是什么?

  • 用户统计分析

  • 地域判断

  • 风险控制

对于线上请求,大部分的请求都是真实有效的,可以通过XFF的值来取到用户的IP。

但对于风险控制来讲,往往需要通过对IP及其他因素进行判断来限制访问。 可以伪造的XFF不是一个合理的取值。 应该使用真实连接nginx的IP进行限制。

业务同学可以根据实际需求,从请求头里获取最正确的IP。

Nginx相关配置参数及解释:

将请求Nginx的remoteIP赋值给XFF,并传给后端集群

proxy_set_header X-Forwarded-For $remote_addr;

将请求Nginx的remoteIP添加到XFF最后,并传给后端集群

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

为了防止XFF为空,额外增加X-Real-IP进行补充

proxy_set_header X-Real-IP $proxy_add_x_forwarded_for;

【问题排查】tomcat无法获取客户端IP

原文  http://mp.weixin.qq.com/s?__biz=MzI1NDc5MzIxMw==&mid=2247485312&idx=1&sn=9adce2fe869f0cd7bea0885b1ff2260e
正文到此结束
Loading...