一、問題背景
通過nginx來反向代理客戶端請求,經過nginx中轉轉發給tomcat服務器,但發現tomcat服務器無法獲取到正確的remoteAddr客戶端地址,每次請求拿到的都是nginx所在服務器的IP
1、在tomcat服務器上查看tomcat服務日誌,發現打印的請求日誌,remoteAddr都是nginx的IP,而不是客戶端的真實IP
## 192.168.200.251是nginx負載均衡服務器的地址
15:08:12 INFO [tid:166453,Q:W228AFjoBcRQIzvVzOuJ] [L-Android]func=xxxx,remote=192.168.200.251
二、定位過程
1、nginx配置如下
location /myService {
proxy_set_header X-FORWARDED-FOR $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
#分發到tomcat服務器,192.168.201.228爲tomcat服務器
proxy_pass http://192.168.201.228:9900;
}
一開始以爲是nginx配置有問題,設置了各種參數後,發現不是nginx問題,而是tomcat 的設置影響,在tomcat的conf/server.xml裏RemoteIpValve項的InternalProxies
<Engine name="Catalina" defaultHost="localhost">
<Host name="localhost" appBase="webapps"
unpackWARs="false" autoDeploy="true"
<Valve className="org.apache.catalina.valves.AccessLogValve"
<!-- for nginx -->
<Valve className="org.apache.catalina.valves.RemoteIpValve"
remoteIpHeader="X-FORWARDED-FOR"
protocolHeader="X-FORWARDED-PROTO"
internalProxies="192\.168\.*"/>
</Host>
</Engine>
在使用Nginx代理網絡請求時,設置proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for,但是在我的server端(Tomcat,Spring)收到的網絡請求中卻得不到這個x-forwarded-for的信息,原因是我的代理機器的IP地址與Tomcat RemoteIpValve中配置的默認內部代理地址internalProxies能匹配,於是x-forwarded-for信息被忽略並刪除了。
(在server.xml中刪除RemoteIpValve的默認配置,或者配置internalProxies爲其他地址(如server的地址),就可以在x-forwarded-for中得到代理傳輸過來的信息。)
三、解決方案
1、將tomcat server.xml的internalProxies修改爲nginx的IP,tomcat即可正確處理x-forwarded-for邏輯,獲取正確的remoteAddr
<Engine name="Catalina" defaultHost="localhost">
<Host name="localhost" appBase="webapps"
unpackWARs="false" autoDeploy="true"
<Valve className="org.apache.catalina.valves.AccessLogValve"
<!-- for nginx -->
<Valve className="org.apache.catalina.valves.RemoteIpValve"
remoteIpHeader="X-FORWARDED-FOR"
protocolHeader="X-FORWARDED-PROTO"
## 修改internalProxies爲nginx的IP
internalProxies="127\.0\.0\.1|192\.168\.200\.251"/>
</Host>
</Engine>
2、重啓tomcat,查看客戶端請求日誌,可以看到remoteAddr已經是客戶端的真實IP而不是nginx的IP了
16:08:12 INFO [tid:166453,Q:W228AFjoBcRQIzvVzOuJ] [L-Android]func=xxxx,remote=192.168.201.46
四、tomcat RemoteIpValve InternalProxies原理解析
X-Forwarded-For
x-forwarded-for在HTTP Header中,用來記錄代理鏈的IP信息(不包括最後一次的代理)。
RemoteIpValve
Tomcat中的RemoteIpValve的設計意圖是根據InternalProxies和TrustedProxies的配置,來過濾代理信息鏈X-Forwarded-For中的信息,獲取代理機器之前的IP地址,並改寫RemoteAddress的值。如果沒有RemoteIpValve的處理,且有代理轉發的情況下,則RemoteAddress永遠爲代理機器的IP地址,而不是代理機器之前發送數據包機器的IP地址(根據網絡情況,可能爲實際的用戶IP地址)
具體邏輯如下圖所示。
初始RemoteAddress爲代理機器的IP地址,首先判斷是否與InternalProxies匹配,如果不匹配,則RemoterIpValve不會做處理;如果匹配,則根據InternalProxies和TrustedProxies的配置來決定是否改寫X-Forwarded-For和RemoteAddress以及X-Forwarded-By。
internalProxies默認情況下是有配置值的(貌似是常見的局域網IP地址),trustedProxies默認爲null。而我的內網代理機器和我本機的IP地址都與InternalProxies匹配,則X-Forwarded-For中代理鏈的信息被刪除。
參考: