防火牆和iptables


基礎服務類系列文章:http://www.cnblogs.com/f-ck-need-u/p/7048359.html


本文介紹防火牆知識和Linux主機處理數據包的過程,同時介紹了iptables管理防火牆的方法。

6.1 為什么需要防火牆

對於沒有防火牆存在的一條網絡路線中,主機A發送給主機B的任何一個數據包,主機B都會照單全收,即使是包含了病毒、木馬等的數據也一樣會收。雖說害人之心不可有,但是在網絡上,你認為是害你的行為在對方眼中是利他的行為。所以防人之心定要有,防火牆就可以提供一定的保障。

有了簡單的防火牆之后,在數據傳輸的過程中就會接受"入關"檢查,能通過的數據包才繼續傳輸,不能通過的數據包則拒絕或者直接丟棄。

從上面的圖中可以看出,防火牆至少需要兩個網卡,其中一塊控制流入數據包,另一塊網卡控制流出數據包。即使是軟件防火牆,要實現完整的防火牆功能,也需要至少兩塊網卡。

所謂防火牆就是"防火的牆",如果過來的是"火"就得擋住,如果過來的不是"火"就放行,但什么是"火",這由人們自行定制。

但無論如何,所謂的"火"都是基於OSI七層模型的,簡單的划分為四層:最高的應用層(如HTTP/FTP/SMTP),往下一層是傳輸層(TCP/UDP),再往下一層是網絡層,最后是鏈路層。可以基於整個7層模型的每一層來定制防火牆,但是默認防火牆(沒有編譯內核源碼定制七層防火牆)一般認為工作在以上的4層中。

在網上和很多書籍上都詳細解釋了OSI七層模型各層的行為和作用,我個人推薦一本《計算機網絡原理創新教程》,里面的OSI模型是我學習過最通俗易懂且完整詳細的教程。

6.2 數據傳輸流程

6.2.1 網絡數據傳輸過程

首先看看網絡數據傳輸的基本流程。

數據從上層進入到傳輸層,加上源端口和目標端口成為數據段(如果是UDP則成為數據報),再進入網絡層加上源IP和目標IP成為數據包,再進入鏈路層加上源MAC地址和目標MAC地址成為數據幀,這段過程是一種"加頭"封裝數據的過程。數據經過網絡傳輸到達目標主機后,逐層"剃頭"解包,最終得到data純數據內容。

6.2.2 本機數據路由決策

其實,進程間數據傳輸的方式有多種:共享內存、命名管道、套接字、消息隊列、信號量等。上面描述的OSI通信模型只是數據傳輸的一種方式,它特指網絡數據傳輸,是基於套接字(ip+port)的,所以既可以是主機間進程通信,也可以是本機服務端和客戶端進程間的通信。

無論如何,網絡數據總是會流入、流出的,即使是本機的客戶端和服務端進程間通信,也需要從一個套接字流出、另一個套接字流入,只不過這些數據無需路由、無需經過物理網卡(走的是LoopBack)。當接收外界發送的數據時,在數據從網卡流入后需要對它做路由決策,根據其目標決定是流入本機數據還是轉發給其他主機,如果是流入本機的數據,則數據會從內核空間進入用戶空間(被應用程序接收、處理)。當用戶空間響應(應用程序生成新的數據包)時,響應數據包是本機產生的新數據,在響應包流出之前,需要做路由決策,根據目標決定從哪個網卡流出。如果不是流入本機的,而是要轉發給其他主機的,則必然涉及到另一個流出網卡,此時數據包必須從流入網卡完整地轉發給流出網卡,這要求Linux主機能夠完成這樣的轉發。但Linux主機默認未開啟ip_forward功能,這使得數據包無法轉發而被丟棄。Linux主機和路由器不同,路由器本身就是為了轉發數據包,所以路由器內部默認就能在不同網卡間轉發數據包,而Linux主機默認則不能轉發。

下圖可以很好地解釋上面的過程:

本文是為了介紹防火牆的,充當防火牆的主機需要至少兩塊網卡,所以有必要解釋下數據流入和流出時,Linux主機是如何處理的。

首先要說明的是,IP地址是屬於內核的(不僅如此,整個tcp/ip協議棧都屬於內核,包括端口號)只要能和其中一個地址通信,就能和另一個地址通信(這么說不嚴謹,准確地說是能路由這兩個地址),而不管是否開啟了數據包轉發功能。例如某Linux主機有兩網卡eth0:172.16.10.5和eth1:192.168.100.20,某192.168.100.22主機網關指向192.168.100.20,它能ping通192.168.100.20,但也一樣能ping通172.16.10.5,因為地址屬於內核,從eth1進來的數據包被內核分析時,發現目標地址為本機地址,直接就產生新數據包回應192.168.100.22,根據路由決策,該響應包應從eth1出去,於是192.168.100.22能收到回復完成整個ping過程。

在此過程中,沒有進行數據包轉發過程,因為流出的響應包是新產生的,而非原來流入的數據包。如果流入和流出的包是一樣的(或者稍作修改),則數據流入后不能進入用戶空間,而是直接通過內核轉發給另一個網卡。數據包從網卡1交給網卡2,這個過程就是轉發,在Linux主機上由ip_forward進行控制。例如,網卡1所在網段主機ping網卡2所在主機時,數據包流入網卡1后就需要轉交給網卡2,然后從網卡2流出。

在后文中有實驗專門測試和說明上面的過程:配置網關以及轉發

6.3 TCP三次握手、四次揮手以及syn攻擊

每次TCP會話的建立都需要經過三次握手,斷開時都需要四次揮手。

6.3.1 三次握手建立TCP連接

如圖。

(1).客戶端和服務端都處於CLOSED狀態。(發起TCP請求的稱為客戶端,接受請求的稱為服務端)

(2).服務端打開服務端口,處於listen狀態。

(3).客戶端發起連接請求。首先發送SYN(synchronous)報文給服務端,等待服務端給出ACK報文回應。發送的SYN=1,ACK=0,表示只發送了SYN信號。此時客戶端處於SYN-SENT狀態(SYN信號已發送)。

(4).服務端收到SYN信號后,發出ACK報文回應,並同時發出自己的SYN信號請求連接。此時服務端處於SYN-RECV狀態(syn recieved,在圖中顯示的是SYN-RCVD)。發送的SYN=1 ACK=1,表示發送了SYN+ACK。

(5).客戶端收到服務端的確認信號ACK后,再次發送ACK信號給服務端以回復服務端發送的syn。此時客戶端進入ESTABLISHED狀態,發送的SYN=0,ACK=1表示只發送了ACK。

(6).服務端收到ACK信號后,也進入ESTABLISHED狀態。

此后進行數據的傳輸都通過此連接進行。其中第3、4、5步是三次握手的過程。這個過程通俗地說就是雙方請求並回應的過程:A發送syn請求B並等待B回應;B回應A,並同時請求A;A回應B。

6.3.2 四次揮手斷開TCP連接

斷開之前,雙方都處於ESTABLISHED狀態。假設是客戶端請求斷開連接。

(1).客戶端發送FIN(finally)報文信號,請求斷開。此后客戶端進入FIN-WAIT-1狀態。

(2).服務端收到FIN信號,給出確認信號ACK,表示同意斷開。此時服務端進入CLOSE-WAIT狀態。此過程結束后表示從客戶端到服務端方向的TCP連接已經關閉了,也就是說整個TCP連接處於半關閉狀態。

(3).客戶端收到服務端的ACK后進入FIN-WAIT-2狀態,以等待服務端發出斷開信號FIN。在客戶端的FIN-WAIT-2狀態的等待過程中,服務端再發出自己的FIN信號給客戶端。此時服務端進入LAST-ACK狀態。

(4).客戶端收到服務端的FIN信號,給出回應信號ACK,表示接受服務端的斷開請求,此時客戶端進入TIME-WAIT狀態,此時客戶端已脫離整個TCP,只需再等待一段時間(2*MSL)就自動進入CLOSED狀態。

(5).服務端收到客戶端的回應ACK信號,知道客戶端同意了服務端到客戶端方向的TCP斷開,直接進入CLOSED狀態。

以上的1、2、3、4步是四次揮手階段。從中可以看出,四次揮手和三次握手的過程其實是類似的,都是雙方發出斷開請求並回應對方,只不過四次揮手的過程是將服務端發送的ACK和FIN分開發送了,而三次握手的過程中服務端發送的ACK和SYN是放在一個數據包內發送的。

以上所述是客戶端請求的斷開,服務端也可以請求斷開,這時過程是完全一致的,只不過角色互換了。還需注意的是,如果是客戶端請求斷開,那么服務端就是被動斷開端,可能會保留大量的CLOSE-WAIT狀態的連接,如果是服務端主動請求斷開,則可能會保留大量的TIME_WAIT狀態的連接。由於每個連接都需要占用一個文件描述符,高並發情況下可能會耗盡這些資源。因此,需要找出對應問題,做出對應的防治,一般來說,可以修改內核配置文件/etc/sysctl.conf來解決一部分問題。

6.3.3 syn flood攻擊

syn洪水攻擊是一種常見的DDos攻擊手段。攻擊者可以通過工具在極短時間內偽造大量隨機不存在的ip向服務器指定端口發送tcp連接請求,也就是發送了大量syn=1 ack=0的數據包,當服務器收到了該數據包后會回復並同樣發送syn請求tcp連接,也就是發送ack=1 syn=1的數據包,此后服務器進入SYN-RECV狀態,正常情況下,服務器期待收到客戶端的ACK回復。但問題是服務器回復的目標ip是不存在的,所以回復的數據包總被丟棄,也一直無法收到ACK回復,於是不斷重發ack=1 syn=1的回復包直至超時。

在服務器被syn flood攻擊時,由於不斷收到大量偽造的syn=1 ack=0請求包,它們將長時間占用資源隊列,使得正常的SYN請求無法得到正確處理,而且服務器一直處於重傳響應包的狀態,使得cpu資源也被消耗。總之,syn flood攻擊會大量消耗網絡帶寬和cpu以及內存資源,使得服務器運行緩慢,嚴重時可能會引起網絡堵塞甚至系統癱瘓。

因此,防范syn flood攻擊非常重要。當然,首先需要判斷出是否受到了syn flood攻擊。可以通過抓包工具或者netstat等工具獲取處於SYN_RECV狀態的半連接,如果有大量處於SYN_RECV且源地址都是亂七八糟的,說明受到了syn洪水攻擊。

例如使用netstat工具判斷的方法如下:

[root@xuexi ~]# netstat -tnlpa | grep tcp | awk '{print $6}' | sort | uniq -c
      1 ESTABLISHED
      7 LISTEN
    256 SYN_RECV

6.4 防火牆的判斷范圍

從設備上分類,防火牆分為軟件防火牆、硬件防火牆、芯片級防火牆。后文所說的可能是軟件防火牆、也可能是硬件防火牆,在理解上它們沒什么區別,只是將防火牆剝離成了獨自的服務器而已。

從技術上分類,防火牆分為數據包過濾型防火牆、應用代理型防火牆。這是因為四層模型的每一層都可以應用防火牆。

6.4.1 從鏈路層來判斷是否處理

基於鏈路層的防火牆是控制MAC的。例如,可以將公司內網員工電腦的MAC地址全部記錄到防火牆上,從而限制他們上外網。再例如,可以將公司電腦的MAC地址全部記錄到防火牆使他們能夠上網,但是非本公司的電腦就無法從本公司上網。

但是,基本上不會有公司這樣做,這樣的行為太死板,而且記錄MAC地址本身就是一件很麻煩的事。

6.4.2 從網絡層來判斷是否處理

網絡層的核心是IP(也包括icmp等)。所以從網絡層來判斷,可以基於源IP、目標IP來指定防火牆的規則。例如,來自38.68.100.61的主機不能穿過防火牆;訪問目標是192.168.109.19的服務器的請求不能讓其穿過防火牆;還可以設置icmp協議作為判斷依據,使得外網人員的ping包被擋住。

在網絡層可以用來制定防火牆規則的內容有很多。如下表。最常用的也就是后三個而已。

6.4.3 從傳輸層來判斷是否處理

可以從TCP或者UDP來判斷。以TCP為例,例如限制目標端口是22端口的請求,這樣SSH就無法連接上服務器了。

下表是TCP數據包中可以用來制定防火牆規則的字段。

6.4.4 從應用層來判斷是否處理

到了這一層的處理就屬於應用代理型的防火牆了。他需要解開數據包並還原數據,也就是說它可以獲取到數據包中的所有內容,但也因此負擔很重,所需CPU和內存較大。它的適用面較窄。

6.4.5 特殊的防火牆判斷

除了以上4種判定方式,還有幾種特殊的判斷方式也較為常用。

◇ 根據數據包內容判斷

例如,不允許內網的客戶端連上taobao.com上的任何主機,可以在防火牆上檢查DNS的解析包中是否包含"taobao.com"這個字符串,如果有就丟棄,這樣就可以讓DNS對任何taobao.com上的主機解析失敗達到限制上該網的目的。

注意:雖說數據部分是應用層的,但是有些防火牆在網絡層就可以進行檢查。Linux默認自帶的iptables/netfilters就是其中一種。

◇ 根據關聯狀態判斷

假如現在不允許任何internet上的主機進入到公司內部,但是允許企業內的計算機可以上網。這樣的設定目的是為了防止來自外網的攻擊。但是如果"禁止源地址為外網的所有地址穿過防火牆、允許源地址為公司內部的地址穿過防火牆"來設置防火牆,將導致一個問題:內網連上internet后請求某個網頁,要能正常上網,內網計算機必然要接收外網網頁的返回數據。但外網數據包無法穿過防火牆,這樣並沒有實現內網主機上網的目的。

現在假設內網某機器上外網時的套接字為192.168.100.8:9000,想要訪問10.0.0.5:80,也即是說數據流向是192.168.100.8:9000→10.0.0.5:80,那么返回的數據包流向必定是10.0.0.5:80→192.168.100.8:9000。根據這種關聯性,防火牆可以設定允許這樣的數據包通過。

這屬於連接跟蹤的行為。FTP服務器對於防火牆的設置是一個考驗,如果沒有連接跟蹤的功能,數據通道的端口不固定性將導致防火牆設置極其困難。

6.4.6 數據包過濾

以下是協議棧(TCP/IP協議棧)底層大致機制。數據包都要通過A流入,再根據路由決策決定數據包的流向(網絡層)。如果是流入本機,則經過B進入用戶空間層,對於每個從本機流出的數據包,也都要經過路由決策來決定從哪個網絡接口出去,然后路經D,從E出去。如果不是流入本機則流向C,然后順着E點出去。

上圖有一處容易理解錯誤,從用戶空間層出去的數據包是本機新產生的數據包,可能是對流入數據包的響應數據,也可能是本機應用向外發出的請求數據包。總之,數據包走不完A-->B點-->User Space-->D-->E這條路,在數據包進入用戶空間層時已經被處理了,從用戶空間層出來的數據已經不是原流入的數據,所以在上圖中我將用戶空間的兩個箭頭斷開了。

用英文來說明ABCDE這5個點就比較淺顯易懂:

A表示:altering packets as soon as they come in,

B表示:destined to local sockets,

C表示:be routed through the box,

D表示:locally-generated packets,and altering before routing(注:這個before routing是指路由出去之前,而不是路由決策之前),

E表示:altering packets as they are about to go out。

在上圖中的ABCDE這五個點,其實就是防火牆發揮作用的點。在Linux主機上,防火牆是由內核空間的netfilter實現的,其中作用在ABCDE這五個點的分別稱為PREROUTING鏈、INPUT鏈、FORWARD鏈、OUTPUT鏈和POSTROUTING鏈。這幾個術語在后文會做非常詳細的說明。

注意:是先經過OUTPUT,還是先經過路由決策,我也不確定,我多次查找過相關資料,幾乎總都能找到這兩種說法的"權威"說明,比如在iptables.info中給出的iptable packets flow圖中表明先經過OUTPUT再經過路由決策,而iptables wiki文檔中給出的圖則表明先經過路由決策再流經OUTPUT:https://upload.wikimedia.org/wikipedia/commons/3/37/Netfilter-packet-flow.svg。


6.4.7 iptables和Netfilter的關系

防火牆起作用的是Netfilter,而iptables只是管理控制netfilter的工具,可以使用該工具進行相關規則的制定以及其他的動作。iptables是用戶層的程序,netfilter是內核空間的,在netfilter剛加入到Linux中時,netfilter是一個Linux的一個內核模塊,要實現其他的防火牆行為還需要加載其他對應的模塊,到了后來netfilter一部分必須的模塊已經加入到內核中了。

也就是說,iptables命令工具操作的netfilter,真正起"防火"作用的是netfilter。

6.5 Linux上防火牆相關基礎

6.5.1 netfilter與其模塊

Linux是一個極其模塊化的內核。netfilter也是以模塊化的形式存在於Linux中,所以每添加一個和netfilter相關的模塊,代表着netfilter就多一個功能。

但是有些模塊是使用netfilter所必須的,所以這些模塊已經默認編譯到內核中而非需要時加載。

存放netfilter模塊的目錄有三個:/lib/modules/$kernel_ver/net/{netfilter,ipv4/netfilter,ipv6/netfilter}。$kernel_ver代表內核版本號。

其中ipv4/netfilter/存放的ipv4的netfilter,ipv6/netfilter/存放的ipv6的netfilter,/lib/modules/$kernel_net/kernel/netnetfilter/存放的是同時滿足ipv4和ipv6的netfilter。在最后一個目錄中放入更多的模塊,是netfilter團隊發展的目標,因為要維護ipv4和ipv6兩個版本挺累的。

6.5.2 netfilter的結構

要使netfilter能夠工作,就需要將所有的規則讀入內存中。netfilter自己維護一個內存塊,在此內存塊中有4個表:filter表、NAT表、mangle表和raw表。在每個表中有相應的鏈,鏈中存放的是一條條的規則,規則就是過濾防火的語句或者其他功能的語句。也就是說表是鏈的容器,鏈是規則的容器。實際上,每個鏈都只是一個hook函數(鈎子函數)而已。

說到這里,需要糾正一個概念,Linux上的防火牆是由netfilter實現的,但是netfilter的功能不僅僅只有"防火",一般可以認為"防火"的功能只是filter表的功能。

關於這4個表,它們的結構如下:

注:從內核2.6.34開始,NAT表支持操作INPUT鏈。它只為SNAT服務。和snat on postrouting類似,只不過snat on input用來轉換"目標是本機的數據包"的源地址。

◇ filter表:netfilter中最重要的表,負責過濾數據包,也就是防火牆實現"防火"的功能。filter表中只有OUTPUT/FORWARD/INPUT鏈。

◇ NAT表:實現網絡地址轉換的表。可以轉換源地址、源端口、目標地址、目標端口。NAT表中的鏈是PREROUTING/POSTROUTING/OUTPUT。

◇ mangle表:一種特殊的表,通過mangle表可以實現數據包的拆分和還原。mangle表中包含所有的鏈。

◇ raw表:加速數據包穿過防火牆的表,也就是增強防火牆性能的表。只有PREROUTING/OUTPUT表。

由於這幾個表中有重復的鏈,所以數據被不同鏈中規則處理時是由順序的。下圖是完整的數據包處理流程。

6.5.3 INPUT、OUTPUT、FORWARD鏈

每個鏈對應的都是同名稱的數據包,如INPUT鏈針對的是INPUT數據包。

INPUT鏈的作用是為了保護本機。例如,如果進入的數據包的目標是本機的80端口,且發來數據包的地址為192.168.100.9時則丟棄,這樣的規則應該寫入本機的INPUT鏈。但是要注意,這個本機指的是防火牆所在的機器,如果是硬件防火牆,那么一定會配合FORWARD鏈。

OUTPUT鏈的作用是為了管制本機。例如,限制瀏覽www.taobao.com網頁。

INPUT和OUTPUT鏈很容易理解,FORWARD鏈起的是什么作用呢?數據包從一端流入,但是不經過本機,那么就要從另一端流出。對於硬件防火牆這很容易理解,如下圖,數據總要轉發到另一個網卡接口然后進入防火牆負責為其"防火"的網段。也就是說,FORWARD鏈的作用是保護"后端"的機器。

前文說過,Linux主機自身也有ip_forward功能用於網卡間的數據包轉發,這個ip_forward和netfilter的forward鏈有什么區別呢?這就是防火牆的意義,當數據包需要轉發時,僅使用ip_forward時將不問是非對錯總是進行轉發,而使用forward鏈時可以篩選這些需要轉發的數據包,以決定是否要被轉發。顯然,ip_forward的功能是轉發數據包,而forward鏈是篩選允許轉發的數據包,然后讓ip_forward轉發。這也說明forward鏈要正常工作,要求開啟ip_forward功能。

6.5.4 防火牆布線示例

這樣的布置不僅給了服務器對外的一層防火牆,也給了服務器對內的一層防火牆,可以防外人也可以防內賊。

圖中的DMZ稱為"非軍事區",一般是放在兩個防火牆中間做內網和外網的緩沖。有些必須對外提供服務的服務器應該放在這種區域,而不能直接放在內網,因為將其放入內網又對外提供服務,當它被外界攻擊時就可以借助它(肉機)作為跳板攻陷其它內網主機。

一般構建DMZ區有以下幾個要求:

  1. 內網可以訪問外網。顯然,內網用戶需要自由地訪問外網。
  2. 內網僅部分主機可以訪問DMZ,此策略是為了方便內網用戶使用和管理DMZ中的服務器。不應該全部放行內網訪問DMZ,否則有內賊的風險。
  3. 外網不能訪問內網。內網中存放的是公司內部數據,這些數據不允許外網的用戶進行訪問。
  4. 外網可以訪問DMZ。DMZ中的服務器本身就是要給外界提供服務的。
  5. DMZ不能訪問內網。如果違背此策略,則當入侵者攻陷DMZ時,就可以進一步進攻到內網的重要數據。

6.6 filter表

只考慮filter表的時候,防火牆處理數據包的過程如下圖。

當數據包流入時,首先經過路由判決該數據包是流入防火牆本機還是轉發到其他機器的。對於流入本機的數據包經過INPUT鏈,INPUT鏈所在位置是一個鈎子函數,數據經過時將被鈎子函數檢查一番,檢查后如果發現是被明確拒絕或需要丟棄的則直接丟棄,明確通過的則放行,不符合匹配規則的同樣丟棄。但是鈎子函數畢竟是半路劫財的角色,所以不管怎么樣都要告訴一聲netfilter,說這個數據包是怎樣怎樣的,即使是拒絕或丟棄了數據包也還是會給出一個通知。

當用戶空間產生的數據包要發送出去時,首先經過OUTPUT鏈並被此處的鈎子函數檢查一番,檢查通過則放行,然后經過路由決策判決從哪個接口出去,並最終從網卡流出。

例如,當外界主機請求WEB服務的時候,請求數據包流入到本機,路經INPUT鏈被放行。進入到本機用戶空間的進程后,本機進程根據請求給出響應報文,該響應報文的源地址是本機,目標地址是外界主機。當響應報文要出去時必須先流經OUTPUT,然后經過路由決定從哪個接口出去,最終流出並路由到外界主機。

再例如ping本機地址(如127.0.0.1)的時候,ping請求從用戶空間發送后,數據包從用戶空間流出,於是經過路由決策發現是某網卡的地址,然后流經到OUTPUT,並從此網卡流出。但是由於ping的目標為本機地址,所以數據包仍從流出的網卡流入,並被INPUT鏈檢查,最后返回ping的結果信息。

6.7 iptables命令書寫規則

iptables用法比較復雜,有很多命令、選項和參數。所以,我先絕大多數命令、選項和模塊選項列出,然后再舉例說明iptables命令的用法。

Usage: iptables [-t TABLE] COMMAND CHAIN [ expressions -j target ]

這表示要操作TABLE表中的鏈,操作動作由COMMAND決定,例如添加一條規則、刪除一條規則、列出規則列表等。如果是向鏈中增加規則,則需要寫出規則表達式用來檢查數據包,並指明數據包被規則匹配上時應該做什么操作,例如允許該數據包ACCEPT、拒絕該數據包REJECT、丟棄該數據包DROP,這些操作稱為target,由"-j"選項來指定。

 6.7.1 iptables語法

Commands:
Either long or short options are allowed.
  --append  -A chain                       鏈尾部追加一條規則
  --delete  -D chain                       從鏈中刪除能匹配到的規則
  --delete  -D chain rulenum               從鏈中刪除第幾條鏈,從1開始計算
  --insert  -I chain [rulenum]             向鏈中插入一條規則使其成為第rulenum條規則,從1開始計算
  --replace -R chain rulenum               替換鏈中的地rulenum條規則,從1開始計算
  --list    -L [chain [rulenum]]           列出某條鏈或所有鏈中的規則
  --list-rules -S [chain [rulenum]]        打印出鏈中或所有鏈中的規則
  --flush   -F [chain]                     刪除指定鏈或所有鏈中的所有規則
  --zero    -Z [chain [rulenum]]           置零指定鏈或所有鏈的規則計數器
  --new     -N chain                       創建一條用戶自定義的鏈
  --delete-chain  -X [chain]               刪除用戶自定義的鏈
  --policy  -P chain target                設置指定鏈的默認策略(policy)為指定的target
  --rename-chain  -E old new               重命名鏈名稱,從old到new

Options:
[!] --proto  -p proto                      指定要檢查哪個協議的數據包:可以是協議代碼也可以是協議名稱,
                                           如tcp,udp,icmp等。協議名和代碼對應關系存放在/etc/protocols中
                                           省略該選項時默認檢查所有協議的數據包,等價於all和協議代碼0
[!] --source -s address[/mask][...]        指定檢查數據包的源地址,或者使用"--src"
[!] --destination -d address[/mask][...]   指定檢查數據包的目標地址,或者使用"--dst"
[!] --in-interface  -i input name[+]       指定數據包流入接口,若接口名后加"+",表示匹配該接口開頭的所有接口
[!] --out-interface -o output name[+]      指定數據包流出接口,若接口名后加"+",表示匹配該接口開頭的所有接口
  --jump        -j target                  為規則指定要做的target動作,例如數據包匹配上規則時將要如何處理
  --goto        -g chain                   直接跳轉到自定義鏈上
  --match       -m match                   指定擴展模塊
  --numeric     -n                         輸出數值格式的ip地址和端口號。默認會嘗試反解為主機名和端口號對應的服務名
  --table       -t table                   指定要操作的table,默認table為filter
  --verbose     -v                         輸出更詳細的信息
  --line-numbers                           當list規則時,同時輸出行號
  --exact       -x                         默認統計流量時是以1000為單位的,使用此選項則使用1024為單位

iptables支持extension匹配。支持兩種擴展匹配:使用"-p"時的隱式擴展和使用"-m"時的顯式擴展。根據指定的擴展,隨后可使用不同的選項。在指定擴展項的后面可使用"-h"來獲取該擴展項的語法幫助。

"-p"選項指定的是隱式擴展,用於指定協議類型,每種協議類型都有一些子選項。常見協議和子選項如下說明:

-p tcp 子選項
  子選項:
    [!] --source-port,--sport port[:port]
          指定源端口號或源端口范圍。指定端口范圍時格式為"range_start:range_end",最大范圍為0:65535。
    [!] --destination-port,--dport port[:port]
          指定目標端口號或目標端口號范圍。
    [!] --tcp-flags mask comp
          匹配已指定的tcp flags。mask指定的是需要檢查的flag列表,comp指定的是必須設置的flag。
          有效的flag值為:SYN ACK FIN RST URG PSH ALL NONE。
          如果以0和1來表示,意味着mask中comp指定的flag必須為1其余的必須為0。
          例如:iptables -A FORWARD -p tcp --tcp-flags SYN,ACK,FIN,RST SYN
          表示只匹配設置了SYN=1而ACK、FIN和RST都為0數據包,也即只匹配TCP三次握手的第一次握手。
    [!] --syn
         是"--tcp-flags SYN,ACK,FIN,RST SYN"的簡寫格式。

-p udp 子選項
  子選項:
    [!] --source-port,--sport port[:port]
          指定源端口號或源端口范圍。指定端口范圍時格式為"range_start:range_end",最大范圍為0:65535。
    [!] --destination-port,--dport port[:port]
          指定目標端口號或目標端口號范圍。

-p icmp 子選項
  子選項:
    [!] --icmp-type {type[/code]|typename}
          用於指定ICMP類型,可以是ICMP類型的數值代碼或類型名稱。有效的ICMP類型
          可由iptables -p icmp -h獲取。常用的是"echo-request""echo-reply",分別
          表示ping和pong,數值代號分別是8和0
          ping時先請求后響應:ping別人先出去8后進來0;別人ping自己,先進來8后出去0

"-m"選項指定的是顯式擴展。其實隱式擴展也是要指定擴展名的,只不過默認已經知道所使用的擴展,於是可以省略。例如:-p tcp --dport =  -p tcp -m tcp --dport。

常用的擴展和它們常用的選項如下:

(1).iprange:匹配給定的IP地址范圍。

[!] --src-range from[-to]:匹配給定的源地址范圍

[!] --dst-range from[-to]:匹配給定的目標地址范圍

(2).multiport:離散的多端口匹配模塊,如將21、22、80三個端口的規則合並成一條。

最多支持寫15個端口,其中"555:999"算2個端口。只有指定了-p tcp或-p udp時該選項才生效。

[!] --source-ports,--sports port[,port|,port:port]...

[!] --destination-ports,--dports port[,port|,port:port]...

[!] --ports port[,port|,port:port]... :不區分源和目標,只要是端口就行

(3).state:狀態擴展。結合ip_conntrack追蹤會話的狀態。

[!] --state state

其中state有如下4種:

  INVALID:非法連接(如syn=1 fin=1)

  ESTABLISHED:數據包處於已建立的連接中,它和連接的兩端都相關聯

  NEW:新建連接請求的數據包,且該數據包沒有和任何已有連接相關聯

  RELATED:表示數據包正在新建連接, 但它和已有連接是相關聯的(如被動模式的ftp的命令連接和數據連接)

例如:-m state --state NEW,ESTABLISHED -j ACCEPT

關於這4個狀態,在下文還有更詳細的描述。

(4).string:匹配報文中的字符串。

--algo {kmp|bm}:兩種算法,隨便指定一種

--string "string_pattern"

如:

iptables -A OUTPUT -m string --algo bm --sting "taobao.com" -j DROP

(5).mac:匹配MAC地址,格式必須為XX:XX:XX:XX:XX:XX。

[!] --mac-source address

(6).limit:使用令牌桶(token bucket)來限制過濾連接請求數。

--limit RATE[/second/minute/hour/day]:允許的平均數量。如每分鍾允許10次ping,即6秒一次ping。默認為3/hour。

--limit-burst:允許第一次涌進的並發數量。第一次涌進超出后就按RATE指定數來給出響應。默認值為5。

例如:允許每分鍾6次ping,但第一次可以ping 10次。10次之后按照RATE計算。所以,前10個ping包每秒能正常返回,從第11個ping包開始,每10秒允許一次ping:iptables -A INPUT -d ServerIP -p icmp --icmp-type 8 -m limit --limit 6/minute --limit-burst 10 -j ACCEPT

(7).connlimit:限制每個客戶端的連接上限。

--connlimit-above n:連接數量高於上限n個時就執行TARGET

如最多只允許某ssh客戶端建立3個ssh連接,超出就拒絕。兩種寫法:

iptables -A INPUT -d ServerIP -p tcp --dport 22 -m connlimit --connlimit-above 3 -j DROP
iptables -A INPUT -d ServerIP -p tcp --dport 22 -m connlimit ! --connlimit-above 3 -j  ACCEPT

這個模塊雖然限制能力不錯,但要根據環境計算出網頁正常訪問時需要建立的連接數,另外還要考慮使用NAT轉換地址時連接數會翻倍的問題。

 

最后剩下"-j"指定的target還未說明,target表示對匹配到的數據包要做什么處理,比如丟棄DROP、拒絕REJECT、接受ACCEPT等,除這3個target外,還支持很多種target。以下是其中幾種:

DNAT:目標地址轉換

SNAT:源地址轉換

REDIRECT:端口重定向

MASQUERADE:地址偽裝(其實也是源地址轉換)

RETURN:用於自定義鏈,自定義鏈中匹配完畢后返回到自定義的前一個鏈中繼續向下匹配

6.7.2 ip_conntrack功能和iptstate命令

ip_conntrack提供追蹤功能,后來改稱為nf_conntrack,由nf_conntrack模塊提供。

只要一加載該模塊,/proc/net/nf_conntrack文件中就會記錄下追蹤的連接狀態。雖然會追蹤TCP/UDP/ICMP的所有連接,但是在此文件中只保存tcp的連接狀態。

[root@mail ~]# cat /proc/net/nf_conntrack
ipv4     2 tcp      6 431714 ESTABLISHED src=192.168.100.1 dst=192.168.100.8 sport=1586 dport=22 src=192.168.100.8 dst=192.168.100.1 sport=22 dport=1586 [ASSURED] mark=0 secmark=0 use=2
ipv4     2 tcp      6 427822 ESTABLISHED src=192.168.100.8 dst=192.168.100.1 sport=22 dport=1343 src=192.168.100.1 dst=192.168.100.8 sport=1343 dport=22 [ASSURED] mark=0 secmark=0 use=2
ipv4     2 tcp      6 299 ESTABLISHED src=192.168.100.1 dst=192.168.100.8 sport=1608 dport=22 src=192.168.100.8 dst=192.168.100.1 sport=22 dport=1608 [ASSURED] mark=0 secmark=0 use=2

第一條顯示的是ESTABLISHED狀態的連接,該連接是192.168.100.1:1586-->192.168.100.8:22,以及返回的連接192.168.100.8:22 --> 192.168.100.1:1586。

也可以使用iptstate命令實時顯示連接狀態,它是像top工具一樣的顯示。該命令工具在iptstate包中,可能需要手動安裝,。如圖為iptstate的一次結果。

可以發現TTL(超時時間)值以及狀態,還有其他的一些信息。這些TTL值的設置位置都在/proc/sys/net/netfilter/目錄下的文件中。例如上圖中web服務TIME_WAIT的超時時間為2分鍾,可以將其修改,對應的文件為下圖中標示的。可以直接修改該文件的值,如180秒,即等待TIME_WAIT的時間3分鍾后就斷開連接。(TIME_WAIT處於TCP連接4次揮手主動段開方的倒數第二個階段)

nf_conntrack好處是大大的,但是很悲劇,每一個監控和追蹤的工具都是消耗性能的,而nf_conntrack也有其瓶頸所在。nf_conntrack也會消耗一定的資源,所以在設計的時候默認給出了其最大的追蹤數量,最大追蹤數量值由/proc/sys/net/netfilter/nf_conntrack_max文件決定。默認是31384個。這個值顯然是無法滿足較高並發量的服務器的,所以可以將其增大一些,否則追蹤數達到了最大值后,后續的所有連接都將排隊被阻塞,可能會因此給出警告。但是無論如何要明白的是追蹤是會消耗性能的,所以該值應該酌情考慮。

[root@mail ~]# cat /proc/sys/net/netfilter/nf_conntrack_max
31384

並且要注意的一點是,nf_conntrack模塊不是一定需要顯式裝載才會被裝載的,有些依賴它的模塊被裝載時該模塊也會被裝載。例如iptables命令中包含iptables -t nat時,就會裝載該模塊自動開啟追蹤,進而可能導致達到追蹤max值而出錯。

6.7.3 -m state的狀態解釋

使用-m state表示使用簡稱為"state"的模塊。該模塊提供4種狀態:NEW、ESTABLISHED、RELATED和INVALID。但是這些狀態和TCP三次握手四次揮手的十幾種狀態沒任何關系。而且state提供的4種狀態對於tcp/udp/icmp類型的數據包都是通用的。

注意:這四種狀態是數據包的狀態,不是客戶端或者服務器當時所處的狀態也可以認為是防火牆state模塊的狀態,因為state模塊在收到對應狀態的包時會設置為相同的狀態。

(1).NEW狀態與TCP/UDP/ICMP數據包的關系

為了建立一條連接,發送的第一個數據包(如tcp三次握手的第一次SYN數據包)的狀態為NEW。如果第一次連接沒建立成功,則第二個繼續請求的數據包已經不是NEW數據包了。

所以,如果不允許NEW狀態的數據包表示不允許主動和對方建立連接,也不允許外界和本機建立連接。

(2).ESTABLISHED狀態與tcp/udp/icmp數據包的關系

無論是tcp數據包、udp數據包還是icmp數據包,只要發送的請求數據包穿過了防火牆,那么接下來雙方傳輸的數據包狀態都是ESTABLISHED,也就是說發過去的和返回回來的都是ESTABLISHED狀態數據包。

(3).RELATED數據包的解釋

對於RELATED數據包的解釋是:與當前任何連接都無關,完全是被動或臨時建立的連接之間傳輸的數據包。

例如,下圖中tracert發送數據包的探測過程。

圖中客戶端為了探測服務器的地址發送了tracert命令。這個命令首先會標記一個tcp數據包的TTL值為1,當數據包到達第一個路由器該值就減1,所以TTL變為0表示該數據包到了壽終正寢該DROP掉的時候,然后該路由器就會發送一個icmp數據包(icmp-type=11)返回給客戶端,這樣客戶端就知道了第一個路由器的地址。然后客戶端的tracert命令繼續標記一個TTL為2的數據包向外發送,直到第二個路由器才被丟棄,第二個路由器又發送一個icmp包給客戶端,這樣客戶端就知道了第二個路由器的地址。同理第三次也一樣。

在tracert探測的過程中,由路由器返回的icmp包都是RELATED狀態的數據包。因為可以肯定的說,客戶端發送給路由器的tcp數據包是走的一條連接,數據包被路由器丟棄后路由器發送的icmp數據包與原來的連接已經無關了,這是另外一條返回的連接。但是之所以有這個數據包,完全是因為前面的連接結束而產生的應答數據包。

不過RELATED狀態和協議無關,只要數據包是因為本機先送出一個數據包而導致另一條連接的產生,那么這個新連接的所有數據包都屬於RELATED狀態的數據包。

這樣就容易理解ftp被動模式設置的related狀態了。在ftp服務器上的21號端口上開啟了命令通道(也就是命令連接)后,以后無論是被動模式的隨機數據端口還是主動模式的固定20數據端口,可以肯定的是數據通道的建立是由命令通道指定要開啟的,所以這個數據通道中傳輸的數據包都是RELATED狀態的。

(4).INVALID狀態的數據包

所謂的INVALID狀態,就是惡意的數據包。只要不是ESTABLISHED、NEW、RELATED狀態的數據包,那么就一定是INVALID狀態。對於INVALID數據包最應該放在鏈中的第一條,以防止惡意的循環攻擊。

(5).網關式防火牆的NEW狀態、ESTABLISHED狀態和RELATED

網關式的防火牆擋在客戶端和服務器端中間,用於過濾或改變數據包,但是它的狀態卻不好判斷了。

關於它的狀態變化,可以總結為"牆頭草":客戶端送到防火牆的數據包是什么狀態,防火牆的state模塊就設置為什么狀態,轉發給服務器的數據包就是什么狀態;服務端發給防火牆的數據包是什么狀態,防火牆的state模塊就設置為什么狀態,轉發出去的數據包就是什么狀態。也就是說,防火牆並不改變數據包狀態的性質。

雖說數據包的狀態只有防火牆才有資格判斷,但是這樣歸納卻不妨礙理解。

例如,TCP三次握手的第一次,客戶端發送一個SYN數據包給服務器要建立連接,這個SYN數據包傳到防火牆上,防火牆的state模塊也會將自己的狀態設置為SYN_SENT,並認為這個數據包是NEW狀態的數據包,然后轉發給服務器,轉發過程的數據包的狀態也是NEW。當服務器收到SYN后應答一個SYN+ACK數據包,當SYN+ACK數據包到達防火牆時,防火牆也和服務器一樣將自己設置為SYN_RECV狀態,並認為這個數據包已經是ESTABLISHED的數據包了,然后將這個數據包以ESTABLISHED的狀態轉發給客戶端。

RELATED狀態也是一樣的,只要雙方的連接是"另起爐灶"的數據包,客戶端和服務端之間的防火牆會隨着數據包的流向而做一支"牆頭草"。

其實這些狀態以及轉變都會在/proc/net/nf_conntrack文件中記錄,只是比較難以被人為追蹤到。

6.7.4 filter-iptables命令示例

iptables實驗主機地址:172.16.10.9。首先啟動iptables。

service iptables start

(1).清空自定義鏈、清空規則、清空規則計數器。

[root@xuexi ~]# iptables -X
[root@xuexi ~]# iptables -F
[root@xuexi ~]# iptables -Z

(2).允許172.16.10.0網段連接ssh(端口22)。

[root@xuexi ~]# iptables -A INPUT -s 172.16.10.0/24 -d 172.16.10.9 -p tcp --dport 22 -j ACCEPT
[root@xuexi ~]# iptables -A OUTPUT -s 172.16.10.9 -p tcp --sport 22 -j ACCEPT

(3).設置filter表默認規則為DROP。

[root@xuexi ~]# iptables -P INPUT DROP
[root@xuexi ~]# iptables -P FORWARD DROP

一般防火牆對外是ACCEPT的,所以OUTPUT鏈采用默認的ACCEPT。

由於將INPUT鏈設置為全部DROP,因此除了前面設置的目標為22端口的數據包允許通過,其余全部丟棄,即使是ping環回地址。

[root@xuexi ~]# ping -c 4 127.0.0.1
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.

--- 127.0.0.1 ping statistics ---
4 packets transmitted, 0 received, 100% packet loss, time 13000ms

(4).查看規則列表和統計數據。

[root@xuexi ~]# iptables -L -n
Chain INPUT (policy DROP)
target     prot opt source               destination         
ACCEPT     tcp  --  172.16.10.0/24       172.16.10.9         tcp dpt:22 

Chain FORWARD (policy DROP)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         
ACCEPT     tcp  --  172.16.10.9          0.0.0.0/0           tcp spt:22

如果加上"-v"選項,則會顯示每條規則上的流量統計數據。

[root@xuexi ~]# iptables -L -n -v
Chain INPUT (policy DROP 10 packets, 993 bytes)
 pkts bytes target     prot opt in     out     source               destination         
  655 64963 ACCEPT     tcp  --  *      *       172.16.10.0/24       172.16.10.9         tcp dpt:22 

Chain FORWARD (policy DROP 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain OUTPUT (policy ACCEPT 9 packets, 756 bytes)
 pkts bytes target     prot opt in     out     source               destination         
  242 42857 ACCEPT     tcp  --  *      *       172.16.10.9          0.0.0.0/0           tcp spt:22

(3).放行環回設備的進出數據包(環回地址的放行很重要)。

[root@xuexi ~]# iptables -A INPUT -i lo -s 127.0.0.1 -d 127.0.0.1 -j ACCEPT
[root@xuexi ~]# iptables -A OUTPUT -o lo -s 127.0.0.1 -d 127.0.0.1 -j ACCEPT

此時已經可ping 127.0.0.1。

[root@xuexi ~]# ping 127.0.0.1
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.067 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.051 ms
^C
--- 127.0.0.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1648ms
rtt min/avg/max/mdev = 0.051/0.059/0.067/0.008 ms

但是不建議直接寫127.0.0.1,而是省略目標地址和源地址,因為ping本機ip地址最后交給環回設備但是不是交給127.0.0.1的,而是交給127.0.0網段的其他地址。所以應該這么寫:

[root@xuexi ~]# iptables -A INPUT -i lo -j ACCEPT
[root@xuexi ~]# iptables -A OUTPUT -o lo -j ACCEPT

所以可以將前面多余的規則刪除掉。

[root@xuexi ~]# iptables -D INPUT 2
[root@xuexi ~]# iptables -D OUTPUT 2

(4).能自己ping自己的IP,也能ping別人的IP,但是別人不能ping自己。

ping的過程實際上是ping請求對方,然后對方pong回應,協議類型為icmp。其中ping請求時,icmp類型為echo-request,數值代號為8,pong回應時的icmp類型為echo-reply,數值代號為0。

所以本機向外ping時,流出的是echo-request數據包,流入的是echo-reply數據包。而外界ping本機時,則是流入echo-request數據包,流出echo-reply數據包。因此,要允許本機向外ping,只需允許icmp-type=8的流出包、icmp-type=0的流入包即可,又由於前面的試驗中設置了INPUT和OUTPUT鏈的默認規則為DROP,所以外界主機無法ping本機。

[root@xuexi ~]# iptables -A OUTPUT -p icmp --icmp-type=8 -j ACCEPT
[root@xuexi ~]# iptables -A INPUT -p icmp --icmp-type=0 -j ACCEPT

當然,OUTPUT鏈本身就是放行所有數據包的,所以只需寫INPUT鏈規則即可。

(5).安裝httpd,讓外界能夠訪問web頁面(端口為80)。

[root@xuexi ~]# iptables -A INPUT -d 172.16.10.9 -p tcp --dport 80 -j ACCEPT
[root@xuexi ~]# iptables -A OUTPUT -s 172.16.10.9 -p tcp --sport 80 -j ACCEPT

(6).刪除(或替換)放行ssh服務和web服務的規則,並寫出基於ip_conntrack放行ssh和web的規則(進入的數據包的狀態只可能會是NEW和ESTABLISHED,出去的狀態只可能是ESTABLISHED)

放行ssh:

iptables -R INPUT 1 -s 172.16.10.0/24 -d 172.16.10.9 -p tcp --dport 22 -m state --state=NEW,ESTABLISHED -j ACCEPT
iptables -R OUTPUT 1 -s 172.16.10.9 -p tcp --sport 22 -m state --state=ESTABLISHED -j ACCEPT

放行web:

iptables -R INPUT 4 -d 172.16.10.9 -p tcp --dport 80 -m state --state=NEW,ESTABLISHED -j ACCEPT
iptables -R OUTPUT 4 -s 172.16.10.9 -p tcp --sport 80 -m state --state=ESTABLISHED -j ACCEPT
iptables -L -n --line-number
Chain INPUT (policy DROP)
num  target     prot opt source               destination         
1    ACCEPT     tcp  --  172.16.10.0/24       172.16.10.9         tcp dpt:22 state NEW,ESTABLISHED 
2    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0           
3    ACCEPT     icmp --  0.0.0.0/0            0.0.0.0/0           icmp type 0 
4    ACCEPT     tcp  --  0.0.0.0/0            172.16.10.9         tcp dpt:80 state NEW,ESTABLISHED 

Chain FORWARD (policy DROP)
num  target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
num  target     prot opt source               destination         
1    ACCEPT     tcp  --  172.16.10.9          0.0.0.0/0           tcp spt:22 state ESTABLISHED 
2    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0           
3    ACCEPT     icmp --  0.0.0.0/0            0.0.0.0/0           icmp type 8 
4    ACCEPT     tcp  --  172.16.10.9          0.0.0.0/0           tcp spt:80 state ESTABLISHED

這樣的設置使得外界可以和主機建立會話,但是由主機出去的數據包則一定只能是ESTABLISHED狀態的服務發出的,這樣本機想主動和外界建立會話是不可能的。這樣就實現了狀態監測的功能,防止黑客通過開放的22端口或80端口植入木馬並主動聯系黑客。

(7).放行外界ping自己,但是要基於ip_conntrack來放行。

iptables -A INPUT -d 172.16.10.9 -p icmp --icmp-type=8 -m state --state=NEW,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -s 172.16.10.9 -p icmp --icmp-type=0 -m state --state=ESTABLISHED -j ACCEPT  
iptables -L -n --line-number
Chain INPUT (policy DROP)
num  target     prot opt source               destination         
1    ACCEPT     tcp  --  172.16.10.0/24       172.16.10.9         tcp dpt:22 state NEW,ESTABLISHED 
2    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0           
3    ACCEPT     icmp --  0.0.0.0/0            0.0.0.0/0           icmp type 0 
4    ACCEPT     tcp  --  0.0.0.0/0            172.16.10.9         tcp dpt:80 state NEW,ESTABLISHED 
5    ACCEPT     icmp --  0.0.0.0/0            172.16.10.9         icmp type 8 state NEW,ESTABLISHED 

Chain FORWARD (policy DROP)
num  target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
num  target     prot opt source               destination         
1    ACCEPT     tcp  --  172.16.10.9          0.0.0.0/0           tcp spt:22 state ESTABLISHED 
2    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0           
3    ACCEPT     icmp --  0.0.0.0/0            0.0.0.0/0           icmp type 8 
4    ACCEPT     tcp  --  172.16.10.9          0.0.0.0/0           tcp spt:80 state ESTABLISHED
5    ACCEPT     icmp --  172.16.10.9          0.0.0.0/0           icmp type 0 state ESTABLISHED

(8).安裝vsftpd,並設置其防火牆。

由於ftp有主動模式和被動模式,被動模式的數據端口不定,且使用哪種模式是由客戶端決定的,這使得ftp的防火牆設置比較復雜,但是借助netfilter的state模塊,設置就簡單的多了。

首先裝載其專門的模塊nf_conntrack_ftp。

[root@xuexi ~]# modprobe nf_conntrack_ftp

也可以寫入/etc/sysconfig/iptables-config的"IPTABLES_MODULES="nf_conntrack_ftp"。

然后編寫規則:放行21端口的進入數據包,放行related關聯數據包,放行出去的包(放行出去的包是前面已默認的,但此處為了試驗完整性,還是顯式指定了)。

iptables -A INPUT -d 172.16.10.9 -p tcp --dport 21 -m state --state=NEW,ESTABLISHED -j ACCEPT
iptables -A INPUT -d 172.16.10.9 -m state --state=RELATED,ESTABLISHED -j ACCEPT                       
iptables -A OUTPUT -s 172.16.10.9 -m state --state=ESTABLISHED -j ACCEPT 

上面一個規則中多個狀態列表,狀態列表中的狀態是“或”的關系,滿足其一即可。

[root@xuexi ~]# iptables -L -n --line-number
Chain INPUT (policy DROP)
num  target     prot opt source               destination         
1    ACCEPT     tcp  --  172.16.10.0/24       172.16.10.9         tcp dpt:22 state NEW,ESTABLISHED 
2    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0           
3    ACCEPT     icmp --  0.0.0.0/0            0.0.0.0/0           icmp type 0 
4    ACCEPT     tcp  --  0.0.0.0/0            172.16.10.9         tcp dpt:80 state NEW,ESTABLISHED 
5    ACCEPT     icmp --  0.0.0.0/0            172.16.10.9         icmp type 8 state NEW,ESTABLISHED 
6    ACCEPT     tcp  --  0.0.0.0/0            172.16.10.9         tcp dpt:21 state NEW,ESTABLISHED 
7    ACCEPT     all  --  0.0.0.0/0            172.16.10.9         state RELATED,ESTABLISHED 

Chain FORWARD (policy DROP)
num  target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
num  target     prot opt source               destination         
1    ACCEPT     tcp  --  172.16.10.9          0.0.0.0/0           tcp spt:22 state ESTABLISHED 
2    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0           
3    ACCEPT     icmp --  0.0.0.0/0            0.0.0.0/0           icmp type 8 
4    ACCEPT     tcp  --  172.16.10.9          0.0.0.0/0           tcp spt:80 state ESTABLISHED
5    ACCEPT     icmp --  172.16.10.9          0.0.0.0/0           icmp type 0 state ESTABLISHED 
6    ACCEPT     all  --  172.16.10.9          0.0.0.0/0           state ESTABLISHED

現在iptables已經有很多規則,但是也足夠亂的。不僅想看懂挺復雜,在數據包檢查的時候性能也更差,所以有必要將它們合並成簡單易懂的規則。

6.7.5 合並規則以及調整規則的順序

執行iptables-save命令,可以dump出當前內核維護的netfilter指定表中的規則,默認導出filter表。

[root@xuexi ~]# iptables-save -t filter
# Generated by iptables-save v1.4.7 on Sun Aug 13 05:33:29 2017
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -s 172.16.10.0/24 -d 172.16.10.9/32 -p tcp -m tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT 
-A INPUT -i lo -j ACCEPT 
-A INPUT -p icmp -m icmp --icmp-type 0 -j ACCEPT 
-A INPUT -d 172.16.10.9/32 -p tcp -m tcp --dport 80 -m state --state NEW,ESTABLISHED -j ACCEPT 
-A INPUT -d 172.16.10.9/32 -p icmp -m icmp --icmp-type 8 -m state --state NEW,ESTABLISHED -j ACCEPT 
-A INPUT -d 172.16.10.9/32 -p tcp -m tcp --dport 21 -m state --state NEW,ESTABLISHED -j ACCEPT 
-A INPUT -d 172.16.10.9/32 -m state --state RELATED,ESTABLISHED -j ACCEPT 
-A OUTPUT -s 172.16.10.9/32 -p tcp -m tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT 
-A OUTPUT -o lo -j ACCEPT 
-A OUTPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT 
-A OUTPUT -s 172.16.10.9/32 -p tcp -m tcp --sport 80 -m state --state ESTABLISHED -j ACCEPT 
-A OUTPUT -s 172.16.10.9/32 -p icmp -m icmp --icmp-type 0 -m state --state ESTABLISHED -j ACCEPT 
-A OUTPUT -s 172.16.10.9/32 -m state --state ESTABLISHED -j ACCEPT 
COMMIT
# Completed on Sun Aug 13 05:36:22 2017

從導出結果中可以看到,input鏈中有好幾條規則都是針對state=NEW,ESTABLISHED而建立的,同理OUTPUT鏈中的state=ESTABLISHED,且他們的target都是一樣的,這樣的規則可以考慮是否能夠合並。

注意:環回接口lo一定要顯式指定所有類型的數據都通過。

例如,先將OUTPUT鏈中state=ESTABLISHED的規則進行合並。

iptables -I OUTPUT 1 -s 172.16.10.9 -m state --state=ESTABLISHED -j ACCEPT

再將OUTPUT鏈中除了lo接口的所有規則刪除掉即可。

最終OUTPUT鏈剩下以下兩條規則。

-A OUTPUT -s 172.16.10.9/32 -m state --state ESTABLISHED -j ACCEPT
-A OUTPUT -o lo -j ACCEPT

雖然這樣使得OUTPUT沒有再限制端口和協議,但對於流出數據包而言,這樣已經足夠了。一般來說,OUTPUT鏈的定義方式是:默認所有數據出去,但禁止某些端口(如80)發送NEW、INVALID狀態的包。所以,在OUTPUT鏈默認規則為ACCEPT的情況下,可以參照如下方式定義該鏈的規則:

-A OUTPUT -s 172.16.10.9/32 -p tcp -m multiport --sports 21,22,80 -m state --state NEW,INVALID -j DROP

現在規則如下:

[root@xuexi ~]# iptables -L -n --line-number
Chain INPUT (policy DROP)
num  target     prot opt source               destination         
1    ACCEPT     tcp  --  172.16.10.0/24       172.16.10.9         tcp dpt:22 state NEW,ESTABLISHED 
2    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0           
3    ACCEPT     icmp --  0.0.0.0/0            0.0.0.0/0           icmp type 0 
4    ACCEPT     tcp  --  0.0.0.0/0            172.16.10.9         tcp dpt:80 state NEW,ESTABLISHED 
5    ACCEPT     icmp --  0.0.0.0/0            172.16.10.9         icmp type 8 state NEW,ESTABLISHED 
6    ACCEPT     tcp  --  0.0.0.0/0            172.16.10.9         tcp dpt:21 state NEW,ESTABLISHED 
7    ACCEPT     all  --  0.0.0.0/0            172.16.10.9         state RELATED,ESTABLISHED 

Chain FORWARD (policy DROP)
num  target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
num  target     prot opt source               destination         
1    DROP       tcp  --  172.16.10.9          0.0.0.0/0           multiport sports 21,22,80 state INVALID,NEW

再來合並INPUT鏈的規則。INPUT鏈的合並應該遵循這樣一種規則:先定義好大規則,再逐漸向后添加更具體、針對性更強的小規則。

首先將INPUT鏈中第4、6兩條規則合並。

iptables -I INPUT 4 -d 172.16.10.9 -p tcp -m multiport --dport 21,80 -m state --state=NEW,ESTABLISHED -j ACCEPT

再合並INPUT鏈中對內和對外的ping規則。

iptables -I INPUT 3 -p icmp --icmp-type any -m state --state=NEW,ESTABLISHED -j ACCEPT

再刪除被合並的多余規則。最終INPUT鏈中規則列表如下:

-A INPUT -s 172.16.10.0/24 -d 172.16.10.9/32 -p tcp -m tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT 
-A INPUT -i lo -j ACCEPT 
-A INPUT -p icmp -m icmp --icmp-type any -m state --state NEW,ESTABLISHED -j ACCEPT 
-A INPUT -d 172.16.10.9/32 -p tcp -m multiport --dports 21,80 -m state --state NEW,ESTABLISHED -j ACCEPT 
-A INPUT -d 172.16.10.9/32 -m state --state RELATED,ESTABLISHED -j ACCEPT

之后再有其他需求時,只需在此基礎上添加更細致、具體的規則即可。如禁止外界ping本機,只需在第三條INPUT規則前DROP掉進來的icmp-type=8的包即可。

從上面的規則列表中,也許已經發現了順序不是很易讀。規則列表中規則的順序是至關重要的,不僅影響易讀性,還影響檢查的順序從而影響性能,例如大並發量的數據包應該盡早匹配。以下是調整INPUT鏈中規則順序的幾個建議:

(1).請求量大的盡量放前面。

(2).通用型的規則盡量放前面。

(3).直接拒絕的考慮放前面,主要是防惡意的循環攻擊,對於個別拉黑但非攻擊意圖的其實無需放前面。

其實,iptables服務腳本配置文件/etc/sysconfig/iptables中初始的規則就是最佳的框架。

[root@xuexi ~]# cat /etc/sysconfig/iptables
# Firewall configuration written by system-config-firewall
# Manual customization of this file is not recommended.
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state NEW -m tcp -p tcp --dport 22 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
COMMIT

之后有任何特定的需求,都可以直接在這些初始規則基礎上進行追加。

6.8 規則的管理方法

6.8.1 保存規則

使用iptables寫的規則都存放在內存中,內核會維護netfilter的每張表中的規則,所以重啟iptables"服務"會使內存中的規則列表全部被清空。

要想手動寫的規則長期有效,需要將規則保存到持久存儲文件中,例如iptables"服務"啟動時默認加載的腳本配置文件/etc/sysconfig/iptables。

有兩種方法保存規則。

方法一:直接保存到/etc/sysconfig/iptables中

service iptables save

存方法二:可自定義保存位置

iptables-save >/etc/sysconfig/iptables
iptables-save >/etc/sycofnig/iptables.20170103

恢復規則的方法:

iptables-restore </etc/sysconfig/iptables
iptables-restore </etc/sysconfig/iptables.2170103

6.8.2 規則的管理方法

雖然將規則放入/etc/sysconfig/iptables文件中可以每次加載netfilter都能應用相應的規則,但是極其不建議這么做,否則將來很有可以能會欲哭無淚。假如防火牆規則數據庫中關於192.168.100.8的規則有100條,但是該主機改了IP地址,難道要去修改所有192.168.100.8的規則嗎。

管理規則更好的方法是寫成腳本。將這些規則全部寫入到一個shell腳本中,並對多次重復的地址使用變量,例如服務器的地址或網段,內網的網段。如下圖所示:

使用腳本的優點有:

1.管理的便捷。寫成腳本可以直接修改該文件,要重新生效時只需執行一次該腳本文件即可。但是要注意腳本的第一條命令最好是iptables -F,這樣每次運行腳本都會先清空已有規則再加載腳本中的其他規則。

2.可以在腳本中使用變量。這樣以后某台服務器地址改變了只需修改該服務器地址對應的變量值即可。

3.容易閱讀,因為可以加注釋,並通過注釋來對規則進行分類,未來修改就變得相對容易的多。

4.備份規則變得更容易。備份后,即使硬盤壞了導致現有的規則丟失了也可以簡單的拷貝一個腳本過去運行即可,而不用再一條一條命令的敲。

5.也可以實現開機加載規則。只需在/etc/rc.d/rc.local中加上一條執行該腳本的命令即可。

6.可以將其加入任務計划。

6.9 自定義鏈

自定義鏈是被主鏈引用的。引用位置由"-j"指定自定義鏈名稱,表示跳轉到自定義鏈並匹配其內規則列表。

例如,在INPUT鏈中的第三條規則為自定義鏈的引用規則,則數據包匹配到了第三條時進入自定義鏈匹配,匹配完自定義鏈后如果定義了返回主鏈的RETURN動作,則返回主鏈繼續向下匹配,如果沒有定義RETURN動作,則匹配結束。

創建一條自定義鏈。

iptables -N mychain

向其中加入一些基於安全的攻防規則,讓每次數據包進入都匹配一次攻防鏈。

iptables -A mychain -d 255.255.255.255 -p icmp -j DROP
iptables -A mychain -d 192.168.255.255 -p icmp -j DROP
iptables -A mychain -p tcp ! --syn -m state --state NEW -j DROP
iptables -A mychain -p tcp --tcp-flags ALL ALL -j DROP
iptables -A mychain -p tcp --tcp-flags ALL NONE -j DROP

在自定義鏈中的最后一條加上一條返回主鏈的規則,表示匹配完自定義后繼續回到主鏈進行匹配。

iptables -A mychain -d 192.168.100.8 -j RETURN

在主鏈的適當位置加上一條引用主鏈的規則。表示數據包匹配到了這個位置開始進入自定義鏈匹配,如果自定義鏈都沒被匹配而是被最后的RETURN規則匹配,則回到主鏈再次匹配。

iptables -I INPUT -d 192.168.100.8 -j mychain

刪除自定義鏈:需要先清空自定義鏈,去除被引用記錄,然后使用-X刪除空的自定義鏈。

iptables -F mychain
iptables -D INPUT 1
iptables -X mychain

可以使用-E命令重命名自定義鏈。

6.10 NAT

6.10.1 配置網關以及轉發

首先是一個網關配置實驗。

以CentOS_1作為兩邊內網的網關,讓內網1和內網2可以互相通信。試驗過程中,先關閉CentOS_1的防火牆。

首先配置Windows Server 2003和CentOS_2的網關指向CentOS_1。

 

目前CentOS_1還沒有打開轉發功能。測試CentOS_2和Windows Server 2003都能ping通CentOS_1的兩個地址,但CentOS_2和Windows Server 2003兩者無法互相ping通。

為什么到兩個內網到CentOS_1的兩個地址都通,但是到對方內網卻不通呢?

對於CentOS_1主機而言,網絡是內核空間中的內容,兩個IP地址都屬於主機而非屬於網卡,內核知道eth0和eth1的存在由於默認在路由表中有對應內網1和內網2的路由條目,這兩個路由條目用於維持和自己所在網段的地址通信(Iface列指定了從eth1和eth0流出去)。

當CentOS_2發起ping CentOS_1:eth1的請求時,ping請求包從eth0接口進入CentOS_1,進入后被路由決策一次,內核發現這個數據包的目標地址是eth1,可以直接應答給CentOS_2,於是產生pong響應包並被路由一次,決定從eth0出去,最終回復給了CentOS_2。同理從eth1進來目標地址是eth0的數據包也是一樣處理的。這里面並沒有涉及到數據包轉發的過程。

但內網1主機在ping內網2主機時,在CentOS_1上卻需要數據包的轉發。因為數據包到達eth0上時數據包的目標地址是win server 2003的地址172.16.10.30,路由決策時發現是和eth1同網段的主機,但卻不是本機,於是決定從eth1流出去。要完成這個過程,需要將數據包完完整整地從eth0交給eth1,這要求CentOS_1主機能夠完成轉發,但沒有開啟ip_forward功能時是無法轉發的,因此從eth0流入的數據包被丟棄,導致內網1主機ping不通內網2主機。

在CentOS_1上開啟轉發功能。

echo 1 > /proc/sys/net/ipv4/ip_forward

再使用CentOS_2來ping Windows Server 2003,結果一定是通的,如果沒有通,考慮CentOS_1是否開啟了防火牆。

6.10.2 配置網關防火牆

打開轉發后就可以對filter表的FORWARD鏈進行設置了:只要是被forward的數據包都會受到防火牆的"鈎子伺候",並進行一番檢查。

要注意的是,此時防火牆是負責兩個網段的,轉發后的數據包狀態並不會因為經過FORWARD而改變。例如內網1發出的NEW狀態的數據包到內網2時途經FORWARD時,如果允許通過則轉發出去的數據包還是NEW狀態的,這樣也就保證了內網2接受到的數據包還是NEW狀態的,如果內網2也配置一個單機防火牆就可以判斷這是NEW狀態的數據包從而進行相關的規則過濾。

例如:

iptables -P FORWARD DROP

此時已經內網1和內網2相互ping不通了。加上下面的規則,內網1的CentOS_2就能ping通外面,且外面進來的數據包都只能是ESTABLISHED狀態的。

iptables -A FORWARD -m state --state NEW,ESTABLISHED -j ACCEPT 

再加上這兩條,就能保證內網2只能向內網1主機的22和80端口發起NEW和ESTABLISHED狀態的數據包,且內網1只能向外發送ESTABLISHED狀態的數據包。

iptables -A FORWARD -d 172.16.10.15 -p tcp -m multiport --dports 22,80 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A FORWARD -s 172.16.10.15 -m state --state ESTABLISHED -j ACCEPT  

6.10.3 SNAT和DNAT

NAT依賴於ip_forward,因此需要先開啟它。NAT的基礎是nf_conntrack,用來記錄NAT表的映射關系。

注:從內核2.6.34開始,NAT表支持操作INPUT鏈。它只為SNAT服務。和snat on postrouting類似,只不過snat on input用來轉換"目標是本機的數據包"的源地址。

NAT有三個作用:

◇ 地址轉換。讓內網(私有地址)可以共用一個或幾個公網地址連接Internet。這是從內向外的,需要在網關式防火牆的POSTROUTING處修改源地址,這是SNAT功能。

◇ 保護內網服務器。內網主機連接Internet使用的是公網地址,對外界而言是看不到內網服務器地址的,所以外界想要訪問內部主機只能經過防火牆主機的公網地址,然后將目標地址轉換為內網服務器地址,這起到了保護內網服務器的作用。轉換目標地址需要在網關式防火牆的PREROUTING鏈處修改,這是DNAT功能。

◇ 不僅可以修改目標地址,還可以使用端口映射功能。DNAT是修改目標地址,端口映射是修改目標端口。如將web服務器的8080端口映射為防火牆的80端口。

外網主機和內網主機通信有兩種情況的數據包:一種情況是建立NEW狀態的新請求連接數據包,一種是回應的數據包。無論哪種情況,在NEW狀態數據包經過地址轉換之后會在防火牆內存中維護一張NAT表,保存未完成連接的地址轉換記錄,這樣在回應數據包到達防火牆時可以根據這些記錄路由給正確的主機。

也就是說,SNAT主要應付的是內部主機連接到Internet的源地址轉換,轉換的位置是POSTROUTING鏈;DNAT主要應付的是外部主機連接內部服務器防止內部服務器被攻擊的目標地址轉換,轉換位置在PREROUTING;端口映射可以在多個地方轉換。

(1).SNAT轉換源地址

SNAT過程的數據包轉換過程如下圖所示。

注意SNAT設置在postrouting鏈,流出接口eth1。由於規則中有其他的條件存在,使得可以大多數時候不用指定流出接口,當然如果指定的話更完整。

iptables -t NAT -A POSTROUTING -s 172.16.10.0/24 -o eth1 -j SNAT --to-source 192.168.100.20
iptables -t NAT -A POSTROUTING -s 172.16.10.0/24 -o eth1 -j SNAT --to-source 192.168.100.20-192.168.100.25
iptables -t NAT -A POSTROUTING -s 172.16.10.0/24 -o eth1 -j MASQUERADE

第一條語句表示將內網1向外發出的數據包進行處理,將源地址轉換為192.168.100.20。

第二條語句表示將源地址轉換成192.168.100.{20-25}之間的某個地址。

第三條語句表示動態獲取流出接口eth1的地址,並將源地址轉換為此地址。這稱為地址偽裝(ip masquerade),地址偽裝功能很實用,但是相比前兩種,性能要稍差一些,因為處理每個數據包時都要獲取eth1的地址,也就是說多了一個查找動作。

(2).DNAT目標地址和端口轉換

DNAT過程的數據包轉換過程如下圖所示。

注意DNAT設置在prerouting鏈,流入接口eth1。

iptables -t NAT -A PREROUTING -i eth1 -d 192.168.100.20 -p tcp --dport 80 -j DNAT --to-destination 172.16.10.15:8080

上面的語句表示從外網流入的目標為192.168.100.20:80的數據包轉換為目標172.16.10.15:8080,於是該數據包被路由給內網1主機172.16.10.15的8080端口上。

6.11 其它好資料

wiki netfilter:https://en.wikipedia.org/wiki/Netfilter

很好很詳細很全面的iptables學習手冊:https://www.frozentux.net/iptables-tutorial/chunkyhtml/index.html

OUTPUT和DNAT釋疑:https://www.linuxquestions.org/questions/linux-networking-3/iptables-how-to-redirect-locally-generated-packets-to-a-remote-server-797173/

lvs中的netfilter實現:http://zh.linuxvirtualserver.org/node/98


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM