iptables 應用初探(nat+三層訪問控制)

本文作者:張天成    zhangtiancheng#gmail.com 轉發請註明出處。

    iptables是一個Linux下優秀的nat+防火牆工具,我使用該工具以較低配置的傳統pc配置了一個靈活強勁的防火牆+nat系統,小有心得,看了網上也有很多這方面的文章,但是似乎要麼說的比較少,要麼就是比較偏,內容不全,容易誤導,我研究了一段時間的iptables同時也用了很久,有點滴經驗,寫來供大家參考,同時也備日後自己翻閱。
首先要說明的是,iptables操作的是2.4以上內核的netfilter.所以需要linux的內核在2.4以上。其功能與安全性遠遠比其前輩 ipfwadm,ipchains強大,iptables大致是工作在OSI七層的二、三、四層,其前輩ipchains不能單獨實現對tcp/udp port以及對mac地址的的定義與操作,所以我想ipchains應該是僅僅工作在三層上的。

    我們先簡單介紹一下netfilter的大致工作流程,也就是一個數據包(或者叫分組、packet,我個人習慣叫包)在到達linux的網絡接口的時候(網卡)如何處理這個包,然後再介紹一下如何用iptables改變或者說控制對這個數據包進行操作。netfilter內部分爲三個表,分別是 filter,nat,mangle,每個表又有不同的操作鏈(Chains)。在filter(過濾)表中,也就是他的防火牆功能的這個表,定義了三個 Chain。分別是INPUT,FORWARD,OUTPUT。也就是對包的入、轉發、出進行定義的三個過濾鏈。對於這個filter表的操作和控制也是我們實現防火牆功能的一個重要手段;在nat(Network Address Translation、網絡地址翻譯)表中,也就是我們用以實現地址轉換和端口轉發功能的這個表,定義了PREROUTING, POSTROUTING,OUTPUT三個鏈,下面我們會對這三個鏈作詳細的說明;而netfilter的mangle表則是一個自定義表,裏面包括上面的filter以及nat表中的各種chains,它可以讓我們進行一些自定義的操作,同時這個mangle表中的chains在netfilter對包的處理流程中處在一個比較優先的位置,下面有一張圖清晰的描繪了netfilter對包的處理流程(該圖摘自網上,不知作者是誰,在此深表敬意!),一般情況下,我們用不到這個mangle表,在這裏我們就不做介紹了。


  大家可以看到,PREROUTING這個chain在最前面,當一個包來到linux的網絡接口的時候先過mangle的PREROUTING,然後是 nat的PREROUTING,從這個chain的名字我們可以看出,這個chain是在路由之前(pre-routing)要過的。爲什麼要在路由之前過呢?大家可以看到這個圖上,上面有一個菱形的部分叫ROUTING,這個ROUTING部分就是Linux的route box,也就是路由系統,它同樣有很高深的功能,可以實現策略路由等等一些高級特性,此處我們不做詳細解釋。單說這個PREROUTING鏈,因爲在這個鏈裏面我們對包的操作是DNAT,也就是改變目的地址和(或端口),通常用在端口轉發,或者nat到內網的DMZ區,也就是說當一個包過來的時候我們要改變它的目的地址,大家可以想想,如果一個包在改變目的地址之前就被扔進了route box,讓系統選好路之後再改變目的地址,那麼選路就可能是錯的,或者說毫無意義了,所以,PREROUTING這個Chain一定要在進Routing 之前做。比如說,我們的公網ip是60.1.1.1/24,位於linux中的eth0,內網ip是10.1.1.1/24位於linux中的eth1, 我們的內網有一臺web服務器,地址是10.1.1.2/24,我們怎麼樣能讓internet用戶通過這個公網ip訪問我們內部的這個web服務器呢?我們就可以在這個PREROUTING鏈上面定義一個規則,把訪問60.1.1.1:80的用戶的目的地址改變一下,改變爲10.1.1.2:80,這樣就實現了internet用戶對內網服務器的訪問了,當然了,這個端口是比較靈活的,我們可以定義任何一個端口的轉發,不一定是80-->80,具體的命令我們在下面的例子中介紹,這裏我們只談流程與概念上的實現方法。

好了,我們接着往下走,這個包已經過了兩個PREROUTING鏈了,這個時候,出現了一個分支轉折的地方,也就是圖中下方的那個菱形(FORWARD),轉發!這裏有一個對目的地址的判斷(這裏同樣說明了PREROUTING一定要在最先,不僅要在route box之前,甚至是這個對目的地址的判斷之前,因爲我們可能做一個去某某某ip的地方轉到自己的ip的規則,所以PREROUTING是最先處理這個包的 Chain)!如果包的目的地是本機ip,那麼包向上走,走入INPUT鏈處理,然後進入LOCAL PROCESS,如果非本地,那麼就進入FORWARD鏈進行過濾,我們在這裏就不介紹INPUT,OUTPUT的處理了,因爲那主要是對於本機安全的一種處理,我們這裏主要說對轉發的過濾和nat的實現。

這裏的FORWARD我簡單說一下,當linux收到了一個目的ip地址不是本地的包,Linux會把這個包丟棄,因爲默認情況下,Linux的三層包轉發功能是關閉的,如果要讓我們的linux實現轉發,則需要打開這個轉發功能,可以改變它的一個系統參數,使用sysctl net.ipv4.ip_forward=1或者echo "1" > /proc/sys/net/ipv4/ip_forward命令打開轉發功能。好了,在這裏我們讓linux允許轉發,這個包的目的地址也不是本機,那麼它將接着走入FORWARD鏈,在FORWARD鏈裏面,我們就可以定義詳細的規則,也就是是否允許他通過,或者對這個包的方向流程進行一些改變,這也是我們實現訪問控制的地方,這裏同樣也是Mangle_FORWARD然後filter_FORWARD,我們操作任何一個鏈都會影響到這個包的命運,在下面的介紹中,我們就忽略掉mangle表,我們基本用不到操作它,所以我們假設它是透明的。假設這個包被我們的規則放過去了,也就是ACCEPT了,它將進入POSTROUTING部分,注意!這裏我注意到一個細節問題,也就是上面的圖中數據包過了FORWARD鏈之後直接進入了POSTROUITNG 鏈,我覺得這中間缺少一個環節,也就是route box,對於轉發的包來說,linux同樣需要在選路(路由)之後才能將它送出,這個圖卻沒有標明這一點,我認爲它是在過了route box之後才進入的POSTROUITNG,當然了,這對於我們討論iptables的過濾轉發來說不是很重要,只是我覺得流程上有這個問題,還是要說明一下。

同樣的,我們在這裏從名字就可以看出,這個POSTROUTING鏈應該是路由之後的一個鏈,也就是這個包要送出這臺Linux的最後一個環節了,這也是極其重要的一個環節!!這個時候linux已經完成(has done..^_^)了對這個包的路由(選路工作),已經找到了合適的接口送出這個包了,在這個鏈裏面我們要進行重要的操作,就是被Linux稱爲 SNAT的一個動作,修改源ip地址!爲什麼修改源ip地址?很多情況需要修改源地址阿,最常見的就是我們內網多臺機器需要共享一個或幾個公網ip訪問 internet,因爲我們的內網地址是私有的,假如就讓linux給路由出去,源地址也不變,這個包應該能訪問到目的地,但是卻回不來,因爲 internet上的N多個路由節點不會轉發私有地址的數據包,也就是說,不用合法ip,我們的數據包有去無回。有人會說:“既然是這樣,我就不用私有 ip了,我自己分配自己合法的地址不行嗎?那樣包就會回來了吧?”答案是否定的,ip地址是ICANN來分配的,你的數據包或許能發到目的地,但是回來的時候人家可不會轉到你那裏,internet上的路由器中的路由信息會把這個返回包送到那個合法的獲得ip的地方去,你同樣收不到,而你這種行爲有可能被定義爲一種ip欺騙,很多設備會把這樣的包在接入端就給濾掉了,可能都到不了你要訪問的那個服務器,呵呵。

那麼Linux如何做SNAT呢?比如一個內網的10.1.1.11的pc訪問202.2.2.2的一個web服務器,linux的內網接口10.1.1.1在收到這個包之後把原來的PC的 ip10.1.1.11改變爲60.1.1.1的合法地址然後送出,同時在自己的ip_conntrack表裏面做一個記錄,記住是內網的哪一個ip的哪個端口訪問的這個web服務器,自己把它的源地址改成多少了,端口改成多少了,以便這個web服務器返回數據包的時候linux將它準確的送回給發送請求的這個pc. 

大體的數據轉發流程我們說完了,我們看看iptables使用什麼樣的參數來完成這些操作。
在描述這些具體的操作之前,我還要說幾個我對iptables的概念的理解(未必完全正確),這將有助於大家理解這些規則,以實現更精確的控制。上文中我們提到過,對包的控制是由我們在不同的Chain(鏈)上面添加不同的規則來實現的,比如我們對過濾表(filter table)添加規則來執行對包的操控。那麼既然叫鏈,一定就是一條或者多條規則組成的了,這時就有一個問題了,如果多個規則對同一種包進行了定義,會發生什麼事情呢?在Chain中,所有的規則都是從上向下來執行的,也就是說,如果匹配了第一行,那麼就按照第一行的規則執行,一行一行的往下找,直到找到符合這個類型的包的規則爲止。如果找了一遍沒有找到符合這個包的規則怎麼辦呢?itpables裏面有一個概念,就是Policy,也就是策略。一說這個東西大家可能就會覺得比較麻煩,什麼策略阿,我對於它的理解就是所謂這個策略就是chain中的最後一條規則,也就是說如果找了一遍找不到符合處理這個包的規則,就按照policy來辦。這樣理解起來就容易多了。iptables 使用-P來設置Chain的策略。

好了,我們言歸正傳,來說說iptables到底怎樣實現對包的控制

先介紹一下iptables如何操作鏈
對鏈的操作就那麼幾種,-I(插入) -A(追加) -R(替換) -D(刪除) -L(列表顯示)
這裏要說明的就是-I將會把規則放在第一行,-A將會放在最後一行。
比如我們要添加一個規則到filter表的FORWARD鏈
iptables -t filter -A FORWARD -s 10.1.1.11 -d 202.1.1.1 -j ACCEPT
上面的命令意思爲:追加一個規則至filter表中的FORWARD鏈尾,允許(-j ACCEPT)源地址爲10.1.1.11目的地址爲202.1.1.1的數據包通過。其中-t後面跟的是表名,在-A後面跟Chain名,後面的小寫的 -s爲源地址,-d爲目的地址,-j爲處理方向。
在iptables中,默認的表名就是filter,所以這裏可以省略-t filter直接寫成:iptables -A FORWARD -s 10.1.1.11 -d 202.1.1.1 -j ACCEPT

iptables中的匹配參數:
我們在這裏就介紹幾種常用的參數,詳細地用法可以man iptables看它的聯機文檔,你會有意外的收穫。
-s匹配源地址
-d匹配目的地址
-p協議匹配
-i入接口匹配
-o出接口匹配
--sport,--dport源和目的端口匹配
-j跳轉,也就是包的方向
其中還有一個!參數,使用!就是取反的意思。下面我們簡單舉幾個例子介紹一下。
-s這個參數呢就是指定源地址的,如果使用這個參數也就是告訴netfilter,對於符合這樣一個源地址的包怎麼去處理,可以指定某一個單播ip地址,也可以指定一個網絡,如果單個的ip地址其實隱含了一個32位的子網掩碼,比如-s 10.1.1.11 其實就是-s 10.1.1.11/32 同樣我們可以指定不同的掩碼用以實現源網絡地址的規則,比如一個C類地址我們可以用-s 10.1.1.0/24來指定。
-d參數與-s格式一樣。
-i參數是指定入接口的網絡接口,比如我僅僅允許從eth3接口過來的包通過FORWARD鏈,就可以這樣指定iptables -A FORWARD -i eth3 -j ACCEPT
-o是出接口,與上同.





我們下面用一些簡單的實例來step by step看看iptables的具體配置方法。



實例一:簡單的nat路由器
環境介紹:
linux 2.4 +
2個網絡接口
Lan口:10.1.1.254/24 eth0
Wan口:60.1.1.1/24 eth1
目的:實現內網中的節點(10.1.1.0/24)可控的訪問internet。

首先將Lan的節點pc的網關指向10.1.1.254
確定你的linux的ip配置無誤,可以正確的ping通內外的地址。同時用route命令查看linux的本地路由表,確認指定了可用的ISP提供的默認網關。
使用sysctl net.ipv4.ip_forward=1打開linux的轉發功能。

iptables -P FORWARD DROP
將FORWARD鏈的策略設置爲DROP,這樣做的目的是做到對內網ip的控制,你允許哪一個訪問internet就可以增加一個規則,不在規則中的ip將無法訪問internet.

iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
這條規則規定允許任何地址到任何地址的確認包和關聯包通過。一定要加這一條,否則你只允許lan IP訪問沒有用,至於爲什麼,下面我們再詳細說。

iptables -t nat -A POSTROUTING -s 10.1.1.0/24 -j SNAT --to 60.1.1.1
這條規則做了一個SNAT,也就是源地址轉換,將來自10.1.1.0/24的地址轉換爲60.1.1.1

有這幾條規則,一個簡單的nat路由器就實現了。這時你可以將允許訪問的ip添加至FORWARD鏈,他們就能訪問internet了。

比如我想讓10.1.1.9這個地址訪問internet,那麼你就加如下的命令就可以了。
iptables -A FORWARD -s 10.1.1.9 -j ACCEPT

也可以精確控制他的訪問地址,比如我就允許10.1.1.99訪問3.3.3.3這個ip
iptables -A FORWARD -s 10.1.1.99 -d 3.3.3.3 -j ACCEPT

或者只允許他們訪問80端口。
iptables -A FORWARD -s 10.1.1.0/24 -p tcp --dport http -j ACCEPT
更多的控制可以自己靈活去做,或者查閱iptables的聯機文檔。




實例二:端口轉發

環境介紹:
linux 2.4 +
2個網絡接口
Lan口:10.1.1.254/24 eth0
Lan內web server: 10.1.1.1:80
Lan內ftp server: 10.1.1.2:21
Wan口:60.1.1.1/24 eth1
目的:對內部server進行端口轉發實現internet用戶訪問內網服務器


同樣確認你的linux的各項配置正常,能夠訪問內外網。
iptables -P FORWARD DROP
iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
也需要加入確認包和關聯包的允許通過

如果你要把訪問60.1.1.1:80的數據包轉發到Lan內web server,用下面的命令
iptables -t nat -A PREROUTING -d 60.1.1.1 -p tcp --dport 80 -j DNAT --to 10.1.1.1:80
ftp服務也同樣,命令如下:
iptables -t nat -A PREROUTING -d 60.1.1.1 -p tcp --dport 21 -j DNAT --to 10.1.1.2:21

好了,命令完成了,端口轉發也做完了,本例能不能轉發呢?不能,爲什麼呢?我下面詳細分析一下。對於iptables好像往外訪問的配置比較容易,而對內的轉發似乎就有一些問題了,在一開始的時候我就先說了一些關於netfilter的流程問題,那麼我就簡單說說做了這些配置之後爲什麼有可能還不行呢?

能引起這個配置失敗的原因有很多,我們一個個的來說:
第一,本例中,我們的FORWARD策略是DROP,那麼也就是說,沒有符合規則的包將被丟棄,不管內到外還是外到內,我們在這裏依然不討論那個確認包和關聯包的問題,我們不用考慮他的問題,下面我會詳細說一下這個東西,那麼如何讓本例可以成功呢?加入下面的規則。
iptables -A FORWARD -d 10.1.1.1 -p tcp --dport 80 -j ACCEPT
iptables -A FORWARD -d 10.1.1.2 -p tcp --dport 21 -j ACCEPT
有沒有覺得有一些暈?爲什麼目的地址是10.xxx而不是60.xxx人家internet用戶不是訪問的60.xxx嗎?呵呵,回到上面看看那個圖吧, FORWARD鏈在什麼位置上,它是在PREROUTING之後,也就是說當這個包到達FORWARD鏈的時候,目的地址已經變成10.xxx了,假如 internet用戶的請求是這樣202.1.1.1:1333-->60.1.1.1:80,在經過了我們的PREROUTING鏈之後將變成 202.1.1.1:1333-->10.1.1.1:80,這個時候如果你設置一個目的地址爲60.xxx的規則有用嗎?呵呵,這是問題一。這個時候應該可以完成端口轉發的訪問了,但是有一些時候還是不行?爲什麼?看問題二。
第二,內網server的ip配置問題,這裏我們以web server爲例說明一下(ftp情況有一些特殊,下面我們再詳細討論,說確認包和關聯包的時候討論這個問題),上面說到,有的時候可以訪問了,有的時候卻不行,就是這個web server的ip設置問題了,如果web server沒有指定默認的網關,那麼在作了上面的配置之後,web server會收到internet的請求,但是,他不知道往哪裏回啊,人家的本地路由表不知道你那個internet的ip,202.1.1.1該怎麼走。如果你使用截包工具在web server上面察看,你會發現server收到了來自202.1.1.1:1333-->10.1.1.1:80的請求,由於你沒有給web server配置默認網關,它不知道怎麼回去,所以就出現了不通的情況。怎麼辦呢?兩個解決方法:一就是給這個server配置一個默認網關,當然要指向這個配置端口轉發的linux,本例是10.1.1.254,配置好了,就一定能訪問了。有一個疑問?難道不需要在FORWARD鏈上面設置一個允許 web server的ip地址訪問外網的規則嗎?它的包能出去?答案是肯定的,能出去。因爲我們那一條允許確認包與關聯包的規則,否則它是出不去的。第二種方法,比較麻煩一些,但是對服務器來說這樣似乎更安全一些。方法就是對這個包再作一次SNAT,也就是在POSTROUTING鏈上添加規則。命令如下:
iptables -t nat -A POSTROUTING -d 10.1.1.1 -p tcp --dport 80 -j SNAT --to 10.1.1.254
ftp的方法相同。這條命令不太好懂??其實很簡單,如果使用這條命令,那麼你的web server不需要再設置默認網關,就能收到這個請求,只要他和linux的lan ip地址是能互訪的(也就是說web server和Linux的Lan ip在一個廣播域),我們在根據上面的netfilter流程圖來分析這個包到底被我們怎麼樣了,首先一個請求202.1.1.1:1333--> 60.1.1.1:80被linux收到了,進入PREROUTING,發現一個規則(iptables -t nat -A PREROUTING -d 60.1.1.1 -p tcp --dport 80 -j DNAT --to 10.1.1.1:80)符合,好了,改你的目的地址,於是這個包變成了202.1.1.1:1333-->10.1.1.1:80,繼續往前走,進入FORWARD鏈,okay,也有一條規則允許通過(iptables -A FORWARD -d 10.1.1.1 -p tcp --dport 80 -j ACCEPT),進入route box選路,找到合適的路徑了,繼續進入POSTROUTING鏈,耶?又發現一個符合的規則(iptables -t nat -A POSTROUTING -d 10.1.1.1 -p tcp --dport 80 -j SNAT --to 10.1.1.254),原來是一個SNAT,改你的源地址,於是這個包變成了10.1.1.254:xxxx-->10.1.1.1:80。爲什麼用xxxx了,這裏的端口是隨機的,我也不知道會是什麼。而整個的兩次變化的過程都會記錄在linux的ip_conntrack中,當web server收到這個包的時候,發現,原來是一個內網自己兄弟來的請求阿,又在一個廣播域,不用找網關,把返回包直接扔給交換機了,linux在收到返回包之後,會根據他的ip_conntrack中的條目進行兩次變換,返回真正的internet用戶,於是完成這一次的訪問。


看了上面的兩個例子,不知道大家是否清楚了iptables的轉發流程,希望對大家有所幫助,下面我們就說說我一直在上面提到的關於那個 ESTABLISHED,RELATED的規則是怎麼回事,到底有什麼用處。說這個東西就要簡單說一下網絡的數據通訊的方式,我們知道,網絡的訪問是雙向的,也就是說一個Client與Server之間完成數據交換需要雙方的發包與收包。在netfilter中,有幾種狀態,也就是new, established,related,invalid。當一個客戶端,在本文例一中,內網的一臺機器訪問外網,我們設置了規則允許他出去,但是沒有設置允許回來的規則阿,怎麼完成訪問呢?這就是netfilter的狀態機制,當一個lan用戶通過這個linux訪問外網的時候,它發送了一個請求包,這個包的狀態是new,當外網回包的時候他的狀態就是established,所以,linux知道,哦,這個包是我的內網的一臺機器發出去的應答包,他就放行了。而外網試圖對內發起一個新的連接的時候,他的狀態是new,所以linux壓根不去理會它。這就是我們爲什麼要加這一句的原因。還有那個 related,他是一個關聯狀態,什麼會用到呢?tftp,ftp都會用到,因爲他們的傳輸機制決定了,它不像http訪問那樣,Client_IP: port-->server:80  然後server:80-->Client_IP:port,ftp使用tcp21建立連接,使用20端口發送數據,其中又有兩種方式,一種主動 active mode,一種被動passive mode,主動模式下,client使用port命令告訴server我用哪一個端口接受數據,然後server主動發起對這個端口的請求。被動模式下, server使用port命令告訴客戶端,它用那個端口監聽,然後客戶端發起對他的數據傳輸,所以這對於一個防火牆來說就是比較麻煩的事情,因爲有可能會有new狀態的數據包,但是它又是合理的請求,這個時候就用到這個related狀態了,他就是一種關聯,在linux中,有個叫 ftp_conntrack的模塊,它能識別port命令,然後對相應的端口進行放行。


一口氣寫了這麼多東西,不知道質量如何,大家湊和着看吧,希望多多交流共同進步,我還是一個linux的初學者,難免很多謬誤,希望高手賜教指正,以期不斷進步。


對了,還有幾個在實際中比較實用(也比較受用:-))的命令參數,寫出來供大家參考

iptables -L -n
這樣的列表會跳過linux的domain lookup,有的時候使用iptables -L會比較慢,因爲linux會嘗試解析ip的域名,真是羅嗦,如果你的dns server比較不爽的話,iptables -L就會讓你很不爽,加一個-n參數就好了。列表刷的就出來。當然了,如果你的linux就是做防火牆,建議把nameserver去掉,在 /etc/resolve.conf裏面,因爲有時候使用route命令也會比較慢列出來,很是不爽。

iptables -L -v
這個命令會顯示鏈中規則的包和流量計數,嘿嘿,看看哪些小子用的流量那麼多,用tc限了他。

cat /proc/net/ip_conntrack
查看目前的conntrack,可能會比較多哦,最好加一個|grep "關鍵字",看看你感興趣的鏈接跟蹤

wc -l /proc/net/ip_conntrack
看看總鏈接有多少條。

iptables-save >/etc/iptables
把當前的所有鏈備份一下,之所以放到/etc下面叫iptables,因爲這樣重起機器的時候會自動加載所有的鏈,經常地備份一下吧,否則如果鏈多,萬一掉電重啓,你還是會比較痛苦。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章