連接配接跟蹤(conntrack)
連接配接跟蹤是許多網絡應用的基礎。例如,Kubernetes Service、ServiceMesh sidecar、 軟體四層負載均衡器 LVS/IPVS、Docker network、OVS、iptables 主機防火牆等等,都依賴連接配接跟蹤功能。連接配接跟蹤,顧名思義,就是跟蹤(并記錄)連接配接的狀态。 例如,圖 1.1 是一台 IP 位址為 10.1.1.2 的 Linux 機器,我們能看到這台機器上有三條 連接配接:
- 機器通路外部 HTTP 服務的連接配接(目的端口 80)
- 外部通路機器内 FTP 服務的連接配接(目的端口 21)
- 機器通路外部 DNS 服務的連接配接(目的端口 53)
連接配接跟蹤所做的事情就是發現并跟蹤這些連接配接的狀态,具體包括:
- 從資料包中提取元組(tuple)資訊,辨識資料流(flow)和對應的連接配接(connection)。
- 為所有連接配接維護一個狀态資料庫(conntrack table),例如連接配接的建立時間、發送 包數、發送位元組數等等。
- 回收過期的連接配接(GC)。
- 為更上層的功能(例如 NAT)提供服務。
需要注意的是,連接配接跟蹤中所說的“連接配接”,概念和 TCP/IP 協定中“面向連接配接”(connection oriented)的“連接配接”并不完全相同,簡單來說:
- TCP/IP 協定中,連接配接是一個四層(Layer 4)的概念。TCP 是有連接配接的,或稱面向連接配接的(connection oriented),發送出去的包都要求對端應答(ACK),并且有重傳機制。UDP 是無連接配接的,發送的包無需對端應答,也沒有重傳機制。
- conntrack(CT) 中,一個元組(tuple)定義的一條資料流(flow )就表示一條連接配接(connection)。後面會看到 UDP 甚至是 ICMP 這種三層協定在 CT 中也都是有連接配接記錄的,但不是所有協定都會被連接配接跟蹤。

Linux 的連接配接跟蹤是在 Netfilter 中實作的。
Netfilter 是 Linux 核心中一個對資料 包進行控制、修改和過濾(manipulation and filtering)的架構。它在核心協定棧中設定了若幹 hook 點,以此對資料包進行攔截、過濾或其他處理。
現在提到連接配接跟蹤(conntrack),可能首先都會想到 Netfilter,Netfilter 隻是 Linux 核心中的一種連接配接跟蹤實作。換句話說,隻要具備了 hook 能力,能攔截到進出主機的每個包,完全可以在此基礎上自己實作一套連接配接跟蹤。
雲原生網絡方案 Cilium 在 1.7.4+ 版本就實作了這樣一套獨立的連接配接跟蹤和 NAT 機制 (完備功能需要 Kernel 4.19+)。其基本原理是:
- 基于 BPF hook 實作資料包的攔截功能(等價于 netfilter 裡面的 hook 機制)
- 在 BPF hook 的基礎上,實作一套全新的 conntrack 和 NAT 是以,即便解除安裝掉 Netfilter ,也不會影響 Cilium 對 Kubernetes ClusterIP、NodePort、ExternalIPs 和 LoadBalancer 等功能的支援。由于這套連接配接跟蹤機制是獨立于 Netfilter 的,是以它的 conntrack 和 NAT 資訊也沒有 存儲在核心的(也就是 Netfilter 的)conntrack table 和 NAT table。是以正常的 conntrack/netstats/ss/lsof 等工具是看不到的,要使用 Cilium 的指令,例如:
$ cilium bpf nat list
$ cilium bpf ct list global
Iptables
Iptables 是配置 Netfilter 過濾功能的使用者空間工具。 netfilter 才是防火牆真正的安全架構(framework),netfilter 位于核心空間。iptables 其實是一個指令行工具,位于使用者空間,我們用這個工具操作真正的架構。Iptable 根據規則所定義的方法來處理資料包,如放行(accept)、拒絕(reject)和丢棄(drop)等。
例如當用戶端通路伺服器的web服務時,用戶端發送封包到網卡,而 tcp/ip 協定棧是屬于核心的一部分,是以,用戶端的資訊會通過核心的 TCP 協定傳輸到使用者空間中的 web 服務中,而此時,用戶端封包的目标終點為 web 服務所監聽的套接字(IP:Port)上,當web服務需要響應用戶端請求時,web 服務發出的響應封包的目标終點則為用戶端,這個時候,web 服務所監聽的 IP 與端口反而變成了原點,我們說過,netfilter 才是真正的防火牆,它是核心的一部分,是以,如果我們想要防火牆能夠達到”防火”的目的,則需要在核心中設定關卡,所有進出的封包都要通過這些關卡,經過檢查後,符合放行條件的才能放行,符合阻攔條件的則需要被阻止。
iptables 包含 4個表,5個鍊。其中表是按照對資料包的操作區分(過濾, NAT等)的,鍊是按照不同的 Hook 點來區分的,表和鍊實際上是netfilter的兩個次元。
iptables 的四個表分别是 filter,mangle,nat,raw,預設表是filter。
- filter 表:用來對資料包進行過濾,具體的規則要求決定如何處理一個資料包。
- nat 表:主要用來修改資料包的 IP 位址、端口号資訊。
- mangle 表:主要用來修改資料包的服務類型,生存周期,為資料包設定标記,實作流量整形、政策路由等。
- raw 表:主要用來決定是否對資料包進行狀态跟蹤。
iptables 的五個鍊分别是 PREROUTING,INPUT,FORWARD,OUTPUT,POSTROUTING。
- input 鍊:當收到通路本機位址的資料包時,将應用此鍊中的規則。
- output 鍊:當本機向外發送資料包時,将應用此鍊中的規則。
- forward 鍊:當收到需要轉發給其他位址的資料包時,将應用此鍊中的規則,注意如果需要實作forward轉發需要開啟Linux核心中的ip_forward功能。
- prerouting 鍊:在對資料包做路由選擇之前,将應用此鍊中的規則。
- postrouting 鍊:在對資料包做路由選擇之後,将應用此鍊中的規則。
表和鍊的對應關系如下圖所示:
我們能夠想象出某些常用場景中,封包的流向:
- 到本機某程序的封包:PREROUTING –> INPUT。
- 由本機轉發的封包:PREROUTING –> FORWARD –> POSTROUTING。
- 由本機的某程序發出封包(通常為響應封包):OUTPUT –> POSTROUTING。
我們可以将資料包通過防火牆的流程總結為下圖:
- -t:表名
- -n:不解析IP位址
- -v:會顯示出計數器的資訊,資料包的數量和大小
- -x:選項表示顯示計數器的精确值
- --line-numbers:顯示規則的序号(簡寫為--line)
- -L:鍊名
#iptables -t filter -nvxL DOCKER --line
Chain DOCKER (1 references)
num pkts bytes target prot opt in out source destination
1 5076 321478 ACCEPT tcp -- !docker0 docker0 0.0.0.0/0 172.17.0.2 tcp dpt:8443
2 37233 54082508 ACCEPT tcp -- !docker0 docker0 0.0.0.0/0 172.17.0.2 tcp dpt:22
3 1712 255195 ACCEPT tcp -- !docker0 docker0 0.0.0.0/0 172.17.0.3 tcp dpt:9000
4 0 0 ACCEPT tcp -- !docker0 docker0 0.0.0.0/0 172.17.0.3 tcp dpt:8000
5 40224 6343104 ACCEPT tcp -- !docker0 docker0 0.0.0.0/0 172.17.0.4 tcp dpt:3443
6 21034 2227009 ACCEPT tcp -- !docker0 docker0 0.0.0.0/0 172.17.0.5 tcp dpt:3306
7 58 5459 ACCEPT tcp -- !docker0 docker0 0.0.0.0/0 172.17.0.6 tcp dpt:80
8 826 70081 ACCEPT tcp -- !docker0 docker0 0.0.0.0/0 172.17.0.6 tcp dpt:443
9 10306905 1063612492 ACCEPT tcp -- !docker0 docker0 0.0.0.0/0 172.17.0.9 tcp dpt:3306
10 159775 12297727 ACCEPT tcp -- !docker0 docker0 0.0.0.0/0 172.17.0.7 tcp dpt:11111
增加規則
在指定表的指定鍊的尾部添加一條規則,-A 選項表示在對應鍊的末尾添加規則,省略 -t 選項時,表示預設操作 filter 表中的規則:
指令文法:iptables -t 表名 -A 鍊名 比對條件 -j 動作
示例:iptables -t filter -A INPUT -s 192.168.1.146 -j DROP
在指定表的指定鍊的首部添加一條規則,-I 選型表示在對應鍊的開頭添加規則:
指令文法:iptables -t 表名 -I 鍊名 比對條件 -j 動作
示例:iptables -t filter -I INPUT -s 192.168.1.146 -j ACCEPT
在指定表的指定鍊的指定位置添加一條規則:
指令文法:iptables -t 表名 -I 鍊名 規則序号 比對條件 -j 動作
示例:iptables -t filter -I INPUT 5 -s 192.168.1.146 -j REJECT
删除規則
按照規則序号删除規則,删除指定表的指定鍊的指定規則,-D 選項表示删除對應鍊中的規則。示例表示删除filter表中INPUT鍊中序号為3的規則。:
指令文法:iptables -t 表名 -D 鍊名 規則序号
示例:iptables -t filter -D INPUT 3
按照具體的比對條件與動作删除規則,删除指定表的指定鍊的指定規則。示例表示删除filter表中INPUT鍊中源位址為192.168.1.146并且動作為DROP的規則。:
指令文法:iptables -t 表名 -D 鍊名 比對條件 -j 動作
示例:iptables -t filter -D INPUT -s 192.168.1.146 -j DROP
删除指定表的指定鍊中的所有規則,-F選項表示清空對應鍊中的規則:
指令文法:iptables -t 表名 -F 鍊名
示例:iptables -t filter -F INPUT
修改規則
修改指定表中指定鍊的指定規則,-R 選項表示修改對應鍊中的規則,使用 -R 選項時要同時指定對應的鍊以及規則對應的序号,并且規則中原本的比對條件不可省略。示例表示修改filter表中INPUT鍊的第3條規則,将這條規則的動作修改為ACCEPT, -s 192.168.1.146為這條規則中原本的比對條件,如果省略此比對條件,修改後的規則中的源位址可能會變為0.0.0.0/0:
指令文法:iptables -t 表名 -R 鍊名 規則序号 規則原本的比對條件 -j 動作
示例:iptables -t filter -R INPUT 3 -s 192.168.1.146 -j ACCEPT
設定指定表的指定鍊的預設政策(預設動作):
指令文法:iptables -t 表名 -P 鍊名 動作
示例:iptables -t filter -P FORWARD ACCEPT
儲存規則
方式一
當我們對規則進行了修改以後,如果想要修改永久生效,必須使用下面指令儲存規則:
service iptables save
當然,如果你誤操作了規則,但是并沒有儲存,那麼使用 service iptables restart 指令重新開機 iptables 以後,規則會再次回到上次儲存 /etc/sysconfig/iptables 檔案時的模樣。
centos7 中,已經不再使用 init 風格的腳本啟動服務,而是使用 unit 檔案,是以,在 centos7 中已經不能再使用類似 service iptables start 這樣的指令了,是以 service iptables save 也無法執行,同時,在 centos7中,使用 firewall 替代了原來的 iptables service,不過不用擔心,我們隻要通過 yum 源安裝 iptables與iptables-services 即可(iptables 一般會被預設安裝,但是iptables-services 在 centos7 中一般不會被預設安裝),在centos7 中安裝完 iptables-services 後,即可像 centos6 中一樣,通過 service iptables save 指令儲存規則了,規則同樣儲存在 /etc/sysconfig/iptables 檔案中。此處給出 centos7 中配置 iptables-service 的步驟:
#配置好yum源以後安裝iptables-service
yum install -y iptables-services
#停止firewalld
systemctl stop firewalld
#禁止firewalld自動啟動
systemctl disable firewalld
#啟動iptables
systemctl start iptables
#将iptables設定為開機自動啟動,以後即可通過iptables-service控制iptables服務
systemctl enable iptables
上述配置過程隻需一次,以後即可在 centos7 中使用 service iptables save 指令儲存 iptables 規則了。
方式二
還可以使用另一種方法儲存 iptables 規則,就是使用 iptables-save 指令。使用 iptables-save 并不能儲存目前的 iptables 規則,但是可以将目前的 iptables 規則以”儲存後的格式”輸出到螢幕上。
是以,我們可以使用 iptables-save 指令,再配合重定向,将規則重定向到 /etc/sysconfig/iptables 檔案中即可。
iptables-save > /etc/sysconfig/iptables
加載規則
我們也可以将 /etc/sysconfig/iptables 中的規則重新載入為目前的iptables 規則,但是注意,未儲存入 /etc/sysconfig/iptables 檔案中的修改将會丢失或者被覆寫。
使用 iptables-restore 指令可以從指定檔案中重載規則,示例如下
iptables-restore < /etc/sysconfig/iptables
比對條件
當規則中同時存在多個比對條件時,多個條件之間預設存在”與”的關系,即封包必須同時滿足所有條件,才能被規則比對。
-s 用于比對封包的源位址,可以同時指定多個源位址,每個IP之間用逗号隔開,也可以指定為一個網段。
#示例如下
iptables -t filter -I INPUT -s 192.168.1.111,192.168.1.118 -j DROP
iptables -t filter -I INPUT -s 192.168.1.0/24 -j ACCEPT
iptables -t filter -I INPUT ! -s 192.168.1.0/24 -j ACCEPT
-d 用于比對封包的目标位址,可以同時指定多個目标位址,每個 IP 之間用逗号隔開,也可以指定為一個網段。
#示例如下
iptables -t filter -I OUTPUT -d 192.168.1.111,192.168.1.118 -j DROP
iptables -t filter -I INPUT -d 192.168.1.0/24 -j ACCEPT
iptables -t filter -I INPUT ! -d 192.168.1.0/24 -j ACCEPT
-p 用于比對封包的協定類型,可以比對的協定類型 tcp、udp、udplite、icmp、esp、ah、sctp 等(centos7 中還支援 icmpv6、mh)。
#示例如下
iptables -t filter -I INPUT -p tcp -s 192.168.1.146 -j ACCEPT
iptables -t filter -I INPUT ! -p udp -s 192.168.1.146 -j ACCEPT
-i 用于比對封包是從哪個網卡接口流入本機的,由于比對條件隻是用于比對封包流入的網卡,是以在 OUTPUT 鍊與 POSTROUTING 鍊中不能使用此選項。
#示例如下
iptables -t filter -I INPUT -p icmp -i eth4 -j DROP
iptables -t filter -I INPUT -p icmp ! -i eth4 -j DROP
-o 用于比對封包将要從哪個網卡接口流出本機,于比對條件隻是用于比對封包流出的網卡,是以在 INPUT 鍊與 PREROUTING 鍊中不能使用此選項。
#示例如下
iptables -t filter -I OUTPUT -p icmp -o eth4 -j DROP
iptables -t filter -I OUTPUT -p icmp ! -o eth4 -j DROP
擴充比對條件
tcp擴充子產品
常用的擴充比對條件如下:
- –sport:用于比對 tcp 協定封包的源端口,可以使用冒号指定一個連續的端口範圍。
- –dport:用于比對 tcp 協定封包的目标端口,可以使用冒号指定一個連續的端口範圍。
- –tcp-flags:用于比對封包的tcp頭的标志位。
- –syn:用于比對 tcp 建立連接配接的請求封包,相當于使用
。–tcp-flags SYN,RST,ACK,FIN SYN
注意,-p tcp與 -m tcp 并不沖突,-p 用于比對封包的協定,-m 用于指定擴充子產品的名稱,正好,這個擴充子產品也叫 tcp。
#示例如下
iptables -t filter -I OUTPUT -d 192.168.1.146 -p tcp -m tcp --sport 22 -j REJECT
iptables -t filter -I INPUT -s 192.168.1.146 -p tcp -m tcp --dport 22:25 -j REJECT
iptables -t filter -I INPUT -s 192.168.1.146 -p tcp -m tcp --dport :22 -j REJECT
iptables -t filter -I INPUT -s 192.168.1.146 -p tcp -m tcp --dport 80: -j REJECT
iptables -t filter -I OUTPUT -d 192.168.1.146 -p tcp -m tcp ! --sport 22 -j ACCEPT
iptables -t filter -I INPUT -p tcp -m tcp --dport 22 --tcp-flags SYN,ACK,FIN,RST,URG,PSH SYN -j REJECT
iptables -t filter -I OUTPUT -p tcp -m tcp --sport 22 --tcp-flags SYN,ACK,FIN,RST,URG,PSH SYN,ACK -j REJECT
iptables -t filter -I INPUT -p tcp -m tcp --dport 22 --tcp-flags ALL SYN -j REJECT
iptables -t filter -I OUTPUT -p tcp -m tcp --sport 22 --tcp-flags ALL SYN,ACK -j REJECT
iptables -t filter -I INPUT -p tcp -m tcp --dport 22 --syn -j REJECT
udp 擴充子產品
常用的擴充比對條件:
- –sport:比對udp封包的源位址。
- –dport:比對udp封包的目标位址。
#示例
iptables -t filter -I INPUT -p udp -m udp --dport 137 -j ACCEPT
iptables -t filter -I INPUT -p udp -m udp --dport 137:157 -j ACCEPT
icmp 擴充子產品
- –icmp-type:比對icmp封包的具體類型。
#示例
iptables -t filter -I INPUT -p icmp -m icmp --icmp-type 8/0 -j REJECT
iptables -t filter -I INPUT -p icmp --icmp-type 8 -j REJECT
iptables -t filter -I OUTPUT -p icmp -m icmp --icmp-type 0/0 -j REJECT
iptables -t filter -I OUTPUT -p icmp --icmp-type 0 -j REJECT
iptables -t filter -I INPUT -p icmp --icmp-type "echo-request" -j REJECT
multiport 擴充子產品
- -p tcp -m multiport –sports 用于比對封包的源端口,可以指定離散的多個端口号,端口之間用”逗号”隔開。
- -p udp -m multiport –dports 用于比對封包的目标端口,可以指定離散的多個端口号,端口之間用”逗号”隔開。
#示例如下
iptables -t filter -I OUTPUT -d 192.168.1.146 -p udp -m multiport --sports 137,138 -j REJECT
iptables -t filter -I INPUT -s 192.168.1.146 -p tcp -m multiport --dports 22,80 -j REJECT
iptables -t filter -I INPUT -s 192.168.1.146 -p tcp -m multiport ! --dports 22,80 -j REJECT
iptables -t filter -I INPUT -s 192.168.1.146 -p tcp -m multiport --dports 80:88 -j REJECT
iptables -t filter -I INPUT -s 192.168.1.146 -p tcp -m multiport --dports 22,80:88 -j REJECT
iprange 子產品
包含的擴充比對條件如下:
- –src-range:指定連續的源位址範圍。
- –dst-range:指定連續的目标位址範圍。
#示例
iptables -t filter -I INPUT -m iprange --src-range 192.168.1.127-192.168.1.146 -j DROP
iptables -t filter -I OUTPUT -m iprange --dst-range 192.168.1.127-192.168.1.146 -j DROP
iptables -t filter -I INPUT -m iprange ! --src-range 192.168.1.127-192.168.1.146 -j DROP
string 子產品
常用擴充比對條件如下:
- –algo:指定對應的比對算法,可用算法為bm、kmp,此選項為必需選項。
- –string:指定需要比對的字元串
我們想要達到的目的是,如果封包中包含”OOXX”字元,我們就拒絕封包進入本機:
#示例
iptables -t filter -I INPUT -m string --algo bm --string "OOXX" -j REJECT
time 子產品
- –timestart:用于指定時間範圍的開始時間,不可取反。
- –timestop:用于指定時間範圍的結束時間,不可取反。
- –weekdays:用于指定”星期幾”,可取反。
- –monthdays:用于指定”幾号”,可取反。
- –datestart:用于指定日期範圍的開始日期,不可取反。
- –datestop:用于指定日期範圍的結束時間,不可取反。
#示例
iptables -t filter -I OUTPUT -p tcp --dport 80 -m time --timestart 09:00:00 --timestop 19:00:00 -j REJECT
iptables -t filter -I OUTPUT -p tcp --dport 443 -m time --timestart 09:00:00 --timestop 19:00:00 -j REJECT
iptables -t filter -I OUTPUT -p tcp --dport 80 -m time --weekdays 6,7 -j REJECT
iptables -t filter -I OUTPUT -p tcp --dport 80 -m time --monthdays 22,23 -j REJECT
iptables -t filter -I OUTPUT -p tcp --dport 80 -m time ! --monthdays 22,23 -j REJECT
iptables -t filter -I OUTPUT -p tcp --dport 80 -m time --timestart 09:00:00 --timestop 18:00:00 --weekdays 6,7 -j REJECT
iptables -t filter -I OUTPUT -p tcp --dport 80 -m time --weekdays 5 --monthdays 22,23,24,25,26,27,28 -j REJECT
iptables -t filter -I OUTPUT -p tcp --dport 80 -m time --datestart 2017-12-24 --datestop 2017-12-27 -j REJECT
connlimit 子產品
- –connlimit-above:單獨使用此選項時,表示限制每個IP的連結數量。
- –connlimit-mask:此選項不能單獨使用,在使用–connlimit-above選項時,配合此選項,則可以針對”某類IP段内的一定數量的IP”進行連接配接數量的限制,如果不明白可以參考上文的詳細解釋。
#示例
iptables -I INPUT -p tcp --dport 22 -m connlimit --connlimit-above 2 -j REJECT
iptables -I INPUT -p tcp --dport 22 -m connlimit --connlimit-above 20 --connlimit-mask 24 -j REJECT
iptables -I INPUT -p tcp --dport 22 -m connlimit --connlimit-above 10 --connlimit-mask 27 -j REJECT
limit 子產品
connlimit 子產品是對連接配接數量進行限制的,limit 子產品是對”封包到達速率”進行限制的。用大白話說就是,如果我想要限制機關時間内流入的包的數量,就能用 limit 子產品。我們可以以秒為機關進行限制,也可以以分鐘、小時、天作為機關進行限制。常用的擴充比對條件如下:
- –limit-burst:類比”令牌桶”算法,此選項用于指定令牌桶中令牌的最大數量。
- –limit:類比”令牌桶”算法,此選項用于指定令牌桶中生成新令牌的頻率,可用時間機關有second、minute 、hour、day。
示例表示限制外部主機對本機進行ping操作時,本機最多每6秒中放行一個ping包
#示例,注意,如下兩條規則需配合使用
#令牌桶中最多能存放3個令牌,每分鐘生成10個令牌(即6秒鐘生成一個令牌)。
iptables -t filter -I INPUT -p icmp -m limit --limit-burst 3 --limit 10/minute -j ACCEPT
#預設将icmp包丢棄
iptables -t filter -A INPUT -p icmp -j REJECT
state 擴充子產品
當我們通過 http 的 url 通路某個網站的網頁時,用戶端向服務端的 80 端口發起請求,服務端再通過 80 端口響應我們的請求,于是,作為用戶端,我們似乎應該理所應當的放行 80 端口,以便服務端回應我們的封包可以進入用戶端主機,于是,我們在用戶端放行了 80 端口,同理,當我們通過 ssh 工具遠端連接配接到某台伺服器時,用戶端向服務端的 22 号端口發起請求,服務端再通過 22 号端口響應我們的請求,于是我們理所應當的放行了所有 22 号端口,以便遠端主機的響應請求能夠通過防火牆,但是,作為用戶端,如果我們并沒有主動向 80 端口發起請求,也沒有主動向 22 号端口發起請求,那麼其他主機通過 80 端口或者 22 号端口向我們發送資料時,我們可以接收到嗎?應該是可以的,因為我們為了收到 http 與 ssh 的響應封包,已經放行了 80 端口與 22 号端口,是以,不管是”響應”我們的封包,還是”主動發送”給我們的封包,應該都是可以通過這兩個端口的,那麼仔細想想,這樣是不是不太安全呢?此時 state 擴充子產品就派上用場了。
對于 state 子產品的連接配接而言,”連接配接”其中的封包可以分為5種狀态,分别為:
- NEW:連接配接中的第一個包,狀态就是 NEW,我們可以了解為新連接配接的第一個包的狀态為 NEW。
- ESTABLISHED:我們可以把 NEW 狀态包後面的包的狀态了解為 ESTABLISHED,表示連接配接已建立。
- RELATED:從字面上了解 RELATED 譯為關系,但是這樣仍然不容易了解,我們舉個例子。比如 FTP 服務,FTP 服務端會建立兩個程序,一個指令程序,一個資料程序。指令程序負責服務端與用戶端之間的指令傳輸(我們可以把這個傳輸過程了解成 state 中所謂的一個”連接配接”,暫稱為”指令連接配接”)。資料程序負責服務端與用戶端之間的資料傳輸 (我們把這個過程暫稱為”資料連接配接”)。但是具體傳輸哪些資料,是由指令去控制的,是以,”資料連接配接”中的封包與”指令連接配接”是有”關系”的。那麼,”資料連接配接”中的封包可能就是 RELATED 狀态,因為這些封包與”指令連接配接”中的封包有關系。(注:如果想要對ftp進行連接配接追蹤,需要單獨加載對應的核心子產品 nf_conntrack_ftp,如果想要自動加載,可以配置 /etc/sysconfig/iptables-config 檔案)
- INVALID:如果一個包沒有辦法被識别,或者這個包沒有任何狀态,那麼這個包的狀态就是 INVALID,我們可以主動屏蔽狀态為 INVALID 的封包。
- UNTRACKED:封包的狀态為 untracked 時,表示封包未被追蹤,當封包的狀态為 Untracked 時通常表示無法找到相關的連接配接。
剛才舉例中的問題即可使用 state 擴充子產品解決,我們隻要放行狀态為 ESTABLISHED 的封包即可,因為如果封包的狀态為 ESTABLISHED,那麼封包肯定是之前發出的封包的回應,這樣,就表示隻有回應我們的封包能夠通過防火牆,如果是别人主動發送過來的新的封包,則無法通過防火牆:
iptables -t filter -I INPUT -m state --state ESTABLISHED -j ACCEPT
mangle 表
mangle 表的主要功能是根據規則修改資料包的一些标志位,以便其他規則或程式可以利用這種标志對資料包進行過濾或政策路由。mangle 表主要有以下 3 種操作:
- TOS:用來設定或改變資料包的服務類型域。這常用來設定網絡上的資料包如何被路由等政策。注意這個操作并不完善,有時得不所願。它在Internet 上還不能使用,而且很多路由器不會注意到這個域值。換句話說,不要設定發往 Internet 的包,除非你打算依靠 TOS 來路由,比如用 iproute2。
- TTL:用來改變資料包的生存時間域,我們可以讓所有資料包隻有一個特殊的 TTL。它的存在有 一個很好的理由,那就是我們可以欺騙一些ISP。為什麼要欺騙他們呢?因為他們不願意讓我們共享一個連接配接。那些 ISP 會查找一台單獨的計算機是否使用不同的 TTL,并且以此作為判斷連接配接是否被共享的标志。
- MARK 用來給包設定特殊的标記。iproute 2能識别這些标記,并根據不同的标記(或沒有标記) 決定不同的路由。用這些标記我們可以做帶寬限制和基于請求的分類。
例如内網的用戶端通過 Linux 主機連入 Internet,而 Linux 主機與Internet 連接配接時有兩條線路,它們的網關如圖所示。現要求對内網進行政策路由,所有通過 TCP 協定通路 80 端口的資料包都從 ChinaNet 線路出去,而所有通路 UDP 協定 53 号端口的資料包都從 Cernet 線路出去。
這是一個政策路由的問題,為了達到目的,在對資料包進行路由前,要先根據資料包的協定和目的端口給資料包做上一種标志,然後再指定相應規則,根據資料包的标志進行政策路由。為了給特定的資料包做上标志,需要使用mangle 表,mangle 表共有 5 條鍊,由于需要在路由選擇前做标志,是以應該使用 PREROUTING 鍊,下面是具體的指令:
iptables -t mangle -A PREROUTING -i eth0 -p tcp --dport 80 -j MARK --set-mark 1;
iptables -t mangle -A PREROUTING -i eth0 -p udp --dprot 53 -j MARK --set-mark 2;
資料包經過 PREROUTING 鍊後,将要進入路由選擇子產品,為了對其進行政策路由,執行以下兩條指令,添加相應的規則:
ip rule add from all fwmark 1 table 10
ip rule add from all fwmark 2 table 20
以上兩條指令表示所有标志是1的資料包使用路由表 10 進行路由,而所有标志是 2 的資料包使用路由表 20 進行路由。路由表 10 和 20 分别使用了 ChinaNet 和 Cernet 線路上的網關作為預設網關,具體設定指令如下所示:
ip route add default via 10.10.1.1 dev eth1 table 10
ip route add default via 10.10.2.1 dev eth2 table 20
以上兩條指令在路由表 10 和 20 上分别指定了 10.10.1.1 和 10.10.2.1 作為預設網關,它們分别位于 ChinaNet 和 Cernet 線路上。于是,使用路由表 10 的資料包将通過 ChinaNet 線路出去,而使用路由表20的資料包将通過 Cernet 線路出去。
自定義鍊
當預設鍊中的規則非常多時,不友善我們管理。想象一下,如果 INPUT 鍊中存放了 200 條規則,這 200 條規則有針對 httpd 服務的,有針對 sshd 服務的,有針對私網 IP 的,有針對公網 IP 的,假如,我們突然想要修改針對 httpd 服務的相關規則,難道我們還要從頭看一遍這 200 條規則,找出哪些規則是針對 httpd 的嗎?這顯然不合理。
是以,iptables 中,可以自定義鍊,通過自定義鍊即可解決上述問題。假設,我們自定義一條鍊,鍊名叫 IN_WEB,我們可以将所有針對 80 端口的入站規則都寫入到這條自定義鍊中,當以後想要修改針對 web 服務的入站規則時,就直接修改 IN_WEB 鍊中的規則就好了,即使預設鍊中有再多的規則,我們也不會害怕了,因為我們知道,所有針對 80 端口的入站規則都存放在IN_WEB鍊中。
建立自定義鍊
#在filter表中建立IN_WEB自定義鍊
iptables -t filter -N IN_WEB
引用自定義鍊
#在INPUT鍊中引用剛才建立的自定義鍊
iptables -t filter -I INPUT -p tcp --dport 80 -j IN_WEB
重命名自定義鍊
#将IN_WEB自定義鍊重命名為WEB
iptables -E IN_WEB WEB
删除自定義鍊
删除自定義鍊需要滿足兩個條件:
- 1、自定義鍊沒有被引用。
- 2、自定義鍊中沒有任何規則。
#第一步:清除自定義鍊中的規則
iptables -t filter -F WEB
#第二步:删除自定義鍊
iptables -t filter -X WEB
LOG 動作
LOG 動作預設會将封包的相關資訊記錄在/var/log/message檔案中,當然,我們也可以将相關資訊記錄在指定的檔案中,以防止 iptables 的相關資訊與其他日志資訊相混淆,修改 /etc/rsyslog.conf 檔案(或者 /etc/syslog.conf),在 rsyslog 配置檔案中添加如下配置即可:
kern.warning /var/log/iptables.log
完成上述配置後,重新開機rsyslog服務(或者syslogd):
service rsyslog restart
LOG 動作也有自己的選項,常用選項如下:
- –log-level 選項可以指定記錄日志的日志級别,可用級别有 emerg,alert,crit,error,warning,notice,info,debug。
- –log-prefix 選項可以給記錄到的相關資訊添加”标簽”之類的資訊,以便區分各種記錄到的封包資訊,友善在分析時進行過濾。–log-prefix 對應的值不能超過 29 個字元。
比如,我想要将主動連接配接22号端口的封包的相關資訊都記錄到日志中,并且把這類記錄命名為”want-in-from-port-22″,則可以使用如下指令:
iptables -I INPUT -p tcp --dport 22 -m state --state NEW -j LOG --log-prefix "want-in-from-port-22"
完成上述配置後,我在IP位址為 192.168.1.98 的用戶端機上,嘗試使用 ssh 工具連接配接上例中的主機,然後檢視對應的日志檔案(已經将日志檔案設定為 /var/log/iptables.log):
如上圖所示,ssh 連接配接操作的封包的相關資訊已經被記錄到了 iptables.log 日志檔案中,而且這條日志中包含”标簽”:want-in-from-port-22,如果有很多日志記錄,我們就能通過這個”标簽”進行篩選了,這樣友善我們檢視日志,同時,從上述記錄中還能夠得知封包的源IP與目标IP,源端口與目标端口等資訊,從上述日志我們能夠看出,192.168.1.98 這個 IP 想要在 14點11分 連接配接到 192.168.1.139(目前主機的 IP)的22号端口,封包由eth4網卡進入,eth4 網卡的 MAC 位址為 00:0c:29:b7:f4:d1,用戶端網卡的 MAC 位址為 f4:8e:38:82:b1:29。