如何在 K3s 中使用網絡策略
本文將介紹如何在示例項目中使用網絡策略,並解釋它在 K3s 中的工作原理,從而幫助用戶提高部署的安全性。
關於 K3s 對網絡策略的支持存在一個普遍的誤解,因爲 K3s 默認使用 Flannel CNI,而 Flannel CNI 不支持網絡策略。其實,K3s 使用 Kube-router(https://www.kube-router.io/) 網絡策略控制器爲網絡策略提供支持,所以網絡策略也可以在 K3s 中使用,就像在其他 Kubernetes 發行版中一樣。
—— 正常的 Pod 相互通信 ——
默認情況下,在 K3s 中,一個特定命名空間中的所有 services/Pod 都可以被任何其他命名空間中的所有其他 Pod 訪問。
爲了舉例說明 Pod 如何相互通信,讓我們在兩個不同的命名空間(sample1 和 sample2)中使用簡單的 nginx deployment 和 service 來測試,如下所示:
nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: ngnix-service
spec:
selector:
app: nginx
type: ClusterIP
ports:
- protocol: TCP
port: 80
targetPort: 80
創建測試命名空間並將 nginx 部署到對應命名空間中:
# 在 sample1 中創建第一個示例應用
kubectl create ns sample1
kubectl apply -f nginx.yaml -n sample1
# 在 sample2 中創建第二個示例應用
kubectl create ns sample2
kubectl apply -f nginx.yaml -n sample2
接下來可以嘗試在 Pod 中使用 curl 來檢查 Pod 間的通信。
sample1 中的 pod -> sample2 命名空間中的 service:
# 從 sample1 訪問 sample2
kubectl exec -n sample1 $(kubectl get po -n sample1 -l app=nginx -o name) -- curl --max-time 2 http://ngnix-service.sample2.svc.cluster.local
# 從 sample1 訪問 sample1
kubectl exec -n sample1 $(kubectl get po -n sample1 -l app=nginx -o name) -- curl --max-time 2 http://ngnix-service.sample1.svc.cluster.local:80
sample2 中的 pod -> sample1 命名空間中的 service:
# 從 sample2 訪問 sample1
kubectl exec -n sample2 $(kubectl get po -n sample2 -l app=nginx -o name) -- curl --max-time 2 http://ngnix-service.sample1.svc.cluster.local:80
# 從 sample2 訪問 sample2
kubectl exec -n sample2 $(kubectl get po -n sample2 -l app=nginx -o name) -- curl --max-time 2 http://ngnix-service.sample2.svc.cluster.local:80
可以看到,以上命令均可以訪問到對應目的地址,也就代表相互都可以通信。
—— 使用 NetworkPolicy ——
限制 Pod 相互通信
NetworkPolicy Editor(https://editor.cilium.io/)是一個很好的 UI 工具,可以用於生成 Networkploicy,下面是一個示例。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test-network-policy
namespace: my-namespace
spec:
podSelector:
matchLabels:
role: db <1>
policyTypes:
- Ingress
ingress: <2>
- from:
- namespaceSelector:
matchLabels:
project: myproject
- podSelector:
matchLabels:
role: frontend
ports:
- protocol: TCP
port: 6379
<1> 這會選擇當前命名空間中的特定 Pod 來匹配該策略。<2> 白名單入口規則列表。每個規則都允許與 from 部分匹配的流量。
可以在 ingress from 部分中指定四種選擇器:
-
ipBlock: 會選擇特定的 IP CIDR 範圍以允許作爲入口源。通常這是集羣外部 IP,因爲 Pod IP 是臨時的。
-
podSelector: 會在與 NetworkPolicy 相同的命名空間中選擇特定的 Pod,這些 Pod 應該被允許作爲入口源或出口目的地。
-
namespaceSelector: 會選擇特定的命名空間,所有 Pod 都應該被允許作爲入口源或出口目的地。
-
namespaceSelector 和 podSelector: 指定 namespaceSelector 和 podSelector 的單個 from 條目選擇特定命名空間內的特定 Pod。
—— 使用 NetworkPolicy ——
配置多租戶隔離
現在讓我們使用 NetworkPolicy 來配置隔離。
下面的 yaml 將在不同命名空間的服務之間建立隔離,只允許同一命名空間的 Pod 相互通信,同時也允許從 ingress 和監控 Pod 傳入通信:
networkPolicy.yaml
# 阻止所有傳入流量
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: deny-by-default
spec:
podSelector: {}
ingress: []
---
# 允許所有 Pod 在同一命名空間內的所有流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: allow-same-namespace
spec:
podSelector: {}
ingress:
- from:
- podSelector: {}
---
# 允許 ingress Pod “traefik” 與該命名空間中的所有 Pod 通信
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-svclbtraefik-ingress
spec:
podSelector:
matchLabels:
svccontroller.k3s.cattle.io/svcname: traefik
ingress:
- {}
policyTypes:
- Ingress
---
# 允許 ingress Pod “traefik” 與該命名空間中的所有 Pod 通信
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-traefik-v121-ingress
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: traefik
ingress:
- {}
policyTypes:
- Ingress
---
# 允許監控系統 Pod 與該命名空間中的所有 Pod 通信(以允許抓取指標)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-from-cattle-monitoring-system
spec:
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: cattle-monitoring-system
podSelector: {}
policyTypes:
- Ingress
現在將策略應用於兩個示例命名空間中:
kubectl apply -f networkPolicy.yaml -n sample1
kubectl apply -f networkPolicy.yaml -n sample2
應用上述 networkPolicy.yaml 後,你需要設置好 ingress Pod 或 監控 Pod 的白名單。
現在讓我們再次嘗試之前的 curl 來檢查 Pod 之間的通信:
# 從 sample1 訪問 sample2
kubectl exec -n sample1 $(kubectl get po -n sample1 -l app=nginx -o name) -- curl --max-time 2 http://ngnix-service.sample2.svc.cluster.local
# 從 sample1 訪問 sample1
kubectl exec -n sample1 $(kubectl get po -n sample1 -l app=nginx -o name) -- curl --max-time 2 http://ngnix-service.sample1.svc.cluster.local:80
# 從 sample2 訪問 sample1
kubectl exec -n sample2 $(kubectl get po -n sample2 -l app=nginx -o name) -- curl --max-time 2 http://ngnix-service.sample1.svc.cluster.local:80
# 從 sample2 訪問 sample2
kubectl exec -n sample2 $(kubectl get po -n sample2 -l app=nginx -o name) -- curl --max-time 2 http://ngnix-service.sample2.svc.cluster.local:80
現在你應該看到,不同命名空間之間的通信被阻止了,但同一命名空間內是可以正常通信的。
—— 調試 NetworkPolicy 通信 ——
NetworkPolicy 能夠看到由於匹配的 NetworkPolicy 而丟棄的數據包。由於網絡規則是通過 KUBE-BWPLCY 鏈中的 iptables 部署的,我們可以在當前運行 Pod 的節點上看到這些規則。因此,讓我們檢查生成的 iptables。
首先,我們需要檢查 Pod 是在哪個節點上運行。
kubectl get po -o wide -n sample1
NAMESPACE NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
sample1 nginx-6c8b449b8f-hhwhv 1/1 Running 0 3d6h 192.168.248.2 node2
登錄到 node2,並獲取 iptables 的 KUBE-NWPLCY 鏈:
node2# iptables -L | grep KUBE-NWPLCY -B 2
iptables -L | grep KUBE-NWPLCY -B 2
target prot opt source destination
Chain KUBE-NWPLCY-6MLFY7WSIVQ6X74S (1 references)
target prot opt source destination
Chain KUBE-NWPLCY-6ZMDCAWFW6IG7Y65 (0 references)
--
RETURN all -- anywhere anywhere /* rule to ACCEPT traffic from all sources to dest pods selected by policy name: allow-all-svclbtraefik-ingress namespace sample1 */ match-set KUBE-DST-AZLS65URBWHIM4LV dst mark match 0x10000/0x10000
Chain KUBE-NWPLCY-CMW66LXPRKANGCCT (1 references)
--
RETURN all -- anywhere anywhere /* rule to ACCEPT traffic from specified ipBlocks to dest pods selected by policy name: allow-from-cattle-monitoring-system namespace sample1 */ match-set KUBE-SRC-RCIDLRVZOORE5IEC src match-set KUBE-DST-T5UTRUNREWDWGD44 dst mark match 0x10000/0x10000
Chain KUBE-NWPLCY-DEFAULT (2 references)
--
MARK all -- anywhere anywhere /* rule to mark traffic matching a network policy */ MARK or 0x10000
Chain KUBE-NWPLCY-EM64V3NXOUG2TAJZ (1 references)
--
RETURN all -- anywhere anywhere /* rule to ACCEPT traffic from specified ipBlocks to dest pods selected by policy name: allow-same-namespace namespace sample1 */ match-set KUBE-SRC-DSEC5V52VOYVVZ4H src match-set KUBE-DST-5TPLTTXGTPDHQ2AH dst mark match 0x10000/0x10000
Chain KUBE-NWPLCY-IF5LSB2QJ2HY5MD6 (0 references)
--
...
省略...
...
--
ACCEPT all -- anywhere anywhere /* rule for stateful firewall for pod */ ctstate RELATED,ESTABLISHED
ACCEPT all -- anywhere 192.168.248.2 /* rule to permit the traffic traffic to pods when source is the pod's local node */ ADDRTYPE match src-type LOCAL
KUBE-NWPLCY-DEFAULT all -- 192.168.248.2 anywhere /* run through default egress network policy chain */
KUBE-NWPLCY-CMW66LXPRKANGCCT all -- anywhere 192.168.248.2 /* run through nw policy allow-from-cattle-monitoring-system */
KUBE-NWPLCY-EM64V3NXOUG2TAJZ all -- anywhere 192.168.248.2 /* run through nw policy allow-same-namespace */
KUBE-NWPLCY-RJITOIYNFGLSMNHT all -- anywhere 192.168.248.2 /* run through nw policy deny-by-default */
現在我們觀察鏈 KUBE-NWPLCY-EM64V3NXOUG2TAJZ(在你的環境中會有不同的名稱),它是 allow-same-namespace namespace sample1,同時再次運行 curl 測試:
watch -n 2 -d iptables -L KUBE-NWPLCY-EM64V3NXOUG2TAJZ -nv
Every 2.0s: iptables -L KUBE-NWPLCY-EM64V3NXOUG2TAJZ -nv node2: Mon Mar 6 20:18:38 2023
Chain KUBE-NWPLCY-EM64V3NXOUG2TAJZ (1 references)
pkts bytes target prot opt in out source destination
4 240 MARK all -- * * 0.0.0.0/0 0.0.0.0/0 /* rule to ACCEPT traffic from source pods to dest pods selected by policy name allow-same-namespace namespace sample1 */
match-set KUBE-SRC-OPGXQ4TCHJJUUOWB src match-set KUBE-DST-5TPLTTXGTPDHQ2AH dst MARK or 0x10000
4 240 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 /* rule to ACCEPT traffic from source pods to dest pods selected by policy name allow-same-namespace namespace sample1 */
match-set KUBE-SRC-OPGXQ4TCHJJUUOWB src match-set KUBE-DST-5TPLTTXGTPDHQ2AH dst mark match 0x10000/0x10000
0 0 MARK all -- * * 0.0.0.0/0 0.0.0.0/0 /* rule to ACCEPT traffic from specified ipBlocks to dest pods selected by policy name: allow-same-namespace namespace sample1 */ match-set KUBE-SRC-DSEC5V52VOYVVZ4H src match-set KUBE-DST-5TPLTTXGTPDHQ2AH dst MARK or 0x10000
0 0 RETURN all -- * * 0.0.0.0/0 0.0.0.0/0 /* rule to ACCEPT traffic from specified ipBlocks to dest pods selected by policy name: allow-same-namespace namespace sample1 */ match-set KUBE-SRC-DSEC5V52VOYVVZ4H src match-set KUBE-DST-5TPLTTXGTPDHQ2AH dst mark match 0x10000/0x10000
你會看到在運行 curl 測試期間,計數器不斷變化,顯示已接受和已丟棄的數據包。
被網絡策略丟棄的數據包也可以被記錄下來。被丟棄的數據包被髮送到 iptables NFLOG,它顯示了數據包的詳細信息,包括阻止它的網絡策略。
要將 NFLOG 轉換爲日誌條目,可以安裝 ulogd2/ulogd
包並將 [log1] 配置爲在 group=100 上讀取。然後,重新啓動 ulogd2 服務提交新配置。
要將所有這些數據包記錄到文件中,ulogd2 需要在 /etc/ulogd.conf
中進行配置,示例如下:
[global]
logfile="syslog"
loglevel=3
plugin="/usr/lib/x86_64-linux-gnu/ulogd/ulogd_inppkt_NFLOG.so"
plugin="/usr/lib/x86_64-linux-gnu/ulogd/ulogd_filter_IFINDEX.so"
plugin="/usr/lib/x86_64-linux-gnu/ulogd/ulogd_filter_IP2STR.so"
plugin="/usr/lib/x86_64-linux-gnu/ulogd/ulogd_filter_IP2BIN.so"
plugin="/usr/lib/x86_64-linux-gnu/ulogd/ulogd_filter_PRINTPKT.so"
plugin="/usr/lib/x86_64-linux-gnu/ulogd/ulogd_filter_HWHDR.so"
plugin="/usr/lib/x86_64-linux-gnu/ulogd/ulogd_raw2packet_BASE.so"
plugin="/usr/lib/x86_64-linux-gnu/ulogd/ulogd_output_LOGEMU.so"
# 這是一個堆棧,用於記錄系統通過 LOGEMU 發送的數據包
stack=log1:NFLOG,base1:BASE,ifi1:IFINDEX,ip2str1:IP2STR,print1:PRINTPKT,emu1:LOGEMU
[log1]
group=100
[emu1]
file="/var/log/ulog/syslogemu.log"
sync=1
修改配置文件後,重啓 ulogd:
systemctl restart ulogd2.service
如果數據包被網絡策略規則阻止,日誌消息將出現在 /var/log/ulog/syslogemu.log
中:
# cat /var/log/ulog/syslogemu.log
Mar 7 09:35:43 cluster-k3s-masters-a3620efa-5qgpt IN=cni0 OUT=cni0 MAC=da:f6:6e:6e:f9:ce:ae:66:8d:d5:f8:d1:08:00 SRC=10.42.0.59 DST=10.42.0.60 LEN=60 TOS=00 PREC=0x00 TTL=64 ID=50378 DF PROTO=TCP SPT=47750 DPT=80 SEQ=3773744693 ACK=0 WINDOW=62377 SYN URGP=0 MARK=20000
如果有大量的流量,日誌文件可能增長得非常快。爲了控制這種情況,通過在相關的網絡策略中添加以下注釋,適當地設置 "limit" 和 "limit-burst"iptables 參數:
# 默認值是limit=10/分鐘和limit-burst=10。
kube-router.io/netpol-nflog-limit=<LIMIT-VALUE>
kube-router.io.io/netpol-nflog-limit-burst=<LIMIT-BURST-VALUE>
參考:
-
Kubernetes Network Policies:https://kubernetes.io/docs/concepts/services-networking/network-policies
-
K3s:https://github.com/k3s-io/k3s/blob/master/README.md
-
強化指南 – NetworkPolicies:https://docs.k3s.io/security/hardening-guide#networkpolicies
-
K3s Network Policy:https://docs.k3s.io/advanced#additional-network-policy-logging
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/kvFVQKw5X8-kXtrpvfj_tQ