數(shù)據(jù)包如何游走于 Iptables 規(guī)則之間?
在前文《Linux 路由三大件》中,我們提到了?iptables?可以修改數(shù)據(jù)包的特征從而影響其路由。這個功能無論是傳統(tǒng)場景下的?防火墻,還是云原生場景下的?服務(wù)路由(k8s service)、網(wǎng)絡(luò)策略(calico network policy) 等都有依賴。
雖然業(yè)界在積極地推進(jìn)?ipvs、ebpf?等更新、更高效的技術(shù)落地,但是出于各種各樣的原因(技術(shù)、成本、管理等),iptables?仍然有著非常廣泛的使用 ,所以我們有必要了解?iptables?的方方面面。今天我們就來了解其中的重要一環(huán):調(diào)試和追蹤?iptables。更具體一點:一個數(shù)據(jù)包是怎么在?iptables?的各個?chain/table/rule?中游走的。
注:不了解?iptables?的同學(xué)可以先看看這篇 wiki?https://wiki.archlinux.org/title/Iptables
trace
我們知道,iptables?的 rule 最后是一個 target,這個 target 可以設(shè)置為 TRACE。這樣,匹配這個 rule 的包經(jīng)過的所有?chain/table/rule?都會被記錄下來。
這個 rule 本身只能被放在?raw
?表里,可以存在于?PREROUTING
?和?OUTPUT
?兩個 chain 中。其匹配規(guī)則和其他一般的 rule 沒有什么差別。
實踐
我們采用一個 docker 場景來進(jìn)行實踐,如下圖:

亦即宿主機的 10000 端口映射給了容器的 80 端口。圖中有兩種測試場景,分別是:
本節(jié)點訪問容器 IP
跨節(jié)點訪問宿主機 IP
為了讓分析更直觀/簡單,我們用?telnet?測試即可(curl?會產(chǎn)生很多后續(xù)?http?包,不夠簡潔)。
OK,開始我們的測試!
首先在宿主機上開啟 iptables log
然后增加 trace 配置
查看結(jié)果可以用這個命令:cat /var/log/messages | grep "TRACE:"
本節(jié)點訪問:telnet 172.19.0.9 80
,結(jié)果:
跨節(jié)點訪問:telnet 192.168.64.4 10000
,結(jié)果:
【文章福利】小編推薦自己的Linux內(nèi)核技術(shù)交流群:【749907784】整理了一些個人覺得比較好的學(xué)習(xí)書籍、視頻資料共享在群文件里面,有需要的可以自行添加哦?。。。ê曨l教程、電子書、實戰(zhàn)項目及代碼)? ?


對比這張圖

以及宿主機節(jié)點上具體的規(guī)則(刪除了 docker0 相關(guān))
*raw
:PREROUTING ACCEPT [312:33617]
:OUTPUT ACCEPT [335:24610]
-A PREROUTING -d 192.168.64.4/32 -p tcp -m tcp --dport 10000 -j TRACE
-A OUTPUT -d 172.19.0.0/16 -p tcp -m tcp --dport 80 -j TRACE
COMMIT
*nat
:PREROUTING ACCEPT [4:903]
:INPUT ACCEPT [4:903]
:OUTPUT ACCEPT [222:16027]
:POSTROUTING ACCEPT [224:16155]
:DOCKER - [0:0]
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
-A POSTROUTING -s 172.19.0.0/16 ! -o br-00ea7870520a -j MASQUERADE
-A POSTROUTING -s 172.19.0.9/32 -d 172.19.0.9/32 -p tcp -m tcp --dport 80 -j MASQUERADE
-A DOCKER -i br-00ea7870520a -j RETURN
分析內(nèi)容可以知:
兩個測試都是 TCP 三步握手的第一、三步的兩個包,從包的 SEQ 字段可以驗證,也可以用 iptables rule 的 pkts 字段的差異來進(jìn)行驗證
對于 TCP 鏈接,只有第一個包才會經(jīng)過 nat,后續(xù)包屬于?
RELATED,ESTABLISHED
,直接放過,不需要重新 nat對于跨節(jié)點訪問,第一個包首先來到?
PREROUTING
?鏈, 命中了 docker 添加的?DNAT
?規(guī)則:nat:DOCKER
(rule 3
),效果就是第三行到第四行?DST
?從?192.168.64.4
?變成了?172.19.0.9
DNAT
?后走到了?Forward
?鏈,?DOCKER-USER
?和?DOCKER-ISOLATION-STAGE-1
?都?return
?了。最后通過?filter:FORWARD:rule:8
?來到?filter:DOCKER
?上,accept
?將包送到了?docker bridge: br-00ea7870520a
?上,從而送進(jìn)了容器 172.19.0.9。當(dāng)然,第二個包依然屬于?RELATED,ESTABLISHED
,所以在?filter:FORWARD:rule:7
?上直接?ACCEPT
,不需要再走?filter:DOCKER
?了。
補充
最后補充一下,對于較新的內(nèi)核,iptables 是?nf_tables
?而不是?iptables-legacy
?的場景下:
用?nft monitor trace # xtables-monitor --trace
?命令來觀察結(jié)果(這個方式比 messages 更友好:每個包都有一個單獨的 id,更直觀),當(dāng)然,trace 方式也有差別,此處不展開,感興趣讀者可以自行到參考鏈接中進(jìn)行研究。
原文作者:九零后程序員
