轉載自https://www.idzn.cn/?post=206
-m conntrack --ctstate ESTABLISHED,RELATED -j ...
-m state --state ESTABLISHED,RELATED -j ...
我們要對一條連接做處理,可以用以上兩種match target來實現。
iptables是最常用的一種Linux主機防火牆,借助於netfilter優秀的性能和擴展,雖歷經多年,但仍不落伍。OpenStack中安全組功能,floating IP的實現,以及fwaas的主流方案大多是依賴iptables而實現的。
與包過濾防火牆不同的是,iptables是一種狀態防火牆,可以記錄和跟蹤數據流的狀態。正是基於此,才會有以上優秀方案的落地。
從Linux2.6.15的內核版本后,iptables開始支持狀態跟蹤(conntrack),該功能依賴於netfilter的內核模塊nf_conntrack。此后,iptables可以根據包的狀態進行二次的過濾攔截和狀態跟蹤。它也是state/ctstate和nat的主要依賴模塊。
狀態跟蹤
conntrack將數據流的狀態信息以Hash表的形式儲存在內存中,包括五元組信息以及超時時間等。這里說的狀態跟蹤並非是指狀態協議(如TCP)中連接狀態的跟蹤,而是conntrack特有的與網絡傳輸協議無關的狀態的跟蹤。
下面介紹一下conntrack中的狀態分類以及在TCP和UDP這兩種4層協議中,是如何進行狀態跟蹤的。
五種狀態
conntrack共可以為連接標記五種狀態,分別如下:
NEW:
新建連接請求的數據包,且該數據包沒有和任何已有連接相關聯。
判斷的依據是conntrack當前“只看到一個方向數據包(UNREPLIED)”,沒有回包。
ESTABLISHED:
該連接是某NEW狀態連接的回包,也就是完成了連接的雙向關聯。
RELATED:
匹配那些屬於helper模塊定義的特殊協議的網絡連接,該連接屬於已經存在的一個ESTABLISHED連接的衍生連接。
簡而言之,A連接已經是ESTABLISHED,而B連接如果與A連接相關,那么B連接就是RELATED。這部分不理解沒有關系,也很難一句話說清,后面章節會用大量筆墨來闡明它。
INVALID:
匹配那些無法識別或沒有任何狀態的數據包。這可能是由於系統內存不足或收到不屬於任何已知連接的ICMP錯誤消息,也就是垃圾包,一般情況下我們都會DROP此類狀態的包。
UNTRACKED :
這是一種特殊狀態,或者說並不是狀態。它是管理員在raw表中,為連接設置NOTRACK規則后的狀態。這樣做,便於提高包過濾效率以及降低負載。
conntrack是一種狀態跟蹤和記錄的機制,本身並不能過濾數據包,只是提供包過濾的依據。 有狀態是一種過濾依據,無狀態實際也是一種過濾依據。
因此,需要小心的是,設置了NOTRACK,不代表是放行,只是它的狀態是UNTRACKED。所以如果想要對這種包放行或者處理,同樣需要配置相應的規則。具體使用方法可以查看下面“設置NOTRACK”部分的內容。
TCP的狀態跟蹤
從TCP開始討論的原因也是因為TCP本身是狀態協議。TCP通過三次握手建立連接,分別是SYN,SYN/ACK和ACK,當完成之后連接成為ESTABLISHED狀態。也就是說TCP完成ESTABLISHED的時候,C和S兩端已經進行了三次交互。
對於iptables而言,每一次握手,都需要對連接過濾,如前面所說NEW和ESTABLISHED狀態,分別指的是,當nf_conntrack第一次發現該連接的時候,會將其狀態設置為NEW,當反方向也出現包的時候,即認為是ESTABLISHED。如下圖,觀察在TCP的三次握手過程(SYN,SYN/ACK,ACK)中,conntrack狀態(NEW,ESTABLISHED)出現的時機。可以看到,第1次握手與第2次握手間是NEW,第2次握手之后就是ESTABLISHED。某種角度來說,可以認為兩次握手對conntrack而言就已經完成了ESTABLISHED
討論到這里,已經把本章節的兩個主角(TCP和conntrack中的ESTABLISHED狀態)請了出來,下面舉個具體的例子,看下TCP狀態的變化。
打開/proc/net/nf_conntrack,可以看到類似如下的內容。
先說下每行entry的意思,第1列是網絡層協議名稱;第2列是協議代號;第3列代表傳輸層協議名稱;第4列是傳輸層協議代號,其中tcp是6,udp是17;第5列的117指的是TTL;第6列是TCP的狀態;第7列是表示源目地址以及對應端口;第8列是表示是否收到回應包;第9列表示期望收到的回包的源目地址及端口。
ipv4 2 tcp 6 117 SYN_SENT src=192.168.1.5 dst=192.168.1.7 sport=1031 dport=23 [UNREPLIED] src=192.168.1.7 dst=192.168.1.5 sport=23 dport=1031 use=1
1
第1次握手時(SYN),TCP的狀態是SYN_SENT,且有 [UNREPLIED] 標記,意味着還沒有收到回包。此時,連接是NEW的狀態,這點很好理解。
接着往下看
ipv4 2 tcp 6 57 SYN_RECV src=192.168.1.5 dst=192.168.1.7 sport=1031 dport=23 src=192.168.1.7 dst=192.168.1.5 sport=23 dport=1031 use=1
1
第2次握手時(SYN/ACK),TCP連接的狀態為SYN_RECV,也就是說,kernel收到了回復(replay), 在初次握手時候的[UNREPLIED]的tag被去除。需要注意的是,此時conntrack已經是ESTABLISHED,再往下看
ipv4 2 tcp 6 431999 ESTABLISHED src=192.168.1.5 dst=192.168.1.7 sport=1031 dport=23 src=192.168.1.7 dst=192.168.1.5 sport=23 dport=1031 [ASSURED] use=1
1
第3次握手時(ACK),TCP連接的狀態變為ESTABLISHED,也就是說,Client發出了ACK的確認標記包,此時三次握手完畢,TCP連接建立。
TCP總結: 對Client而言,TCP狀態與conntrack狀態對比是SYN_SENT(NEW),SYN_RECV(ESTABLISHED),ESTABLISHED(ESTABLISHED);
配置iptables規則
對於Client是192.168.1.5:1031,Server是192.168.1.7:23。需求是Client可以主動連接Server,而反向主動的連接不可以。Client上防火牆規則如下:
#iptables -A INPUT -s 192.168.1.7 -p tcp --dport 1031 --sport 23 -m conntrack --ctstate ESTABLISHED -j ACCEPT
#iptables -A OUTPUT -d 192.168.1.7 -p tcp --sport 1031 --dport 23 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
1
2
3
4
5
6
在這里順便提及一下-m conntrack --ctstate與-m state --state的關系。
官方給的說法是這樣:
The conntrack match is an extended version of the state match, which makes it possible to match packets in a much more granular way. It let’s you look at information directly available in the connection tracking system, without any “frontend” systems, such as in the state match.
大意是ctstate是state的擴展版本(內核版本>=2.5開始支持),包括狀態參數也是基本相同,不用過多糾結,不過既然出了新的寫法,個人還是推薦新的寫法和規則。
UDP的狀態跟蹤
UDP是無狀態的傳輸協議,不需要三次握手也沒有SYN和ACK等各種標簽和狀態。雖然沒有三次握手的概念,但是我們還是來看三次連接的狀態記錄。
如下圖,觀察在UDP連接過程中,conntrack狀態(NEW,ESTABLISHED)出現的時機。可以看到,第1次連接與第2次連接間是NEW的狀態,第2次連接之后就是ESTABLISHED。對UDP而言,也可以認為兩次握手對conntrack而言就已經完成了ESTABLISHED
ipv4 2 udp 17 20 src=192.168.1.2 dst=192.168.1.5 sport=137 dport=1025 [UNREPLIED] src=192.168.1.5 dst=192.168.1.2 sport=1025 dport=137 use=1
1
首先可以看到的是UDP沒有SYN_SENT這種TCP特有的狀態標簽,但是有 [UNREPLIED] 的標識,說明這個包是初次發出的包,還沒有收到回應。此時conntrack中對應的狀態是NEW,接着往下看。
ipv4 2 udp 17 170 src=192.168.1.2 dst=192.168.1.5 sport=137 dport=1025 src=192.168.1.5 dst=192.168.1.2 sport=1025 dport=137 [ASSURED] use=1
1
同樣還是沒有標識,但是 [UNREPLIED] 變成了 [ASSURED] ,表明已經收到了回包,且連接建立完成。另外TTL變成了170,這時由於在該連接狀態下,默認的TTL是180,而第一次連接時默認值是30。此時conntrack中定義的狀態是ESTABLISHED。接着往下看
ipv4 2 udp 17 175 src=192.168.1.5 dst=195.22.79.2 sport=1025 dport=53 src=195.22.79.2 dst=192.168.1.5 sport=53 dport=1025 [ASSURED] use=1
1
Client發出第3次包之后,可以發現TTL變為了175,說明TTL更新了,也同時說明第二次和第三次中包的狀態或者說標簽是一致的,所以TTL默認值才會相同。
UDP總結: 對Client而言,UDP過程與conntrack狀態對比是,第一次連接(NEW),第二次連接(ESTABLISHED);
配置iptables規則
對於Client是192.168.1.5:1031,Server是192.168.1.7:23。需求是Client可以主動連接Server,而反向主動的連接不可以。Client上iptables規則如下:
#Client
#iptables -A INPUT -s 192.168.1.7 -p udp --dport 1031 --sport 23 -m conntrack --ctstate ESTABLISHED -j ACCEPT
#iptables -A OUTPUT -d 192.168.1.7 -p udp --sport 1031 --dport 23 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
1
2
3
4
5
6
回頭再看
通過對以上知識的整理,iptables的狀態跟蹤(conntrack)思想若隱若現。
網絡防火牆更關心的“進”和“出”,他有自己的考慮和規則,至於進出的包依循的是什么,他並不關心。
當連接初次出現的時候,該連接就是NEW,當出現了對應的反向連接的時候,那么該連接就是ESTABLISHED。看起來有點像UDP,是不是?
至於為什么會設計成這種模式,可能是考慮到防火牆只是涉及進出兩個方向,而兩次握手已經可以代表兩次的方向,也可能考慮到對UDP以及ICMP的兼容等問題,在這就不去深入討論了。