基礎概念
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 中並不被稱為"關卡",而是被稱為"鏈"。
其實我們上面描述的場景並不完善,因為客戶端發來的報文訪問的目標地址可能並不是本機,而是其他服務器,當本機的內核支持 IP_FORWARD 時,我們可以將報文轉發給其他服務器。這個時候,我們就會提到 iptables 中的其他"關卡",也就是其他"鏈",它們就是 PREROUTING(“路由前”)、FORWARD(“轉發”)、POSTROUTING(“路由後”)。
也就是說,當我們啟用了防火牆功能時,報文需要經過如下關卡,也就是說,根據實際情況的不同,報文經過"鏈"可能不同。如果報文需要轉發,那麼報文則不會經過 INPUT 鏈發往用戶空間,而是直接在內核空間中經過 FORWARD 鏈和 POSTROUTING 鏈轉發出去的。
iptables的結構
iptables 由表(table)、鏈(chain)和規則(rule)組成,其中表包含鏈,鏈包含規則。
我們把具有相同功能的規則集合叫做“表”,對於不同功能的規則,我們可以放置在不同的表中進行管理。iptables 中具有 filter、nat、mangle、raw等幾種內建表:
對於各個具體的鏈而言:
- INPUT 鏈:處理來自外部的數據。
- OUTPUT 鏈:處理向外發送的數據。
- FORWARD 鏈:數據轉發。通過路由表後發現目的地址非本機,則匹配該鏈中的規則。
- PREROUTING 鏈:處理剛到達本機並在路由轉發前的數據包。
- POSTROUTING 鏈:處理即將離開本機的數據包。
數據包的流轉流程可以參考下圖:
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。此處省略修改規則、保存規則的具體操作。
閱讀更多 架構師之路 的文章