nginx+tomcat 獲取正確的remoteAddr

一、問題背景

通過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中代理鏈的信息被刪除。

參考:

  1. Tomcat 8的RemoteIpValve源碼(Valve是閥門的意思),核心處理邏輯是invoke函數
  2. RemoteIpValve文檔
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章