使用 Cilium 增強(qiáng) Kubernetes 網(wǎng)絡(luò)安全
TL;DR
在本篇,我們分別使用了 Kubernetes 原生的網(wǎng)絡(luò)策略和 Cilium 的網(wǎng)絡(luò)策略實(shí)現(xiàn)了 Pod 網(wǎng)絡(luò)層面的隔離。不同的是,前者只提供了基于 L3/4 的網(wǎng)絡(luò)策略;后者支持 L3/4、L7 的網(wǎng)絡(luò)策略。
通過(guò)網(wǎng)絡(luò)策略來(lái)提升網(wǎng)絡(luò)安全,可以極大降低了實(shí)現(xiàn)和維護(hù)的成本,同時(shí)對(duì)系統(tǒng)幾乎沒(méi)有影響。
尤其是基于 eBPF 技術(shù)的 Cilium,解決了內(nèi)核擴(kuò)展性不足的問(wèn)題,從內(nèi)核層面為工作負(fù)載提供安全可靠、可觀(guān)測(cè)的網(wǎng)絡(luò)連接。
背景
為什么說(shuō) Kubernetes 網(wǎng)絡(luò)存在安全隱患?集群中的 Pod 默認(rèn)是未隔離的,也就是 Pod 之間的網(wǎng)絡(luò)是互通的,可以互相通信的。
這里就會(huì)有問(wèn)題,比如由于數(shù)據(jù)敏感服務(wù) B 只允許特定的服務(wù) A 才能訪(fǎng)問(wèn),而服務(wù) C 無(wú)法訪(fǎng)問(wèn) B。要禁止服務(wù) C 對(duì)服務(wù) B 的訪(fǎng)問(wèn),可以有幾種方案:
在 SDK 中提供通用的解決方案,實(shí)現(xiàn)白名單的功能。首先請(qǐng)求要帶有來(lái)源的標(biāo)識(shí),然后服務(wù)端可以接收規(guī)則設(shè)置放行特定標(biāo)識(shí)的請(qǐng)求,拒絕其他的請(qǐng)求。 云原生的解決方案,使用服務(wù)網(wǎng)格的 RBAC、mTLS 功能。RBAC 實(shí)現(xiàn)原理與應(yīng)用層的 SDK 方案類(lèi)似,但是屬于基礎(chǔ)設(shè)施層的抽象通用方案;mTLS 則會(huì)更加復(fù)雜一些,在連接握手階段進(jìn)行身份驗(yàn)證,涉及證書(shū)的簽發(fā)、驗(yàn)證等操作。
以上兩種方案各有利弊:
SDK 的方案實(shí)現(xiàn)簡(jiǎn)單,但是規(guī)模較大的系統(tǒng)會(huì)面臨升級(jí)推廣困難、多語(yǔ)言支持成本高等問(wèn)題。 服務(wù)網(wǎng)格的方案是基礎(chǔ)設(shè)施層的通用方案,天生支持多語(yǔ)言。但是對(duì)于未落地網(wǎng)格的用戶(hù)來(lái)說(shuō),架構(gòu)變化大,成本高。如果單純?yōu)榱私鉀Q安全問(wèn)題,使用網(wǎng)格方案性?xún)r(jià)比又很低,且不說(shuō)現(xiàn)有網(wǎng)格實(shí)現(xiàn)等落地難度大及后期的使用維護(hù)成本高。
繼續(xù)向基礎(chǔ)設(shè)施下層找方案,從網(wǎng)絡(luò)層入手。Kubernetes 提供了的網(wǎng)絡(luò)策略 *NetworkPolicy*[1],則可以實(shí)現(xiàn)“網(wǎng)絡(luò)層面的隔離”。
示例應(yīng)用
在進(jìn)一步演示?NetworkPolicy?的方案之前,先介紹用于演示的示例應(yīng)用。我們使用 Cilium 在互動(dòng)教程?Cilium getting started[2]?中使用的“星球大戰(zhàn)”場(chǎng)景。
這里有三個(gè)應(yīng)用,星戰(zhàn)迷估計(jì)不會(huì)陌生:
死星?deathstar:在? 80?端口提供 web 服務(wù),有 2 個(gè) 副本,通過(guò) Kubernetes Service 的負(fù)載均衡為帝國(guó)戰(zhàn)機(jī)對(duì)外提供”登陸“服務(wù)。鈦戰(zhàn)機(jī)?tiefighter:執(zhí)行登陸請(qǐng)求。 X翼戰(zhàn)機(jī)?xwing:執(zhí)行登陸請(qǐng)求。

如圖所示,我們使用了?Label?對(duì)三個(gè)應(yīng)用進(jìn)行了標(biāo)識(shí):
org?和?class。在執(zhí)行網(wǎng)絡(luò)策略時(shí),我們會(huì)使用這兩個(gè)標(biāo)簽識(shí)別負(fù)載。
#?app.yaml
---
apiVersion:?v1
kind:?Service
metadata:
??name:?deathstar
??labels:
????app.kubernetes.io/name:?deathstar
spec:
??type:?ClusterIP
??ports:
??-?port:?80
??selector:
????org:?empire
????class:?deathstar
---
apiVersion:?apps/v1
kind:?Deployment
metadata:
??name:?deathstar
??labels:
????app.kubernetes.io/name:?deathstar
spec:
??replicas:?2
??selector:
????matchLabels:
??????org:?empire
??????class:?deathstar
??template:
????metadata:
??????labels:
????????org:?empire
????????class:?deathstar
????????app.kubernetes.io/name:?deathstar
????spec:
??????containers:
??????-?name:?deathstar
????????image:?docker.io/cilium/starwars
---
apiVersion:?v1
kind:?Pod
metadata:
??name:?tiefighter
??labels:
????org:?empire
????class:?tiefighter
????app.kubernetes.io/name:?tiefighter
spec:
??containers:
??-?name:?spaceship
????image:?docker.io/tgraf/netperf
---
apiVersion:?v1
kind:?Pod
metadata:
??name:?xwing
??labels:
????app.kubernetes.io/name:?xwing
????org:?alliance
????class:?xwing
spec:
??containers:
??-?name:?spaceship
????image:?docker.io/tgraf/netperf
Kubernetes 網(wǎng)絡(luò)策略
可以通過(guò)官方文檔[3]獲取更多詳細(xì)信息,這里我們直接放出配置:
#?native/networkpolicy.yaml
apiVersion:?networking.k8s.io/v1
kind:?NetworkPolicy
metadata:
??name:?policy
??namespace:?default
spec:
??podSelector:
????matchLabels:
??????org:?empire
??????class:?deathstar
??policyTypes:
??-?Ingress
??ingress:
??-?from:
????-?podSelector:
????????matchLabels:
??????????org:?empire
????ports:
????-?protocol:?TCP
??????port:?80
podSelector?:表示要應(yīng)用網(wǎng)絡(luò)策略的工作負(fù)載均衡,通過(guò)?label?選擇到了?deathstar?的 2 個(gè) Pod。policyTypes?:表示流量的類(lèi)型,可以是?Ingress?或?Egress?或兩者兼具。這里使用?Ingress,表示對(duì)選擇的?deathstar?Pod 的入站流量執(zhí)行規(guī)則。ingress.from:表示流量的來(lái)源工作負(fù)載,也是使用?podSelector?和?Label?進(jìn)行選擇,這里選中了?org=empire?也就是所有“帝國(guó)的戰(zhàn)機(jī)”。ingress.ports:表示流量的進(jìn)入端口,這里列出了?deathstar?的服務(wù)端口。
接下來(lái),我們測(cè)試下。
測(cè)試
先準(zhǔn)備環(huán)境,我們使用?K3s[4]?作為 Kubernetes 環(huán)境。但由于 K3s 默認(rèn)的 CNI 插件 Flannel 不支持網(wǎng)絡(luò)策略,我們需要換個(gè)插件,這里選擇?Calico[5],即 K3s + Calico 的方案。
先創(chuàng)建一個(gè)單節(jié)點(diǎn)的集群:
curl?-sfL?https://get.k3s.io?|?K3S_KUBECONFIG_MODE="644"?INSTALL_K3S_EXEC="--flannel-backend=none?--cluster-cidr=10.42.0.0/16?--disable-network-policy?--disable=traefik"?sh?-
此時(shí),所有的 Pod 都處于?Pending?狀態(tài),因?yàn)檫€需要安裝 Calico:
kubectl?apply?-f?https://projectcalico.docs.tigera.io/manifests/calico.yaml
待 Calico 成功運(yùn)行后,所有的 Pod 也會(huì)成功運(yùn)行。
接下來(lái)就是部署應(yīng)用:
kubectl?apply?-f?app.yaml
執(zhí)行策略前,執(zhí)行下面的命令看看“戰(zhàn)機(jī)能否登陸死星”:
kubectl?exec?tiefighter?--?curl?-s?-XPOST?deathstar.default.svc.cluster.local/v1/request-landing
Ship?landed
kubectl?exec?xwing?--?curl?-s?-XPOST?deathstar.default.svc.cluster.local/v1/request-landing
Ship?landed
從結(jié)果來(lái)看,兩種 ”戰(zhàn)機(jī)“(Pod 負(fù)載)都可以訪(fǎng)問(wèn)?deathstar?服務(wù)。

此時(shí)執(zhí)行網(wǎng)絡(luò)策略:
kubectl?apply?-f?native/networkpolicy.yaml
再次嘗試”登陸“,xwing?的登陸請(qǐng)求會(huì)停在那(需要使用?ctrl+c?退出,或者請(qǐng)求時(shí)加上?--connect-timeout 2)。

思考
使用 Kubernetes 網(wǎng)絡(luò)策略實(shí)現(xiàn)了我們想要的,從網(wǎng)絡(luò)層面為服務(wù)增加了白名單的功能,這種方案沒(méi)有改造成本,對(duì)系統(tǒng)也幾乎無(wú)影響。
Cilium 還沒(méi)出場(chǎng)就結(jié)束了?我們繼續(xù)看:
有時(shí)我們的服務(wù)會(huì)對(duì)外暴露一些管理端點(diǎn),由系統(tǒng)調(diào)用執(zhí)行一些管理上的操作,比如熱更新、重啟等。這些端點(diǎn)是不允許普通服務(wù)來(lái)調(diào)用,否則會(huì)造成嚴(yán)重的后果。
比如示例中,tiefighter?訪(fǎng)問(wèn)了?deathstar?的管理端點(diǎn)?/exhaust-port:
kubectl?exec?tiefighter?--?curl?-s?-XPUT?deathstar.default.svc.cluster.local/v1/exhaust-port
Panic:?deathstar?exploded
goroutine?1?[running]:
main.HandleGarbage(0x2080c3f50,?0x2,?0x4,?0x425c0,?0x5,?0xa)
????????/code/src/github.com/empire/deathstar/
????????temp/main.go:9?+0x64
main.main()
????????/code/src/github.com/empire/deathstar/
????????temp/main.go:5?+0x85
出現(xiàn)了?Panic?錯(cuò)誤,檢查 Pod 你會(huì)發(fā)現(xiàn)?dealthstar?掛了。
Kubernetes 的網(wǎng)絡(luò)策略?xún)H能工作在 L3/4 層,對(duì) L7 層就無(wú)能為力了。
還是要請(qǐng)出 Cilium。
Cilium 網(wǎng)絡(luò)策略
由于 Cilium 涉及了 Linux 內(nèi)核、網(wǎng)絡(luò)等眾多知識(shí)點(diǎn),要講清實(shí)現(xiàn)原理篇幅極大。故這里僅摘取了官網(wǎng)的介紹,后期希望有時(shí)間再寫(xiě)一篇關(guān)于實(shí)現(xiàn)的。
Cilium 簡(jiǎn)介
Cilium[6]?是一個(gè)開(kāi)源軟件,用于提供、保護(hù)和觀(guān)察容器工作負(fù)載(云原生)之間的網(wǎng)絡(luò)連接,由革命性的內(nèi)核技術(shù)?eBPF[7]?推動(dòng)。
eBPF 是什么?
Linux 內(nèi)核一直是實(shí)現(xiàn)監(jiān)控/可觀(guān)測(cè)性、網(wǎng)絡(luò)和安全功能的理想地方。不過(guò)很多情況下這并非易事,因?yàn)檫@些工作需要修改內(nèi)核源碼或加載內(nèi)核模塊, 最終實(shí)現(xiàn)形式是在已有的層層抽象之上疊加新的抽象。eBPF 是一項(xiàng)革命性技術(shù),它能在內(nèi)核中運(yùn)行沙箱程序(sandbox programs), 而無(wú)需修改內(nèi)核源碼或者加載內(nèi)核模塊。
將 Linux 內(nèi)核變成可編程之后,就能基于現(xiàn)有的(而非增加新的)抽象層來(lái)打造更加智能、 功能更加豐富的基礎(chǔ)設(shè)施軟件,而不會(huì)增加系統(tǒng)的復(fù)雜度,也不會(huì)犧牲執(zhí)行效率和安全性。

我們來(lái)看下 Cilium 的網(wǎng)絡(luò)策略:
#?cilium/networkpolicy-L4.yaml
apiVersion:?"cilium.io/v2"
kind:?CiliumNetworkPolicy
metadata:
??name:?"rule1"
spec:
??description:?"L7?policy?to?restrict?access?to?specific?HTTP?call"
??endpointSelector:
????matchLabels:
??????org:?empire
??????class:?deathstar
??ingress:
??-?fromEndpoints:
????-?matchLabels:
????????org:?empire
????toPorts:
????-?ports:
??????-?port:?"80"
????????protocol:?TCP
與 Kubernetes 的原生網(wǎng)絡(luò)策略差異不大,參考前面的介紹也都看懂,我們直接進(jìn)入測(cè)試。
測(cè)試
由于 Cilium 本身就實(shí)現(xiàn)了 CNI,所以之前的集群就不能用了,先卸載集群:
k3s-uninstall.sh
#?!!!切記要清理之前的 cni 插件
sudo?rm?-rf?/etc/cni/net.d
還是使用同樣的命令創(chuàng)建單節(jié)點(diǎn)的集群:
curl?-sfL?https://get.k3s.io?|?K3S_KUBECONFIG_MODE="644"?INSTALL_K3S_EXEC="--flannel-backend=none?--cluster-cidr=10.42.0.0/16?--disable-network-policy?--disable=traefik"?sh?-
#?cilium?會(huì)使用該變量
export?KUBECONFIG=/etc/rancher/k3s/k3s.yaml
接下來(lái)安裝 Cilium CLI:
curl?-L?--remote-name-all?https://github.com/cilium/cilium-cli/releases/latest/download/cilium-linux-amd64.tar.gz{,.sha256sum}
sha256sum?--check?cilium-linux-amd64.tar.gz.sha256sum
sudo?tar?xzvfC?cilium-linux-amd64.tar.gz?/usr/local/bin
rm?cilium-linux-amd64.tar.gz{,.sha256sum}
cilium?version
cilium-cli:?v0.10.2?compiled?with?go1.17.6?on?linux/amd64
cilium?image?(default):?v1.11.1
cilium?image?(stable):?v1.11.1
cilium?image?(running):?unknown.?Unable?to?obtain?cilium?version,?no?cilium?pods?found?in?namespace?"kube-system"
安裝 Cilium 到集群:
cilium?install
待 Cilium 成功運(yùn)行:
cilium?status
????/ˉˉ\
?/ˉˉ\__/ˉˉ\????Cilium:?????????OK
?\__/ˉˉ\__/????Operator:???????OK
?/ˉˉ\__/ˉˉ\????Hubble:?????????disabled
?\__/ˉˉ\__/????ClusterMesh:????disabled
????\__/
Deployment????????cilium-operator????Desired:?1,?Ready:?1/1,?Available:?1/1
DaemonSet?????????cilium?????????????Desired:?1,?Ready:?1/1,?Available:?1/1
Containers:???????cilium?????????????Running:?1
??????????????????cilium-operator????Running:?1
Cluster?Pods:?????3/3?managed?by?Cilium
Image?versions????cilium-operator????quay.io/cilium/operator-generic:v1.11.1@sha256:977240a4783c7be821e215ead515da3093a10f4a7baea9f803511a2c2b44a235:?1
??????????????????cilium?????????????quay.io/cilium/cilium:v1.11.1@sha256:251ff274acf22fd2067b29a31e9fda94253d2961c061577203621583d7e85bd2:?1
部署應(yīng)用:
kubectl?apply?-f?app.yaml
待應(yīng)用啟動(dòng)后測(cè)試服務(wù)調(diào)用:
kubectl?exec?tiefighter?--?curl?-s?-XPOST?deathstar.default.svc.cluster.local/v1/request-landing
Ship?landed
kubectl?exec?xwing?--?curl?-s?-XPOST?deathstar.default.svc.cluster.local/v1/request-landing
Ship?landed
執(zhí)行 L4 網(wǎng)絡(luò)策略:
kubectl?apply?-f?cilium/networkpolicy-L4.yaml
再次嘗試“登陸”死星,xwing?戰(zhàn)機(jī)同樣無(wú)法登陸,說(shuō)明 L4 層的規(guī)則生效。
我們?cè)賴(lài)L試 L7 層的規(guī)則:
#?cilium/networkpolicy-L7.yaml
apiVersion:?"cilium.io/v2"
kind:?CiliumNetworkPolicy
metadata:
??name:?"rule1"
spec:
??description:?"L7?policy?to?restrict?access?to?specific?HTTP?call"
??endpointSelector:
????matchLabels:
??????org:?empire
??????class:?deathstar
??ingress:
??-?fromEndpoints:
????-?matchLabels:
????????org:?empire
????toPorts:
????-?ports:
??????-?port:?"80"
????????protocol:?TCP
??????rules:
????????http:
????????-?method:?"POST"
??????????path:?"/v1/request-landing"
執(zhí)行規(guī)則:
kubectl?apply?-f?cilium/networkpolicy-L7.yaml
這回,使用?tiefighter?調(diào)用死星的管理接口:
kubectl?exec?tiefighter?--?curl?-s?-XPUT?deathstar.default.svc.cluster.local/v1/exhaust-port
Access?denied
#?登陸接口工作正常
kubectl?exec?tiefighter?--?curl?-s?-XPOST?deathstar.default.svc.cluster.local/v1/request-landing
Ship?landed
這回返回了?Access denied,說(shuō)明 L7 層的規(guī)則生效了。

參考資料
網(wǎng)絡(luò)策略?NetworkPolicy:?https://kubernetes.io/zh/docs/concepts/services-networking/network-policies/
[2]?Cilium getting started:?https://play.instruqt.com/isovalent/tracks/cilium-getting-started
[3]?官方文檔:?https://kubernetes.io/zh/docs/concepts/services-networking/network-policies/
[4]?K3s:?https://k3s.io
[5]?Calico:?https://www.tigera.io/project-calico/
[6]?Cilium:?https://cilium.io
[7]?eBPF:?https://ebpf.io
