歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux技術 >> 詳解Linux下iptables中的DNAT與SNAT設置

詳解Linux下iptables中的DNAT與SNAT設置

日期:2017/3/1 17:33:47   编辑:Linux技術

DNAT(Destination Network Address Translation,目的地址轉換) 通常被叫做目的映謝。而SNAT(Source Network Address Translation,源地址轉換)通常被叫做源映謝。
這是我們在設置Linux網關或者防火牆時經常要用來的兩種方式。以前對這兩個都解釋得不太清楚,現在我在這裡解釋一下。
首先,我們要了解一下IP包的結構,如下圖所示:
20151123120626845.gif (616×278)

在任何一個IP數據包中,都會有Source IP Address與Destination IP Address這兩個字段,數據包所經過的路由器也是根據這兩個字段是判定數據包是由什麼地方發過來的,它要將數據包發到什麼地方去。而iptables的DNAT與SNAT就是根據這個原理,對Source IP Address與Destination IP Address進行修改。
然後,我們再看看數據包在iptables中要經過的鏈(chain):
20151123120648524.png (501×258)

圖中正菱形的區域是對數據包進行判定轉發的地方。在這裡,系統會根據IP數據包中的destination ip address中的IP地址對數據包進行分發。如果destination ip adress是本機地址,數據將會被轉交給INPUT鏈。如果不是本機地址,則交給FORWARD鏈檢測。
這也就是說,我們要做的DNAT要在進入這個菱形轉發區域之前,也就是在PREROUTING鏈中做,比如我們要把訪問202.103.96.112的訪問轉發到192.168.0.112上:
iptables -t nat -A PREROUTING -d 202.103.96.112 -j DNAT --to-destination 192.168.0.112
這個轉換過程當中,其實就是將已經達到這台Linux網關(防火牆)上的數據包上的destination ip address從202.103.96.112修改為192.168.0.112然後交給系統路由進行轉發。
而SNAT自然是要在數據包流出這台機器之前的最後一個鏈也就是POSTROUTING鏈來進行操作
iptables -t nat -A POSTROUTING -s 192.168.0.0/24 -j SNAT --to-source 58.20.51.66
這個語句就是告訴系統把即將要流出本機的數據的source ip address修改成為58.20.51.66。這樣,數據包在達到目的機器以後,目的機器會將包返回到58.20.51.66也就是本機。如果不做這個操作,那麼你的數據包在傳遞的過程中,reply的包肯定會丟失。

注意,DNAT target只能用在nat表的PREOUTING 和 OUTPUT 鏈中,或者是被這兩條鏈調用的鏈裡。但還要注意的是,包含DNAT target的連不能被除此之外的其他鏈調用,如POSTROUTING。

復制代碼代碼如下:
Table 6-16. DNAT target
Option --to-destination
Example iptables -t nat -A PREROUTING -p tcp -d 15.45.23.67 --dport 80 -j DNAT --to-destination 192.168.1.1-192.168.1.10

Explanation指定要寫入IP頭的地址,這也是包要被轉發到的地方。上面的例子就是把所有發往地址15.45.23.67的包都轉發到一段LAN使用的私有地址中,即192.168.1.1到192.168.1.10。如前所述,在這種情況下,每個流都會被隨機分配一個要轉發到的地址,但同一個流總是使用同一個地址。我們也可以只指定一個IP地址作為參數,這樣所有包都被轉發到同一台機子。我們還可以在地址後指定一個或一個范圍的端口。比如:--to-destination 192.168.1.1:80或192.168.1.1:80-100。SNAT的語法和這個target的一樣,只是目的不同罷了。要注意,只有先用--protocol指定了TCP或UDP協議,才能使用端口。
因為DNAT要做很多工作,所以我要再啰嗦一點。我們通過一個例子來大致理解一下它是如何工作的。比如,我想通過Internet連接發布我們的網站,但是HTTP server在我們的內網裡,而且我們對外只有一個合法的IP,就是防火牆那個對外的IP——$INET_IP。防火牆還有一個內網的IP——$LAN_IP,HTTP server的IP是%HTTP_IP(當然這是內網的了)。為了完成我們的設想,要做的第一件事就是把下面這個簡單的規則加入到nat表的PREROUTING鏈中:

復制代碼代碼如下:
iptables -t nat -A PREROUTING --dst$INET_IP -p tcp --dport 80 -j DNAT / --to-destination $HTTP_IP

現在,所有從Internet來的、到防火牆的80端口去的包都會被轉發(或稱作被DNAT)到在內網的HTTP服務器上。如果你在Internet上試驗一下,一切正常吧。再從內網裡試驗一下,完全不能用吧。這其實是路由的問題。下面我們來好好分析這個問題。為了容易閱讀,我們把在外網上訪問我們服務器的那台機子的IP地址記為$EXT_BOX。
包從地址為$EXT_BOX的機子出發,去往地址為$INET_IP的機子。
包到達防火牆。
防火牆DNAT(也就是轉發)這個包,而且包會經過很多其他的鏈檢驗及處理。
包離開防火牆向$HTTP_IP前進。
包到達HTTP服務器,服務器就會通過防火牆給以回應,當然,這要求把防火牆作為HTTP到達$EXT_BOX的網關。一般情況下,防火牆就是HTTP服務器的缺省網關。
防火牆再對返回包做Un-DNAT(就是照著DNAT的步驟反過來做一遍),這樣就好像是防火牆自己回復了那個來自外網的請求包。
返回包好像沒經過這麼復雜的處理、沒事一樣回到$EXT_BOX。
現在,我們來考慮和HTTP服務器在同一個內網(這裡是指所有機子不需要經過路由器而可以直接互相訪問的網絡,不是那種把服務器和客戶機又分在不同子網的情況)的客戶訪問它時會發生什麼。我們假設客戶機的IP為$LAN_BOX,其他設置同上。
包離開$LAN_BOX,去往$INET_IP。
包到達防火牆。
包被DNAT,而且還會經過其他的處理。但是包沒有經過SNAT的處理,所以包還是使用它自己的源地址,就是$LAN_BOX(譯者注:這就是IP傳輸包的特點,只根據目的地的不同而改變目的地址,但不因傳輸過程要經過很多路由器而隨著路由器改變其源地址,除非你單獨進行源地址的改變。其實這一步的處理和對外來包的處理是一樣的,只不過內網包的問題就在於此,所以這裡交代一下原因)。
包離開防火牆,到達HTTP服務器。
HTTP服務器試圖回復這個包。它在路由數據庫中看到包是來自同一個網絡的一台機子,因此它會把回復包直接發送到請求包的源地址(現在是回復包的目的地址),也就是$LAN_BOX。
回復包到達客戶機,但它會很困惑,因為這個包不是來自它訪問的那台機子。這樣,它就會把這個包扔掉而去等待“真正”的回復包。
針對這個問題有個簡單的解決辦法,因為這些包都要進入防火牆,而且它們都去往需要做DNAT才能到達的那個地址,所以我們只要對這些包做SNAT操作即可。比如,我們來考慮上面的例子,如果對那些進入防火牆而且是去往地址為$HTTP_IP、端口為80的包做SNAT操作,那麼這些包就好像是從$LAN_IP來的了。這樣,HTTP服務器就會把回復包發給防火牆,而防火牆會再對包做Un-DNAT操作,並把包發送到客戶機。解決問題的規則如下:
iptables -t -nat -A POSTROUTING -p tcp --dst$HTTP_IP --dport 80 -j SNAT / --to-source $LAN_IP
要記住,按運行的順序POSTROUTING鏈是所有鏈中最後一個,因此包到達這條鏈時,已經被做過DNAT操作了,所以我們在規則裡要基於內網的地址$HTTP_IP(包的目的地)來匹配包。
警告:我們剛才寫的這條規則會對日志產生很大影響,這種影響應該說是很不好的,因為來自Internet包在防火牆內先後經過了DNAT和SNAT處理,才能到達HTTP服務器(上面的例子),所以HTTP服務器就認為包是防火牆發來的,而不知道真正的源頭是其他的IP。這樣, 當它記錄服務情況時,所有訪問記錄的源地址都是防火牆的IP而不是真正的訪問源。我們如果想根據這些記錄來了解訪問情況就不可能了。因此上面提供的“簡單辦法”並不是一個明智的選擇,但它確實可以解決“能夠訪問”的問題,只是沒有考慮到日志而已。
其他的服務也有類似的問題。比如,你在LAN內建立了SMTP服務器,那你就要設置防火牆以便能轉發SMTP的數據流。這樣你就創建了一個開放SMTP的中繼服務器,隨之而來的就是日志的問題了。
一定要注意,這裡所說的問題只是針對沒有建立DMZ或類似結構的網絡,並且內網的用戶訪問的是服務器的外網地址而言的。(譯者注:因為如果建立的DMZ,或者服務器和客戶機又被分在不同的子網裡,那就不需要這麼麻煩了。因為所有訪問的源頭都不在服務器所在的網裡,所以就沒有必要做SNAT去改變包的源地址了,從而記錄也就不是問題了。如果內網客戶是直接訪問服務器的內網地址那就更沒事了)
比較好的解決辦法是為你的LAN在內網建立一台單獨的DNS服務器(譯者注:這樣,內網客戶使用網站名訪問HTTP服務器時,DNS就可以把他解析成內網地址。客戶機就可以直接去訪問HTTP服務器的內網地址了,從而避免了通過防火牆的操作,而且包的源地址也可以被HTTP服務器的日志使用,也就沒有上面說的日志問題了。),或者干脆建立DMZ得了(這是最好的辦法,但你要有錢哦,因為用的設備多啊)。
對上面的例子應該考慮再全面些,現在還有一個問題沒解決,就是防火牆自己要訪問HTTP服務器時會發生什麼,能正常訪問嗎?你覺得呢:)很可惜,現在的配置還是不行,仔細想想就明白了。我們這裡討論的基礎都是假設機子訪問的是HTTP服務器的外網地址,那客戶機就會看到頁面內容,不過這不是它想看到的(它想要的在DNAT上了),如果沒有HTTP服務,客戶就只能收到錯誤信息了。前面給出的規則之所以不起作用是因為從防火牆發出的請求包不會經過那兩條鏈。還記得防火牆自己發出的包經過哪些鏈吧:) 我們要在nat表的OUTPUT鏈中添加下面的規則:

復制代碼代碼如下:
iptables -t nat -A OUTPUT --dst$INET_IP -p tcp --dport 80 -j DNAT / --to-destination $HTTP_IP

有了最後這條規則,一切都正常了。和HTTP服務器不在同一個網的機子能正常訪問服務了,和它在一個網內的機子也可以正常訪問服務了,防火牆本身也可以正常訪問服務了,沒有什麼問題了。 我想大家應該能明白這些規則只是說明了數據包是如何恰當的被DNAT和SNAT的。除此之外,在filter表中還需要其他的規則(在FORWARD鏈裡),以允許特定的包也能經過前面寫的(在POSTROUTING鏈和OUTPUT鏈裡的)規則。千萬不要忘了,那些包在到達FORWARD鏈之前已經在PREROUTING鏈裡被DNAT過了,也就是說它們的目的地址已經被改寫,在寫規則時要注意這一點。

Copyright © Linux教程網 All Rights Reserved