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

| 主機 | 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抓包信息

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打開測試

結論:由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抓包信息

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打開測試

結論:由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這個變量,使最終客戶端IP在WEB后端應用服務器的access.log第一列顯示,方便取值分析。
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抓包信息

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打開測試

WEB應用服務器的訪問LOG

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