自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

認(rèn)真聊一次Iptables和Netfilter,簡(jiǎn)單過(guò)下Istio Route

開(kāi)發(fā) 架構(gòu)
本文會(huì)混用流量、數(shù)據(jù)包、網(wǎng)絡(luò)包這些名詞,準(zhǔn)確地說(shuō),它們指代的是內(nèi)核數(shù)據(jù)結(jié)構(gòu) Skb (sk_buffer)。

大家好,我是二哥。

上一篇文章本意是給大家一個(gè)新的視角來(lái)研究 istio route 的細(xì)節(jié)。不過(guò)后臺(tái)不少同學(xué)私信我說(shuō),一直沒(méi)有辦法理解 iptables ,也就不想細(xì)看那篇文章了。二哥一看就慌了,為了讓大家能安心地研究那篇文章,我就先來(lái)聊聊 iptables ,準(zhǔn)確地說(shuō)我們需要聊的是 netfilter 。

理解 iptables 其實(shí)不難,難的是看懂 iptables 是如何配合協(xié)議棧處理流量的。本篇除了聊 iptables 之外,更重要的是二哥會(huì)帶大家一探協(xié)議棧和 iptables 密切配合過(guò)程。最后我以 istio route 為例來(lái)看看它是如何利用 iptables 將網(wǎng)絡(luò)包透明地劫持到了 Envoy 的 Outbound hanlder 15001 端口。

本文會(huì)混用流量、數(shù)據(jù)包、網(wǎng)絡(luò)包這些名詞,準(zhǔn)確地說(shuō),它們指代的是內(nèi)核數(shù)據(jù)結(jié)構(gòu) skb (sk_buffer)。

這是一篇長(zhǎng)文,誠(chéng)意滿滿,干貨滿滿,掉發(fā)也滿滿。如果你已經(jīng)對(duì) iptables/netfilter 很熟悉了,那可以跳過(guò)前兩部分。

在開(kāi)始之前,我們先區(qū)分兩個(gè)概念:

  • netfilter:內(nèi)核中對(duì)數(shù)據(jù)包進(jìn)行控制、修改和過(guò)濾(manipulation and filtering)的框架 。一個(gè)著名的實(shí)現(xiàn)是內(nèi)核模塊 ip_tables 。
  • iptables:客戶端命令行,用于操作(CRUD)各種規(guī)則來(lái)干預(yù)內(nèi)核協(xié)議棧的行為。

大家日常工作中,碰到直接上手操作 netfilter 的機(jī)會(huì)越來(lái)越少了。但這不表示 netfilter 不重要。實(shí)際上 netfilter 是 K8s 網(wǎng)絡(luò)的基礎(chǔ),即使在kubernetes v1.8 中引入了 ipvs 模式,ipvs 的著力點(diǎn)也是 netfilter 。不信你看下面的一段規(guī)則。ipvs 介入工作的前提是它得作為規(guī)則的一部分,讓 netfilter 框架在合適的時(shí)機(jī)點(diǎn)運(yùn)行。

iptables -t nat -A POSTROUTING -m ipvs --vaddr 10.10.0.1 --vport 8410 -j MASQUERADE

肯定有另一部分同學(xué)有疑惑:既然平時(shí)都不怎么用它了,為什么還要學(xué)習(xí) netfilter 呢?

不知道大家有沒(méi)有另外一個(gè)疑惑:既然整個(gè)小學(xué)都不可能用到微積分,為什么小學(xué)的數(shù)學(xué)教師需要學(xué)高等數(shù)學(xué)呢?這其實(shí)涉及到處理問(wèn)題時(shí)的一個(gè)角度問(wèn)題:如果解決一個(gè)問(wèn)題只需要 3 分功力,你最好得具有 6 成內(nèi)功。只有這樣你才能俯視而不是仰視或者平視它,唯有俯視方得從容。

1、只看 iptables

文首提到 iptables 是客戶端命令,用于操作各種規(guī)則來(lái)干預(yù)內(nèi)核協(xié)議棧的行為。那它具體是如何使用的呢?

先來(lái)看命令長(zhǎng)什么樣子的:

iptables [-t <table-name>] <command> <chain-name> <parameter-1> <option-1> <parameter-n> <option-n>

再來(lái)看一個(gè)使用示例:

iptables -t filter -I INPUT -s 1.2.3.4 -p tcp --dport 22 -j ACCEPT  iptables -t filter -I INPUT -p tcp --dport 22 -j DROP

(1)四表五鏈

圖片

圖 1:四表五鏈(橫鏈豎表,橫為鏈,豎為表) 圖片來(lái)源:公眾號(hào)“開(kāi)發(fā)內(nèi)功修煉”

圖中橫向的四個(gè)框表示四表,對(duì)應(yīng)于 iptables 命令里面的 -t <table-name> 。如果不指定,那么默認(rèn)的 table 是 filter 。其實(shí)還有一個(gè) security 表,用于在數(shù)據(jù)包上應(yīng)用 SELinux ,這張表并不常用,故本篇我們略過(guò)。

圖中第 1 列表示的五個(gè)框叫做五鏈,對(duì)應(yīng)于命令里面的 <chain-name> 。每個(gè)鏈像竹簽一樣串著不少肉串。這些肉串叫規(guī)則,它們的種類不同,且由不同的表提供。比如 mangle 表可能提供的是羊肉串,而 nat 表提供的是牛肉串,filter 表提供的是雞肉串。

這四個(gè)表的具體作用我就不細(xì)講了,大家可以到網(wǎng)上搜索出更詳細(xì)的答案。但下面這兩點(diǎn)是重點(diǎn)(重點(diǎn),重點(diǎn),重點(diǎn)),你一定要記得。

  • 這五個(gè)預(yù)置的鏈直接源自于 Netfilter 的鉤子,它們與四張規(guī)則表的關(guān)系是固定的。用戶即不能增加自定義表也不能修改圖 1 中已有的表與鏈的關(guān)系,但可以增加自定義的鏈(見(jiàn)下文)。
    新增的自定義鏈與 Netfilter 的鉤子沒(méi)有天然的對(duì)應(yīng)關(guān)系。自定義鏈不會(huì)被自動(dòng)觸發(fā),只有顯式地使用 JUMP 行為(見(jiàn)后文),才能從默認(rèn)的五條鏈中跳轉(zhuǎn)過(guò)去執(zhí)行它們。
  • 每個(gè)命名空間都是有自己獨(dú)立的 iptables 規(guī)則,這當(dāng)然也包括四表五鏈。

表、鏈和規(guī)則之間的關(guān)系,一句話總結(jié)就是:規(guī)則是執(zhí)行的最小單元,鏈決定了規(guī)則被執(zhí)行的時(shí)機(jī),而表則限定了規(guī)則的類別。鏈的執(zhí)行時(shí)機(jī)詳見(jiàn)后文。

(2) command

命令中的 command 是啥?它是一些由大寫(xiě)字母表示的動(dòng)作。見(jiàn)圖 2 所示。比如 -A 用于將一個(gè)新規(guī)則插入到鏈上,嗯,就是把肉串插到竹簽上。每一次用 -A 這樣的 command 調(diào)用 iptables ,都會(huì)在對(duì)應(yīng)的鏈和表所形成的宮格里面插入一個(gè)新的規(guī)則。

圖片

圖 2:iptables command 列表

(3)自定義鏈

我們可以用 -N 來(lái)創(chuàng)建一個(gè)新鏈。如果不用 -t 來(lái)指定 table 的話,新建的 chain 默認(rèn)使用 filter table 。熟悉自定義 chain 的創(chuàng)建過(guò)程非常重要,因?yàn)楹笪奈覀円治龅?istio route 就自建了不少鏈。

二哥再?gòu)?qiáng)調(diào)一遍:自定義鏈不能被 netfilter 自動(dòng)執(zhí)行,只有從五大入口鏈那里通過(guò) -j target 才能跳轉(zhuǎn)到自定義鏈(例見(jiàn)后文)。

下面的例子里,自定義了一個(gè) chain LANCE-OUTPUT ,可以看到它被放到了 table filter 里面。

# 自定義 chain LANCE-OUTPUT[root@xx usr]# iptables -N LANCE-OUTPUT
[root@xx usr]# iptables -L -t filterChain INPUT (policy ACCEPT)target prot opt source destination
Chain FORWARD (policy ACCEPT)target prot opt source destination
Chain OUTPUT (policy ACCEPT)target prot opt source destination
Chain LANCE-OUTPUT (0 references)target prot opt source destination

然后用 -A 來(lái)追加一個(gè)規(guī)則到這個(gè)自定義鏈里面。

# 追加一個(gè)規(guī)則到自定義鏈 LANCE-OUTPUT[root@xx usr]# iptables -A LANCE-OUTPUT -m comment --comment "kubernetes service portals"
[root@xx usr]# iptables -L -t filterChain INPUT (policy ACCEPT)target prot opt source destination
Chain FORWARD (policy ACCEPT)target prot opt source destination
Chain OUTPUT (policy ACCEPT)target prot opt source destination
Chain LANCE-OUTPUT (0 references)target prot opt source destination all -- anywhere anywhere /* kubernetes service portals */

(4)parameter / option

光有 command 還不行,它太粗獷了,得細(xì)膩、得精準(zhǔn)控制。這就需要通過(guò) parameter 來(lái)實(shí)現(xiàn)。<parameter-1> <option-1> 里面填什么呢?看你喜歡,你有若干個(gè)選擇,比如文首示例里面的 -s 1.2.3.4 和 -p tcp --dport 22 。有一些 parameter 還提供了額外的以 -- 開(kāi)頭的 額外 match option ,比如對(duì) -p tcp ,你可以添加  --dport 22 這樣的額外 match option ,用以更精準(zhǔn)地控制要命中的規(guī)則。除了 tcp 外你還有  -p udp   -p icmp 可供選擇。

下面是可供使用的 parameter 列表。

圖片

圖 3:iptables parameter 列表

(5)額外的 match option

這就結(jié)束了嗎?不,還有大招沒(méi)有放。我們來(lái)看下面這個(gè)例子。例子很平淡,重點(diǎn)看 -m 。-m comment 表示這個(gè)規(guī)則需要加載 comment 模塊,從字面意思你大概能猜得出來(lái)它可以干啥。對(duì),就是給這條規(guī)則加點(diǎn)注釋。通過(guò) --comment xxx  這個(gè) option ,你可以添加最多 265 個(gè)字符的注釋,前文在介紹用 -A 命令追加規(guī)則到自定義鏈時(shí),從 iptables -L -t filter 的輸出里面你可以體驗(yàn)到這些注釋的作用。

iptables -A INPUT -i eth1 -m comment --comment "my local LAN"

通過(guò) -m 我們可以調(diào)用包括 set / ipvs 在內(nèi)的各種擴(kuò)展模塊。有多少模塊可以選擇呢?多到?jīng)]朋友,不信你到這個(gè)鏈接里面去看:https://ipset.netfilter.org/iptables-extensions.man.html#lbAD 。我估計(jì)應(yīng)該有 60 個(gè)左右。

(6)跳轉(zhuǎn)到特點(diǎn)的目標(biāo) -j

我們?cè)O(shè)置的規(guī)則匹配上數(shù)據(jù)包后,總得干點(diǎn)啥是吧,不然不是白廢老大勁了么。當(dāng)然, -j  不是必填項(xiàng),但你非得說(shuō)我就不想讓這個(gè)規(guī)則干具體的事情,也行!

我們可以給 -j 指定像 ACCEPT / DROP / QUEUE / RETURN 這樣的 netfilter 自帶的標(biāo)準(zhǔn) target ,也可以給它指定我們自定義的鏈,除此之外還有若干個(gè)像 SNAT / REDIRECT / SET 這樣的擴(kuò)展 target 可供我們使用。

比如下面這個(gè)例子中,就通過(guò) -j KUBE-SERVICES 跳轉(zhuǎn)到自定義鏈 KUBE-SERVICES 去了。

-A INPUT -m conntrack --ctstate NEW -m comment --comment "kubernetes service portals" -j KUBE-SERVICES

流量通過(guò) -j 跳轉(zhuǎn)到指定 target 之后會(huì)發(fā)生什么?這取決于 target 會(huì)對(duì)流量做啥:

  • 比如對(duì)于 DROP target ,你也能猜出結(jié)局是什么:不但流量會(huì)丟棄了,它更加不會(huì)到達(dá)傳輸層(見(jiàn)后文)。
  • 而對(duì)于 KUBE-SERVICES 這樣的 target,netfilter 會(huì)去執(zhí)行這個(gè)鏈所定義的各種規(guī)則。

還記得前文我們說(shuō)到的那默認(rèn)的五條鏈嗎?它們既是默認(rèn)的五條鏈更是 netfilter 施展拳腳的入口。從這些入口進(jìn)去,netfilter 可能會(huì)調(diào)用到若干個(gè)自定義鏈以及串在鏈上的多種多樣的規(guī)則。假如所有的規(guī)則都不會(huì)下流量下死手,那么這些規(guī)則執(zhí)行完后,就又回到入口處,也就是這五個(gè)默認(rèn)的鏈。

2、不能單看 iptables

其實(shí)讀懂和理解 iptables 規(guī)則并不難,難的是理解 netfilter 是如何和 TCP/IP 協(xié)議棧緊密集成和協(xié)作以控制流量的行為的。你們見(jiàn)過(guò)機(jī)場(chǎng)行李托運(yùn)輸送系統(tǒng)嗎?我們?cè)谥禉C(jī)口托運(yùn)的行李會(huì)穿過(guò)行李分揀大廳的各條分叉,兜兜轉(zhuǎn)轉(zhuǎn)才來(lái)到飛機(jī)貨艙里面。

無(wú)論是入口流量還是本地進(jìn)程產(chǎn)生的出口流量都如同我們?cè)谥禉C(jī)口托運(yùn)的行李,而 netfilter 和 TCP/IP 協(xié)議棧則扮演了那個(gè)行李分揀系統(tǒng)。

圖片

圖 4:四表五鏈與協(xié)議棧集成細(xì)節(jié)  圖片來(lái)源:公眾號(hào)“開(kāi)發(fā)內(nèi)功修煉”

既然說(shuō)到 netfilter 和 TCP/IP 協(xié)議棧則的緊密合作,那我們先看看協(xié)議棧部分。

圖中 ip_rcv() 是流量進(jìn)入 IP 層的入口,ip_forward() 是轉(zhuǎn)發(fā)流量的入口,而流量通過(guò) ip_output() 離開(kāi) IP 層。當(dāng) IP 層決定要把流量送往傳輸層的時(shí)候,它通過(guò) ip_local_deliver() 來(lái)完成,相對(duì)應(yīng)地,本地進(jìn)程想要把數(shù)據(jù)發(fā)送出去,需要借助 __ip_local_out() 。注意所有這些函數(shù)都在 IP 層。

協(xié)議棧在執(zhí)行這些不同的入口函數(shù)時(shí),會(huì)有選擇地查看四表五鏈里面的鏈和相應(yīng)的規(guī)則并執(zhí)行這些規(guī)則。而規(guī)則里面所定義的 target 也反過(guò)來(lái)影響協(xié)議棧下一步的行為。

(1)過(guò)客和山海

從圖 4 我們可以看得出來(lái),四表五鏈以及路由選擇其實(shí)是協(xié)議棧留出來(lái)給大家自由發(fā)揮的空間和口子。我們以圖中標(biāo)號(hào) ④ 這一步的 __ip_local_out() 為例,看看內(nèi)核是如何與這些開(kāi)口打交道的:

// net/ipv4/ip_output.cint __ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb){  // 省略其它代碼  return nf_hook(NFPROTO_IPV4, NF_INET_LOCAL_OUT,        net, sk, skb, NULL, skb_dst(skb)->dev,        dst_output);}
//include/net/dst.hstatic inline int dst_output(struct net *net, struct sock *sk, struct sk_buff *skb){ return INDIRECT_CALL_INET(skb_dst(skb)->output, ip6_output, ip_output, net, sk, skb);}
// net/ipv4/ip_output.cint ip_output(struct net *net, struct sock *sk, struct sk_buff *skb){ struct net_device *dev = skb_dst(skb)->dev, *indev = skb->dev; // 省略其它代碼 return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, net, sk, skb, indev, dev, ip_finish_output, !(IPCB(skb)->flags & IPSKB_REROUTED));}

可以看到在這個(gè)函數(shù)的最后一步,協(xié)議棧就開(kāi)始通過(guò) nf_hook() 去遍歷 OUTPUT 鏈里面的規(guī)則了。這也是為什么我們說(shuō) OUTPUT 鏈?zhǔn)俏彐溨坏脑颉?/span>

nf_hook() 在遍歷完 OUTPUT 鏈之后,就調(diào)用 dst_output() 來(lái)送別網(wǎng)絡(luò)包。而網(wǎng)絡(luò)包從此則需獨(dú)自一人進(jìn)入下一段旅程,過(guò)一會(huì)兒它將會(huì)遇到 ip_output() ,從那里離開(kāi) IP 層。

我們也可以看得出來(lái)對(duì)于發(fā)送流程, OUTPUT 鏈只是一個(gè)過(guò)客,網(wǎng)絡(luò)包在這一站稍作停留后還是要繼續(xù)奔赴山海,在后面的旅途中它會(huì)碰到協(xié)議棧其它代碼和其它鏈,比如在 ip_output() 里面,它會(huì)遇到 POSTROUTING 鏈。

(2)PREROUTING 鏈

讓我們把圖 4 仔細(xì)看一遍。

對(duì)于 ① ,當(dāng)流量從外部進(jìn)入網(wǎng)卡,ip_rcv() 負(fù)責(zé)將其接入 IP 層,PREROUTING 鏈先于路由選擇介入對(duì)流量的處理流程。比如下面的例子里,每一個(gè)原本想訪問(wèn) 8022 端口的流量的 dest IP 和 dest Port 全部都被改成 127.0.0.1:22 。

# iptables -t nat -A PREROUTING -p tcp -m tcp --dport 8022 -j DNAT --to-destination 127.0.0.1:22

dest IP 和 dest Port 全部都改成 127.0.0.1:22 ,你很容易就猜到:在接下來(lái)的路由選擇這一步,協(xié)議棧會(huì)把修改過(guò)之后的流量通過(guò) ② 送往本地進(jìn)程。

如果 dest IP 和 dest Port 被改成了 39.156.66.10:443 呢?流量不會(huì)被送往本機(jī),而是通過(guò) ③ 被 forward 離開(kāi)本機(jī)。當(dāng)然前提是本機(jī) forward 功能已經(jīng)開(kāi)啟了。

(3)INPUT 鏈

我們剛才說(shuō)流量最終是通過(guò) ip_local_deliver() 離開(kāi) IP 層并進(jìn)入傳輸層的。不過(guò)在這之前,還有一個(gè) INPUT 鏈等著它。流量能否被傳輸層處理還得看 INPUT 鏈?zhǔn)欠裨试S。

比如對(duì)于下面這條規(guī)則,它存放在 INPUT 鏈的 filter 表里面,當(dāng)發(fā)現(xiàn)流量是 tcp 協(xié)議,且訪問(wèn)的是本機(jī) 22 端口,就把流量丟棄掉,說(shuō)白話就是不允許任何人通過(guò) ssh 訪問(wèn)本機(jī)。于是對(duì)于任何進(jìn)來(lái)的流量,② 這條路就算走到頭了。ip_local_deliver() 不會(huì)被執(zhí)行,sshd 也就沒(méi)有機(jī)會(huì)收到這個(gè)請(qǐng)求。

iptables -t filter -I INPUT -p tcp --dport 22 -j DROP

又或者如果一切都很正常,不出意外的話,位于傳輸層的函數(shù) tcp_v4_rcv() 會(huì)接收到這個(gè)可能已經(jīng)被修改過(guò)之后的流量,從此流量開(kāi)始了它在傳輸層的旅程。

(4)FORWARD 鏈

當(dāng)路由選擇決定要把流量 forward 后,會(huì)調(diào)用 ip_forward() 開(kāi)始后續(xù)的 forward 處理流程,這個(gè)流程如 ③ 所示。如果你喜歡,你可以在 FORWARD 鏈中加入你喜歡的規(guī)則來(lái)控制流量從命運(yùn)。比如下面的例子:

-A FORWARD -m comment --comment "kubernetes forwarding rules" -j KUBE-FORWARD-A KUBE-FORWARD -m comment --comment "kubernetes forwarding rules" -m mark --mark 0x4000/0x4000 -j ACCEPT

(5)OUTPUT 鏈

① ② ③ 這三條數(shù)據(jù)流里面涉及到的三個(gè)鏈都是因網(wǎng)卡接收到了外部的流量引起的,它們都是被動(dòng)被執(zhí)行。而 OUTPUT 鏈則是因?yàn)楸镜爻绦蛳蛑鲃?dòng)發(fā)送流量而觸發(fā)執(zhí)行流程。

當(dāng)網(wǎng)絡(luò)包從傳輸層通過(guò) tcp_write_xmit() 來(lái)到 IP 層時(shí),首先迎接它的還是路由選擇。這一步會(huì)產(chǎn)生兩個(gè)重要的決定:

  • next-hop 是誰(shuí),也即由誰(shuí)來(lái)接收網(wǎng)絡(luò)包
  • 從本機(jī)哪個(gè) interface 離開(kāi)

你可能會(huì)困惑,不是由應(yīng)用程序?qū)懞玫?dest IP 來(lái)接收網(wǎng)絡(luò)包嗎?沒(méi)錯(cuò),不過(guò)那是最終接收者,在這中間還會(huì)有若干個(gè)設(shè)備會(huì)經(jīng)手并傳遞這個(gè)網(wǎng)絡(luò)包。這就好像你從南京快遞一個(gè) iPhone 給遠(yuǎn)在北京的女友。當(dāng)然最終是你的女友負(fù)責(zé)接收、拆開(kāi)這個(gè)包裹,但在她拿到包裹之前,有非常多的快遞站中轉(zhuǎn)站、快遞小哥也會(huì)觸碰到它。這里所說(shuō)的 next-hop 就是負(fù)責(zé)收取快遞的第一個(gè)人。

如果這臺(tái)設(shè)備有多個(gè)網(wǎng)卡的話,得選擇其中一個(gè)網(wǎng)卡來(lái)將網(wǎng)絡(luò)包傳送出去。

ip_output() 負(fù)責(zé)將網(wǎng)絡(luò)包送離 IP 層,但且慢,看到 ④ 那里的 OUTPUT 鏈了嗎?是的,這次輪到它大顯身手了。我們可以在這里對(duì)包做一次 SNAT ,使得它離開(kāi)本機(jī)的時(shí)候,源地址使用本機(jī)的 IP 地址。關(guān)于 OUTPUT 鏈具體的例子我們留到最后聊 istio route 的時(shí)候再細(xì)說(shuō)。

3、通過(guò) loopback 通信

問(wèn)大家一個(gè)問(wèn)題:現(xiàn)有下面這兩個(gè)網(wǎng)絡(luò)通信場(chǎng)景:

場(chǎng)景一:本機(jī)同一個(gè) network namespace 下面的兩個(gè)進(jìn)程之間通過(guò) loopback(后文簡(jiǎn)稱 lo) 設(shè)備進(jìn)行網(wǎng)絡(luò)通信,如圖 5 所示。

場(chǎng)景二:一個(gè)局域網(wǎng)內(nèi),連接在同一個(gè)交換機(jī)上的兩臺(tái)主機(jī)上的兩個(gè)進(jìn)程相互進(jìn)行網(wǎng)絡(luò)通信,如圖 6 所示。

這兩個(gè)場(chǎng)景下,除去鏈路層設(shè)備的不同所帶來(lái)的二層收發(fā)數(shù)據(jù)的區(qū)別外,內(nèi)核協(xié)議棧對(duì)數(shù)據(jù)包的處理過(guò)程有本質(zhì)的不同嗎?從圖中你也可以看出來(lái),無(wú)論是圖 5 還是圖 6,數(shù)據(jù)均需要走完如下的過(guò)程:

發(fā)端應(yīng)用層 -> 發(fā)端傳輸層 -> 發(fā)端網(wǎng)絡(luò)層 -> 發(fā)端鏈路層 ->(物理層數(shù)據(jù)收發(fā))-> 收端鏈路層 -> 收端網(wǎng)絡(luò)層 -> 收端傳輸層 -> 收端應(yīng)用層

對(duì)于圖 6,上述這個(gè)過(guò)程大家應(yīng)該沒(méi)有任何異議。重點(diǎn)是對(duì)于圖 5 所示的場(chǎng)景:即便通過(guò) loopback 設(shè)備通信,網(wǎng)絡(luò)包還是要兩次完整地穿越協(xié)議棧。注意我這里的用詞:完整地。理解這點(diǎn)非常重要,因?yàn)楹竺嬉谩?/span>

圖片

圖 5:通信場(chǎng)景一:兩個(gè)進(jìn)程相互之間通過(guò) loopback 設(shè)備通信

圖片

圖 6:通信場(chǎng)景二:LAN 通信

4、簡(jiǎn)單過(guò)下 istio route

二哥需要強(qiáng)調(diào)一個(gè)重點(diǎn):圖 7 所示的包括四表五鏈、conntrack 表、供路由選擇的路由表、接口 eth0 和 loopback 在內(nèi)的信息都是 network namespace 的一部分。對(duì)于一個(gè)進(jìn)程來(lái)說(shuō),這些要素其實(shí)就構(gòu)成了它發(fā)起和響應(yīng)網(wǎng)絡(luò)請(qǐng)求的基本環(huán)境。在正常情況下,一個(gè) Pod 里面所有的 container 都共享一個(gè) network ns,也就共享著這個(gè)基本環(huán)境。

一個(gè) network ns 如同一座圍城,圍住了所有的數(shù)據(jù)。

我們都知道 Linux 支持多個(gè) network namespace,這也就意味著類似這樣的基本環(huán)境會(huì)有若干份。當(dāng)然,在每個(gè)基本環(huán)境里面,像四表五鏈、路由表之類的數(shù)據(jù)各有千秋。

我們可以將 TCP/IP 協(xié)議??闯墒浅绦虻拇a部分,而將上述的基本環(huán)境看成是程序的數(shù)據(jù)部分。很顯然 TCP/IP 棧應(yīng)該是被這個(gè) OS 上所有人共享的,無(wú)論是進(jìn)程還是容器,甚至是基于 qemu-kvm 的虛擬機(jī)都共享著宿主機(jī)的協(xié)議棧,但 network ns 所圍起來(lái)的數(shù)據(jù)卻是各個(gè) network ns 獨(dú)享的。

圖片

圖 7:Envoy 劫持網(wǎng)絡(luò)流量全景圖

下面我們將以 App container 想要訪問(wèn) www.baidu.com 443 端口為例來(lái)帶大家過(guò)一下 istio route 。

這個(gè)過(guò)程在圖 7 上來(lái)看,就是始于 App container internal logic 的,標(biāo)號(hào)為 ⑨ ~ ? ~ ? 的數(shù)據(jù)流。我們沿著箭頭的方向會(huì)發(fā)現(xiàn)網(wǎng)絡(luò)包被透明地劫持到了 Envoy 的 Outbound hanlder 15001 端口。我們重點(diǎn)分析 Envoy 是如何通過(guò) iptables 來(lái)做到這一點(diǎn)的。

(1)相關(guān) iptables

首先我們來(lái)看下與這個(gè)流程相關(guān)的 iptables 。為節(jié)省篇幅,突出重點(diǎn),二哥省去了其余的部分,只保留了 OUTPUT 鏈及其會(huì)調(diào)用到的自定義鏈。

# View the details of the rule configuration in the NAT table.$ iptables -t nat -L -v
# OUTPUT chain: jumps all outbound packets to the ISTIO_OUTPUT chain.Chain OUTPUT (policy ACCEPT 79 packets, 6761 bytes)pkts bytes target prot opt in out source destination15 900 ISTIO_OUTPUT tcp -- any any anywhere anywhere
# POSTROUTING CHAIN: All packets must first enter the POSTROUTING chain when they leave the network card, and the kernel determines whether they need to be forwarded out according to the packet destination.Chain POSTROUTING (policy ACCEPT 79 packets, 6761 bytes)pkts bytes target prot opt in out source destination
# ISTIO_IN_REDIRECT chain: jumps all inbound traffic to the local 15006 port, thus successfully blocking traffic to the sidecar.Chain ISTIO_IN_REDIRECT (3 references)pkts bytes target prot opt in out source destination0 0 REDIRECT tcp -- any any anywhere anywhere redir ports 15006
# ISTIO_OUTPUT chain: see the details bellowChain ISTIO_OUTPUT (1 references)pkts bytes target prot opt in out source destination0 0 RETURN all -- any lo 127.0.0.6 anywhere0 0 ISTIO_IN_REDIRECT all -- any lo anywhere !localhost owner UID match 13370 0 RETURN all -- any lo anywhere anywhere ! owner UID match 133715 900 RETURN all -- any any anywhere anywhere owner UID match 13370 0 ISTIO_IN_REDIRECT all -- any lo anywhere !localhost owner GID match 13370 0 RETURN all -- any lo anywhere anywhere ! owner GID match 13370 0 RETURN all -- any any anywhere anywhere owner GID match 13370 0 RETURN all -- any any anywhere localhost0 0 ISTIO_REDIRECT all -- any any anywhere anywhere
# ISTIO_REDIRECT chain: redirects all traffic to Sidecar (i.e. local) port 15001.Chain ISTIO_REDIRECT (1 references)pkts bytes target prot opt in out source destination0 0 REDIRECT tcp -- any any anywhere anywhere redir ports 15001

首先我們看到這段輸出是用命令 iptables -t nat -L -v 得到的。你看到 -t nat 了嗎?這表示這些規(guī)則全部都存放在 nat 表里面。我相信大家對(duì) NAT 有所耳聞,看到它也就大概猜得出來(lái)幾分:既然這些規(guī)則是與 NAT 表相關(guān)的,那么它們干的事情也就涉及到修改 IP 地址或端口這樣的操作。

我把這些規(guī)則之間的關(guān)系用圖 8 表示出來(lái)了。我們來(lái)看看它們是如何協(xié)作的。

圖片

圖 8:istio route 自定義鏈 ISTIO_OUTPUT 細(xì)節(jié)

(2)協(xié)作細(xì)節(jié)

首先當(dāng) App cotainer 訪問(wèn) baidu.com 時(shí),請(qǐng)求從傳輸層出來(lái)后,首先需要經(jīng)過(guò)一次路由。這個(gè)時(shí)候協(xié)議棧也僅僅知道這個(gè)包的目的 IP(39.156.66.10) 和 目的端口(443),還不知道它的二層信息是什么。為什么呢?得經(jīng)過(guò)路由后,才能知道包需要從本地哪個(gè)接口離開(kāi),以及誰(shuí)是 next-hop ,也只有當(dāng)知曉了這些信息后,才能填充二層頭的 src MAC 和 dest MAC。因?yàn)?IP 地址是與接口綁在一起的,所以從哪里接口離開(kāi)也就決定了 src IP 是什么。

路由選擇細(xì)節(jié)就不細(xì)講了。我們先把它看作一個(gè)黑盒子,經(jīng)過(guò)它之后,協(xié)議棧做了一個(gè)決定:去 39.156.66.10 的話,得從接口 eth0 離開(kāi)。再?gòu)?qiáng)調(diào)一次,eth0 位于 App cotainer 所在的 network namespace 里面。

按照前文所述的協(xié)議棧和 netfilter 配合流程,我們現(xiàn)在知道路由選擇后,緊接著需要執(zhí)行 OUTPUT 鏈里面的規(guī)則。

? OUTPUT 鏈?zhǔn)俏宕笕肟阪溨?,可在這里,它啥都沒(méi)干,直接把活外包給自定義鏈 ISTIO_OUTPUT 了。我們可以看到鏈 ISTIO_OUTPUT 上掛了 9 個(gè)規(guī)則。

圖片

圖 9:ISTIO_OUTPUT 鏈上的 9 個(gè)規(guī)則

規(guī)則 1,2,3,5,6 都不滿足條件,因?yàn)槲覀冃枰獜慕涌?eth0 離開(kāi),而不是 lo ,當(dāng)然這兩個(gè)接口都屬于這個(gè) Pod 所使用的 network ns。

規(guī)則 4,7 對(duì)目的地的 owner UID 和 GID 做了限制,不符合我們的場(chǎng)景。

規(guī)則 8 的目的地是 localhost,而我們想要去 39.156.66.10。很抱歉,完美錯(cuò)過(guò)。

最后就剩規(guī)則 9 了。這個(gè)規(guī)則非常粗礦,啥都行,碰到這個(gè)它,大家就只能全部乖乖地跳轉(zhuǎn)到自定義鏈 ISTIO_REDIRECT 了。

? 這一步表示了這樣的跳轉(zhuǎn)過(guò)程。

? 自定義鏈 ISTIO_REDIRECT 也是人狠話不多。只要傳輸層是 TCP 的流量,全部統(tǒng)一 REDIRECT 到 127.0.0.1:10051 。

Chain ISTIO_REDIRECT (1 references)pkts bytes target     prot opt in     out     source               destination  0     0 REDIRECT    tcp  --  any    any     anywhere             anywhere             redir ports 15001

我們來(lái)看看 target REDIRECT 會(huì)對(duì)流量干啥事。

REDIRECT
This target is only valid in the nat table, in the PREROUTING and OUTPUT chains, and user-defined chains which are only called from those chains. It redirects the packet to the machine itself by changing the destination IP to the primary address of the incoming interface (locally-generated packets are mapped to the localhost address, 127.0.0.1 for IPv4 and ::1 for IPv6).

說(shuō)白了,它會(huì)把 dest IP 和 dest Port 改成 127.0.0.1:15001 。這好理解,畢竟只有這樣,在圖 8 ? 處路由的時(shí)候, IP 層才會(huì)把從 App container 出來(lái)的流量路由至 listen 在 15001 的 Outbound hanlder 去處理。

這一步做完后,從 ? 開(kāi)始的 OUTPUT 鏈遍歷執(zhí)行過(guò)程就結(jié)束了。那結(jié)束之后下一步協(xié)議棧要干什么呢?

跟著 ? ? ? ? ? 走一遍,你會(huì)知道全部的答案。不過(guò)注意看 ? 那里所標(biāo)示的目的 IP 和目的端口,它們已經(jīng)被改掉了。既然目的 IP 已經(jīng)被改成了 127.0.0.1 了,那在 ? ? 那里發(fā)生的就是前文所述的通過(guò) loopback 通信所涉及的技術(shù)細(xì)節(jié)了。

責(zé)任編輯:姜華 來(lái)源: 二哥聊云原生
相關(guān)推薦

2022-10-28 11:26:01

光纖終端盒光纖安裝

2011-03-15 10:00:01

NetfilterIPTables

2021-04-15 12:10:42

Go語(yǔ)言Go開(kāi)發(fā)者

2010-12-07 09:51:43

Linux安全性netfilteriptables

2011-03-15 15:47:34

netfilteriptables

2011-03-15 15:47:30

netfilteriptables安裝

2011-03-15 12:47:11

netfilteriptables

2023-03-05 18:40:39

iptables防火墻軟件

2022-03-08 16:10:38

Redis事務(wù)機(jī)制

2018-01-10 14:13:04

測(cè)試矩陣API測(cè)試

2016-10-11 11:38:06

程序員

2011-03-15 15:47:26

netfilteriptables

2021-04-14 20:10:50

Netfileter Iptables 源碼

2011-03-15 15:51:12

netfilteriptables

2011-03-15 09:10:43

iptables防火墻

2015-07-14 10:34:42

ViewModel代碼高效

2023-10-07 08:17:40

公平鎖非公平鎖

2010-07-14 09:48:14

IMAP設(shè)置

2009-05-22 17:05:52

2011-03-15 10:48:47

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)