有個需求,就是在本地通過一臺代理(1.1.1.1)訪問它后面的服務(2.2.2.2),需要直接配置本地應用的訪問地址為服務的地址,但是本地網路到服務又是無法直接通信的。于是想到用iptables規則,對發出的資料包做dnat處理,即本地應用直接訪問2.2.2.2,然后將請求的目的地址通過dnat替換成1.1.1.1。先說一下成功的做法,后面再探討一下操作的誤區和一些思考。(以下操作全都在本地應用節點上進行)
百度搜索 "iptables 將主機發出的目的地址改掉" 這幾個字找到答案:
iptables -t nat -A OUTPUT -d 2.2.2.2 -j DNAT --to 1.1.1.1 (完事了,就是這么簡單)
下面說一下錯誤的做法:
iptables -t nat -A PREROUTING -d 2.2.2.2 -j DNAT --to 1.1.1.1
iptables -t nat -A POSTROUTING -s 1.1.1.1 -j SNAT --to 2.2.2.2
對這是錯誤的,想法是對的,操作是錯的。先說一下為什么這么做,第一條規則是想把發送發往2.2.2.2的包的目的地址改成1.1.1.1,這樣請求就可以通過1.1.1.1被代理到后面的服務2.2.2.2上面。然后第二條規則,是想將2.2.2.2回傳的回應包的源的地址做snat,改成2.2.2.2,這樣做的目的也是為了保證客戶端請求和回應地址的一致性,以確保tcp會話的完整性,如果請求和回應的地址不通夠不成一個完整的會話。
目前來看想法是沒問題的,然而并沒有達到預期的效果,在本地ping 2.2.2.2 ,同時另開一個shell進行tcpdump截包,發現資料包根本出不來。
對于修改請求包的目的地址,對比正確的操作,我們可以看到,規則加錯了鏈,應該加到OUTPUT鏈,而不是PREROUTING鏈。于是我找到了下面這張圖(來自簡書-任總的精美技術檔案 https://www.jianshu.com/p/e6360c2ac19d)。
從而復習了以下各條鏈作用的位置
prerouting:流入的資料包進入路由表之前。
input :通過路由表判斷后目的地址是本機,然后進入本機內部資源。
output :由本機產生的資料向外轉發
forward :通過路由表判斷目的地址是本機,然后通過路由轉發到其他地方。
postrouting:傳出的資料包到達網卡出口前。

結合大佬的解釋和我遭遇的挫折,我理解或者我猜想,postroutging鏈只處理網卡接收的資料且,只能添加SNAT規則,不能添加DNAT規則,prerouting鏈與次正好相反,只處理應用發出的資料包,我嘗試過確實如此。
用戶空間的請求包為出方向的資料包是這樣走的:用戶空間---->output----->(經過路由)------>postrouting
服務器應的資料包是入方向的資料包是這樣走的:prerouting----->(經過路由)----->input----->用戶空間
我要做的是出方向的DNAT,上面說了postrouting不能添加DNAT規則,那么只能在output鏈添加DNAT規則鏈了,猜想與最開始百度的結果不謀而合了。基本上就是這么回事了,解決了請求包的DNAT的需求。
看上去我還漏了一個多余的需求,就是回應包的SNAT,說它多余是因為真的多余,這個是不需要做的。難道不需要保持請求回應地址的一致性來確保tcp會話的完整性嗎??就這個問題我思考了 a long time,看上面的圖片,資料在output鏈經過DNAT處理后,被送到路由處理,然后才到postrouting從網卡發送出去,路由作業在哪,ip層好吧。這個地方我就想了,OUTPUT鏈作業在哪一層,tcp層還是ip層,tcp包不帶ip,那至少不在tcp層,那就認為在ip層了。既然ip包的封裝是在OUTPUT鏈所在的ip層做的,那這個地方我就可以認為與服務端建立會話的地址就是DNAT之后的地址,也就是1.1.1.1。
事實上我做了DNAT以后,通過ping,通過curl命令訪問對端服務,都能正常訪問了,既然如此,我只能這么猜了,有猜的不對的地方請大佬帶帶我


轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/8327.html
標籤:專題技術討論區
上一篇:Dind9.16+dlz+mysql的主從配置,該如何配置主從
下一篇:Linux系統開機報錯
