Nginx多級反向代理下的IP透傳


透傳IP

為何要做透傳IP

在使用了CDN做加速站點靜態資源加速后,當用戶請求的靜態資源沒能命中,此時CDN會到源站請求內容,那么此時訪問源站的IPCDN節點的IP,不僅如此,可能經我們的WAF防火牆和前端的負載均衡(SLB)后更不容易獲取到真實的用戶IP信息,我們如果要統計用戶的訪問IP和地區就變得比較麻煩,因為可能不是真實的IP,必須使用一個什么機制將用戶IP傳遞到最終后端的應用服務器才行。

實驗環境

訪問流程

liucheng-img

主機 IP配置 備注
Chrome 10.0.0.1 Windows瀏覽器
LB-01 10.0.0.5 一級代理
LB-02 10.0.0.6 二級代理
LB-03 10.0.0.7 三級代理
WEB 10.0.0.8 WEB主機

常見的幾種方式

X-Real-IP

描述

在每個HTTP請求頭中加入X-Real-IP信息,若只存在1級的代理服務器,則該參數的確就是客戶端的真實IP,但若是存在多級代理時,此信息為上級代理的IP信息,並不能獲取到真實的用戶IP,故此法目前已棄用。

nginx配置
#LB-01一級代理配置
server {
    listen 80;
    server_name ip.test.com;
    
    location / {
        proxy_pass http://10.0.0.6;
        proxy_http_version 1.1;
        Proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}
#LB-02二級代理配置
server {
    listen 80;
    server_name ip.test.com;
    
    location / {
        proxy_pass http://10.0.0.7;
        proxy_http_version 1.1;
        Proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}
#LB-03三級代理配置
server {
    listen 80;
    server_name ip.test.com;
    
    location / {
        proxy_pass http://10.0.0.8;
        proxy_http_version 1.1;
        Proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}
#WEB主機配置
server {
    listen 80;
    server_name ip.test.com;
    
    root /wwwroot;
    index index.php;
    location ~ \.php$ {
        fastcgi_pass 127.0.0.1:9000;
		fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
		include fastcgi_params;
    }
}
WEB主機測試腳本
#文件名:index.php
#放到測試目錄/wwwroot內
<?php
    $IP_ADDRESS = getenv("HTTP_X_REAL_IP");
	echo "SOURCE IP ADDRESS: $IP_ADDRESS";
?>

注:配置完成后保存所有配置文件,然后啟動所有的nginx服務。如果在windows下測試一定要在C:\Windows\System32\drivers\etc\hosts文件內加入本地解析的記錄,如下:

10.0.0.5	ip.test.com
Wireshark抓包信息

Wireshark-pack

HTTP報頭信息
GET / HTTP/1.1
Host: ip.test.com
X-Real-IP: 10.0.0.6
Connection: close
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: UM_distinctid=16afc8d323f94-0360d7ebcc9bae-3d644509-18e414-16afc8d324063f
chrome打開測試

X-Real-IP

結論:Wireshark追蹤流和Chrome的測試可見,X-Real-IP並不能獲取到真實的客戶端IP地址,如果時單級代理情況下,可以獲取到正確的客戶端IP,若存在多級代理就歇菜了。


X-Forwarded-For

描述

在每個HTTP請求頭中加入X-Forwarded-For信息,若只存在1級的代理服務器,則該參數為客戶端的真實IP,若是存在多級代理時,每經過一級代理服務器,則追加上級代理服務的IP,可以獲取到真實的用戶IP,但若是遇到偽造的X-Forwarded-For信息或第一級代理未啟用X-Forwarded-For都不能獲取到真實用戶IP,此法是目前比較常用的方法,但更推薦使用nginx_http_realip_module模塊添加可信代理的方法。

nginx配置
#LB-01一級代理配置
server {
    listen 80;
    server_name ip.test.com;
    
    location / {
        proxy_pass http://10.0.0.6;
        proxy_http_version 1.1;
        Proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        #可以嘗試注釋此處的,看看最終能否獲取到真實客戶端IP
    }
}
#LB-02二級代理配置
server {
    listen 80;
    server_name ip.test.com;
    
    location / {
        proxy_pass http://10.0.0.7;
        proxy_http_version 1.1;
        Proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}
#LB-03三級代理配置
server {
    listen 80;
    server_name ip.test.com;
    
    location / {
        proxy_pass http://10.0.0.8;
        proxy_http_version 1.1;
        Proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}
#WEB主機配置
server {
    listen 80;
    server_name ip.test.com;
    
    root /wwwroot;
    index index.php;
    location ~ \.php$ {
        fastcgi_pass 127.0.0.1:9000;
		fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
		include fastcgi_params;
    }
}
WEB主機測試腳本
#文件名:index.php
#放到測試目錄/wwwroot內
<?php
    $IP_ADDRESS = getenv("HTTP_X_FORWARD_FOR");
	echo "SOURCE IP ADDRESS: $IP_ADDRESS";
?>

注:記得重載nginx配置。

Wireshark抓包信息

Wireshark-pack-x-forward-for.png

HTTP報頭信息
GET / HTTP/1.1
Host: ip.test.com
X-Forwarded-For: 10.0.0.1, 10.0.0.5, 10.0.0.6
Connection: close
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: UM_distinctid=16afc8d323f94-0360d7ebcc9bae-3d644509-18e414-16afc8d324063f
chrome打開測試

x-forward-for-php.png
結論:Wireshark追蹤流和Chrome的測試可見,X-Forwarded-For可以獲取到真實的客戶端IP地址,即便是在多級代理下,也可以獲取到正確的客戶端IP,但如果某台代理未設置X-Forwarded-For,后端應用服務器可能並不能獲取到正確的客戶端IP


nginx_http_realip_module

描述

通過預定義可信任的代理主機的IP的方式(可信代理主機默認都會加入X-Forwarded-For信息),根據些X-Forwarded-For的信息,從右到左,濾除掉這些可信代理的IP信息,最終獲取到的就是真實客戶端IP信息,此信息替換$remote_addr這個變量,使最終客戶端IPWEB后端應用服務器的access.log第一列顯示,方便取值分析。

nginx官方描述

nginx配置
#LB-01一級代理配置
server {
    listen 80;
    server_name ip.test.com;
    
    location / {
        proxy_pass http://10.0.0.6;
        proxy_http_version 1.1;
        Proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        #此為可信任的代理服務器,故需要加入這條配置
    }
}
#LB-02二級代理配置
server {
    listen 80;
    server_name ip.test.com;
    
    location / {
        proxy_pass http://10.0.0.7;
        proxy_http_version 1.1;
        Proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        #此為可信任的代理服務器,故需要加入這條配置
    }
}
#LB-03三級代理配置
server {
    listen 80;
    server_name ip.test.com;
    
    location / {
        proxy_pass http://10.0.0.8;
        proxy_http_version 1.1;
        Proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        #此為可信任的代理服務器,故需要加入這條配置
    }
}
#WEB主機配置,使用此模塊是在后端WEB應用服務器添加參數
server {
    listen 80;
    server_name ip.test.com;
    #定義可信任的代理服務器地址
	set_real_ip_from  10.0.0.5;
	set_real_ip_from  10.0.0.6;
	set_real_ip_from  10.0.0.7;
    #指定從哪個HTTP報頭里檢索IP信息
	real_ip_header    X-Forwarded-For;
    #遞歸排除每個代理服務器的IP
	real_ip_recursive on;
    
    root /wwwroot;
    index index.php;
    location ~ \.php$ {
        fastcgi_pass 127.0.0.1:9000;
		fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
		include fastcgi_params;
    }
}
WEB主機測試腳本
#文件名:index.php
#放到測試目錄/wwwroot內
<?php
    $IP_ADDRESS = getenv("HTTP_X_FORWARD_FOR");
	echo "SOURCE IP ADDRESS: $IP_ADDRESS";
?>

注:記得重載nginx配置。

Wireshark抓包信息

Wireshark-pack-x-forward-for-nginx-module.png

HTTP報頭信息
GET / HTTP/1.1
Host: ip.test.com
X-Forwarded-For: 10.0.0.1, 10.0.0.5, 10.0.0.6
Connection: close
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: UM_distinctid=16afc8d323f94-0360d7ebcc9bae-3d644509-18e414-16afc8d324063f
chrome打開測試

nginx-http-realip-module

WEB應用服務器的訪問LOG

nginx-accesslog

結論:為了解決X-Forwarded-For可能存在偽裝IP的問題,我們在后端使用了nginx_http_realip_module模塊的set_real_ip_from關鍵字來添加可信IP並從X-Forwarded-For頭中篩選出去,最終成功獲取到了客戶端IP,並且也對格式日志做了一定的替換,使客戶端IP顯示於access.log的第一列,方便最終的取值分析,這里需要注意的是,一定要添加正確且絕對可信的代理服務器IP,否則最終結果還是存在問題。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM