這次終於不再為 iptables 犯迷糊了

基礎概念

linux 的包過濾功能,即 linux 防火牆,它由 netfilter 和 iptables 兩個組件組成。netfilter 位於內核空間,由一些信息包過濾表組成,這些表包含內核用來控制信息包過濾處理的規則集。iptables 是一個命令行工具,位於用戶空間,它使得插入、修改和刪除信息包過濾表中的規則變得容易。

我們知道 iptables 是按照規則來辦事的,規則其實就是網絡管理員預定義的條件,規則一般的定義為"如果數據包頭符合這樣的條件,就這樣處理這個數據包"。規則存儲在內核空間的信息包過濾表中,這些規則分別指定了源地址、目的地址、傳輸協議(如TCP、UDP、ICMP)和服務類型(如HTTP、FTP和SMTP)等。當數據包與規則匹配時,iptables 就根據規則所定義的方法來處理這些數據包,如放行(ACCEPT)、拒絕(REJECT)和丟棄(DROP)等。配置防火牆的主要工作就是添加、修改和刪除這些規則。

當客戶端訪問服務器的服務時,客戶端發送報文到服務器的網卡,而 TCP/IP 協議棧是屬於內核的一部分,所以客戶端的信息會通過內核的 TCP 協議傳輸到用戶空間中的服務中,而此時客戶端報文的目標終點(destination)為服務所監聽的套接字(IP:PORT),當服務需要響應客戶端請求時,服務發出的響應報文的目標終點則為客戶端,這個時候服務所監聽的IP與端口反而變成了原點(source)。netfilter才是真正的防火牆,它是內核的一部分,如果我們想要防火牆能夠達到"防火"的目的,則需要在內核中設置關卡,所有進出的報文都要通過這些關卡,經過檢查後,符合放行條件的才能放行,符合阻攔條件的則需要被阻止,於是就出現了 INPUT 關卡和 OUTPUT 關卡。然而這些關卡在 iptables 中並不被稱為"關卡",而是被稱為"鏈"。

這次終於不再為 iptables 犯迷糊了

其實我們上面描述的場景並不完善,因為客戶端發來的報文訪問的目標地址可能並不是本機,而是其他服務器,當本機的內核支持 IP_FORWARD 時,我們可以將報文轉發給其他服務器。這個時候,我們就會提到 iptables 中的其他"關卡",也就是其他"鏈",它們就是 PREROUTING(“路由前”)、FORWARD(“轉發”)、POSTROUTING(“路由後”)。

也就是說,當我們啟用了防火牆功能時,報文需要經過如下關卡,也就是說,根據實際情況的不同,報文經過"鏈"可能不同。如果報文需要轉發,那麼報文則不會經過 INPUT 鏈發往用戶空間,而是直接在內核空間中經過 FORWARD 鏈和 POSTROUTING 鏈轉發出去的。


這次終於不再為 iptables 犯迷糊了

iptables的結構

iptables 由表(table)、鏈(chain)和規則(rule)組成,其中表包含鏈,鏈包含規則。

我們把具有相同功能的規則集合叫做“表”,對於不同功能的規則,我們可以放置在不同的表中進行管理。iptables 中具有 filter、nat、mangle、raw等幾種內建表:

  • filter 表:iptables的默認表。負責過濾功能、防火牆,也就是由 filter 表來決定一個數據包是否繼續發往它的目的地址或者被丟棄。對應的內核模塊為 iptables_filter。filter表具有三種內建鏈:INPUT、OUTPUT、FORWARD。
  • nat 表:nat 是 network address translation的簡稱,具備網絡地址轉換的功能。對應的內核模塊為 iptables_nat。nat 表有三種內建鏈:PREROUTING、POSTROUTING、OUTPUT(CentOS 7中還包含INPUT,但是在CentOS 6中沒有)。
  • mangle 表:用於指定如何處理數據包,具備拆解報文、修改報文以及重新封裝的功能,可用於修改IP頭部信息,如TTL。對應的內核模塊為 iptables_mangle。mangle 表具有5種內建鏈:PREROUTING、INPUT、FORWARD、OUTPUT、POSTROUTING。
  • raw 表:用於處理異常。對應的內核模塊為 iptables_raw。raw 表包含2個內建鏈:PREROUTING 和 OUTPUT。

  • 對於各個具體的鏈而言:

    • INPUT 鏈:處理來自外部的數據。
    • OUTPUT 鏈:處理向外發送的數據。
    • FORWARD 鏈:數據轉發。通過路由表後發現目的地址非本機,則匹配該鏈中的規則。
    • PREROUTING 鏈:處理剛到達本機並在路由轉發前的數據包。
    • POSTROUTING 鏈:處理即將離開本機的數據包。

    數據包的流轉流程可以參考下圖:

    這次終於不再為 iptables 犯迷糊了

    iptables 命令格式

    iptables 命令格式為:

    iptables [ -t 表名] 命令選項 [鏈名] [條件匹配] [-j 處理動作或跳轉]

    如果沒有顯式設置表名,那麼默認為 filter表,即默認 -t filter。

    命令選項

    • -L 列出一個或所有鏈的規則
    • -v 顯示詳細信息,包括每條規則的匹配句數量和匹配字節數
    • -x 在v的基礎上,禁止自動換算單位(K,M)
    • -n 只顯示ip地址和端口號,不顯示域名和服務名稱
    • -I 插入到防火牆第一條生效
    • -A 添加鏈是添加到最後一條
    • -D 刪除指定鏈中的某一條規則,按規則序號或內容確定要刪除的規則
    • -F 清空指定鏈中的所有規則,默認清空表中所有鏈的內容
    • -X 刪除指定表中用戶自定義的規則鏈

    匹配條件

    • -i 入站請求interface(網卡)
    • -o 出站請求interface(網卡)
    • -s 入站源地址
    • -d 目標地址
    • -p 指定規則協議,如tcp, udp,icmp等,可以使用 all 來指定所有協議
    • --dport 目的端口,數據包的目的(dport)地址是80,就是要訪問我本地的80端口
    • --sport 來源端口 數據包的來源端口是(sport)80,就是對方的數據包是80端口發送過來的。

    動作

    • ACCEPT:允許數據包通過。
    • DROP:直接丟棄數據包,不給任何回應信息,這時候客戶端會感覺自己的請求泥牛入海了,過了超時時間才會有反應。
    • REJECT:拒絕數據包通過,必要時會給數據發送端一個響應的信息,客戶端剛請求就會收到拒絕的信息。(一般不使用REJECT(拒絕)行為,REJECT會帶來安全隱患。)
    • SNAT:源地址轉換,解決內網用戶用同一個公網地址上網的問題。
    • MASQUERADE:是SNAT的一種特殊形式,適用於動態的、臨時會變的ip上。
    • DNAT:目標地址轉換。
    • REDIRECT:在本機做端口映射。
    • LOG:在/var/log/messages文件中記錄日誌信息,然後將數據包傳遞給下一條規則,也就是說除了記錄以外不對數據包做任何其他操作,仍然讓下一條規則去匹配。

    規則

    在上述描述中我們一直在提規則,可是沒有細說。那麼規則具體指什麼呢?

    規則:根據指定的匹配條件來嘗試匹配每個流經此處的報文,一旦匹配成功,則由規則後面指定的處理動作進行處理。

    規則大致又兩個邏輯單元組成:匹配條件和動作。最常用的匹配條件是“源地址”、“目標地址”、“源端口”、“目標端口”;最常用的動作有 ACCEPT(接受)、DROP(丟棄)、REJECT(拒絕)。

    在實際操作iptables 的過程中,是以“表”作為操作入口的,如果你經常操作關係型的數據庫,那麼當你聽到“表”這個詞的時候,你可能會聯想到另一個詞——“增刪改查”,當我們定義 iptables 規則時,所做的操作其實類似於“增刪改查”。我們不妨從最簡單的“查”操作入手。

    filter 表是我們最常用到的表,我們這裡以 filter表為例來說明具體的操作。下面的命令展示如何查看 filter 表中的規則:

    <code>iptables -t filter -L/<code>
    <code>Chain INPUT (policy ACCEPT)/<code>
    <code>target prot opt source destination/<code>
    <code>Chain FORWARD (policy ACCEPT)/<code>
    <code>target prot opt source destination/<code>
    <code>ACCEPT all -- anywhere anywhere ctstate RELATED,ESTABLISHED/<code>
    <code>Chain OUTPUT (policy ACCEPT)/<code>
    <code>target prot opt source destination/<code>


    上面我們使用-t選項指定要操作的表,使用-L選項查看-t選項對應表的規則。-L 對應 List,意為列出規則。上面命令的含義為列出filter表的所有規則。

    我們可以查看指定表中指定鏈的規則。比如,我們只查看 filter 表中 INPUT 鏈的規則:


    <code>iptables -L INPUT/<code>
    <code>Chain INPUT (policy ACCEPT)/<code>
    <code>target prot opt source destination/<code>


    上面只顯示了 filter 表中 INPUT 鏈的規則(省略 -t 選項默認為 filter 表),當然,你也可以指定只查看其它鏈。我們還可以使用-v選項查看出更多、更詳細的信息,示例如下:

    <code>iptables -vL INPUT/<code>
    <code>Chain INPUT (policy ACCEPT 1509K packets, 851M bytes)/<code>
    <code> pkts bytes target prot opt in out source destination/<code>


    這裡我們看到多了一些字段,這些字段就是規則對應的屬性,具體的含義歸納如下:

    • pkts: 匹配到的報文的個數
    • bytes: 匹配到的報文包的大小總和
    • target: 表示規則對應的“動作”,即規則匹配成功後需要採取的措施
    • prot: 對應的協議,是否只針對某些協議應用此規則
    • opt: 表示規則對應的選項
    • in: 表示數據包由哪個interface(網卡)流入,我們可以設置通過哪塊網卡流入的報文需要匹配當前規則
    • out:表示數據包由哪個interface(網卡)流出,我們可以設置通過哪塊網卡流出的報文需要匹配當前規則
    • source: 表示規則對應的源頭地址,可以是一個 IP,也可以是一個網段
    • destination: 表示規則對應的目標地址

    上面鏈(Chain INPUT)的背後還有一個括號,括號裡包含了policy ACCEPT、1509K packets、851M bytes三部分。

    • policy: 表示當前鏈的默認策略,policy ACCEPT 表示上面 INPUT 鏈中的默認動作為 ACCEPT, 換句話說就是,默認接受通過INPUT的所有請求。
    • packets: 表示當前鏈的默認策略匹配到的包的數量。
    • bytes:表示當前鏈的默認策略匹配到的所有包的大小總和。

    如果需要,我們可以使用iptables -F INPUT 命令來清空 filter 表 INPUT 鏈中的規則。

    假設我們有2臺測試機,IP地址分別為 host1 和 host2, 我們可以在 host1 上使用 ping 命令來查看一下網絡連通情況:

    ping host2

    64 bytes from host2: icmp_seq=512 ttl=49 time=213 ms

    64 bytes from host2: icmp_seq=513 ttl=49 time=213 ms

    64 bytes from host2: icmp_seq=514 ttl=49 time=213 ms


    然後我們在 host1 上配置一條規則,拒絕 host2 上的所有報文訪問 host1。對應的命令如下:

    iptables -t filter -I INPUT -s host2 -j DROP


    使用-I選項,指明將“規則”插入哪個鏈中,I 表示 Insert,即插入的意思,這裡表示添加規則之意。使用-s選項,指明匹配條件中的“源地址”,即如果報文的源地址屬於 -s 對應的地址,那麼報文則滿足匹配條件。s 表示 source,即源地址。使用-j選項,指明當匹配條件滿足時,所對應的動作,上面指定了動作為 DROP,當報文的源地址為 host2 時,報文則被 DROP。

    再來查看一下 filter 表中的 INPUT 鏈:


    iptables -nvL INPUT

    Chain INPUT (policy ACCEPT 1421 packets, 344K bytes)

    pkts bytes target prot opt in out source destination

    114 7992 DROP all -- * * host2 0.0.0.0/0


    可以看到相應的規則已經添加了,在 iptables 中,動作被稱之為 “target”, 所以上面的 target 字段對應的動作為 DROP。

    現在 INPUT 鏈中已經存在了一條規則,它拒絕了所有來自 host2 的報文,如果此時我們在這條規則之後再配置一條規則—— 接受所有來自 host2 的報文,那麼此時 iptables 的表現如何呢?

    使用如下命令在 filter 表中追加一條規則:


    iptables -nvL INPUT

    Chain INPUT (policy ACCEPT 355 packets, 133K bytes)

    pkts bytes target prot opt in out source destination

    3960 274K DROP all -- * * host2 0.0.0.0/0

    0 0 ACCEPT all -- * * host2 0.0.0.0/0


    上面並沒有繼續使用-I選項,而是使用了-A選項,A代表 Append,也是表示在 INPUT 鏈中追加規則。-I與-A之間的區別在於:-I表示在鏈的首部插入規則,而-A表示在鏈的頭部插入規則。

    上面的信息中也顯示了新添加的 ACCEPT 規則在原先的 DROP 之後。

    此時再在 host1 上嘗試去 ping 通 host2 時會發現還是 ping 不通。看來新添加的規則沒有生效。我們這裡再次添加一條相同的規則,不過此時使用-I選項來添加。


    iptables -I INPUT -s host2 -j ACCEPT

    iptables -nvL INPUT

    Chain INPUT (policy ACCEPT 57 packets, 6438 bytes)

    pkts bytes target prot opt in out source destination

    16 2274 ACCEPT all -- * * host2 0.0.0.0/0

    7319 507K DROP all -- * * host2 0.0.0.0/0

    0 0 ACCEPT all -- * * host2 0.0.0.0/0


    如果我們此時再嘗試 ping 通 host2 時,發現已經可以正常ping 通了。如果觀察仔細,我們可以發現,剛剛添加的 ACCEPT 規則在 DROP 規則之前了。可見,規則的順序很重要。

    如果報文已經被前面的規則匹配到,iptables 則會對報文執行對應的動作,即使後面的規則也能匹配到當前報文,很有可能也沒有機會再對報文執行相應的動作了。就以上面的例子來說,報文先被第一條規則匹配到了,於是當前報文被“放行”了。也正因為報文已經被放行了,後面的第二條規則及時能夠匹配到放行的報文,也沒有機會在對剛才的報文進行丟棄操作了。這就是 iptables 的工作機制。

    使用 iptables 是可以通過 --line-number 選項來列出規則的序號,如下所示:

    iptables --line-number -nvL INPUT

    Chain INPUT (policy ACCEPT 13186 packets, 6606K bytes)

    num pkts bytes target prot opt in out source destination

    1 1568 278K ACCEPT all -- * * host2 0.0.0.0/0

    2 7319 507K DROP all -- * * host2 0.0.0.0/0

    3 0 0 ACCEPT all -- * * host2 0.0.0.0/0


    我們在添加規則的時候,還可以指定新增規則的編號,這樣我們就能在任意位置插入規則了,我們只要把剛才的命令稍作修改即可,如下:

    <code>iptables t fileter -I INPUT 2 -s host2 -j DROP/<code>

    這裡仍舊使用-I選項進行插入規則的操作,-I INPUT 2表示在 INPUT 鏈中新增規則,新增規則的編號為2。

    在刪除規則時,我們可以使用規則的編號去刪除,也可以使用具體的匹配條件和動作去刪除。

    舉例,我們刪除第三條規則:

    iptables -t filter -D INPUT 3

    iptables --line-number -nvL INPUT

    Chain INPUT (policy ACCEPT 87 packets, 9870 bytes)

    num pkts bytes target prot opt in out source destination

    1 5115 594K ACCEPT all -- * * host2 0.0.0.0/0

    2 7319 507K DROP all -- * * host2 0.0.0.0/0


    我們再刪除第一條規則:


    iptables -D INPUT -s host2 -j ACCEPT

    iptables --line-number -nvL INPUT

    Chain INPUT (policy ACCEPT 228 packets, 26328 bytes)

    num pkts bytes target prot opt in out source destination

    1 7325 507K DROP all -- * * host2 0.0.0.0/0


    如果要一下子全部清空怎麼操作?這個在前面已經提及過了,使用iptables -t 表名 -F 鏈名。-F選項為 flush 之意,即沖刷指定的鏈,即刪除指定鏈中的所有規則。此操作相當於刪除操作,在沒有保存 iptables 規則的情況下慎用。如果不指定鏈名,那麼會刪除表中的所有規則,即

    iptables -t 表名 -F。

    此處省略修改規則、保存規則的具體操作。


    分享到:


    相關文章: