K8s · 維運與部署 · 第 34 課 · · 10min read

NetworkPolicy 入門:K8s 預設叢集內全通,怎麼鎖網路?

預設情況下,K8s 任何 Pod 都能連任何 Pod。生產環境這樣等於沒防火牆。NetworkPolicy 是 K8s 內建的網路 ACL,用 label 控制「誰能連誰」。

#Kubernetes #NetworkPolicy #網路安全 #ACL
章節目錄 · 11

K8s 預設網路全通 — 這是個 bug 不是 feature

你做完 RBAC,覺得自己很安全。直到某次 Pen Test 報告:

「滲透測試發現,從 \marketing\ namespace 的容器可直接連到 \prod\ 的 MySQL Pod。」

你打開查看:

\\\bash
# 在 marketing namespace 隨便找個 Pod
kubectl exec -it any-pod -n marketing -- sh

# 直接連得到 prod 的 MySQL!

mysql -h mysql.prod.svc.cluster.local -uroot -p

\
\\

原因:K8s 預設所有 Pod 之間網路全通。這是 K8s 設計上的選擇——簡化網路。但對生產環境,這是個災難。

解法:NetworkPolicy(Pod 等級的防火牆)

NetworkPolicy 的邏輯

\\\yaml
spec:
podSelector: # 這條規則套用在「誰」身上
matchLabels: {role: db}
policyTypes:
- Ingress # 管進來的流量
- Egress # 管出去的流量
ingress: # 允許進來的清單
- from: [...]
ports: [...]
egress: # 允許出去的清單
- to: [...]
ports: [...]
\
\\

關鍵法則

只要對某個 Pod 套用了 NetworkPolicy,所有沒明確允許的流量都會被拒絕——白名單機制。

名詞警告:跟 Ingress Controller 不一樣

NetworkPolicy 的 \ingress\ 跟第六堂的 \Ingress Controller\ 完全不同

NetworkPolicy ingressIngress Controller
層級L3/L4(IP/Port)L7(HTTP)
管什麼Pod 之間的網路從外面來的 HTTP 請求
名字一樣,意思不同——別混淆。

實作:「只有 API 能連 DB」

最常見的需求:API Pod 可連 DB,但前端 Pod 不能直連。

\\\yaml
# networkpolicy-db.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: db-allow-api
namespace: default
spec:
podSelector:
matchLabels:
role: database # 套用在 DB Pod 上
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
role: api # 只允許帶 role=api 的 Pod
ports:
- protocol: TCP
port: 3306
\
\\

意思是:

我(DB Pod)只接受帶 \role=api\ 的 Pod 連到 TCP 3306。其他通通擋。

驗證流程

\\\bash
kubectl apply -f networkpolicy-db.yaml

# 1. 從 API Pod 連 DB → 應該成功
kubectl exec -it api-xxx -- nc -zv db-svc 3306
# Connection to db-svc 3306 port [tcp/*] succeeded! ✅

# 2. 從前端 Pod 連 DB → 應該失敗
kubectl exec -it frontend-xxx -- nc -zv db-svc 3306
# (timeout 卡住... 表示被擋了) ✅
\
\\

三層架構的標準寫法

生產環境最常見的需求:

[Ingress] → [Frontend] → [API] → [DB]

對應的 NetworkPolicy:

規則
Frontend只接受來自 Ingress Controller
API只接受來自 Frontend
DB只接受來自 API(port 3306)
寫成 3 個 NetworkPolicy,每層只開上一層。實習生在 marketing namespace 想連 prod 的 DB?通通擋。

常見搭配:先擋全部,再開特定

進階做法是先 \default deny all\

\\\yaml
# 先擋掉這個 namespace 所有 ingress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: prod
spec:
podSelector: {} # 空的 = 全部 Pod
policyTypes:
- Ingress
\
\\

接著再用獨立的 NetworkPolicy 一個一個放行——白名單最安全

跨 namespace 的 NetworkPolicy

要允許 \monitoring\ namespace 的 Prometheus 抓 metrics:

\\\yaml
ingress:

  • from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: monitoring
podSelector:
matchLabels:
app: prometheus
ports:
- protocol: TCP
port: 9090
\
\\

\namespaceSelector\ + \podSelector\ 並用,限定「monitoring 命名空間裡,只有 Prometheus 那組 Pod」可進。

注意:NetworkPolicy 需要 CNI 支援

NetworkPolicy 是 規範,但實際執行靠 CNI 外掛:

CNI支援 NetworkPolicy
Calico
Cilium✅(甚至支援 L7)
k3s 預設✅(內建 network policy controller)
Flannel(純)
AWS VPC CNI(基本版)❌(要加 Calico)
寫了 NetworkPolicy 但沒擋住?先確認你的 CNI 支援。

重點整理

  • K8s 預設全通——這是設計而不是 bug,但生產環境必須補
  • NetworkPolicy = Pod 等級防火牆,白名單機制
  • podSelector + policyTypes + ingress/egress 三段式
  • 名字陷阱:NetworkPolicy 的 ingress ≠ Ingress Controller
  • CNI 必須支援——k3s / Calico / Cilium 都行
  • 實務做法:先 default-deny-all,再獨立放行

下一步

到目前為止,你學了 K8s 八大概念加上生產維運的 5 個技巧。但是全部串起來呢?

下一篇開始 2 篇實戰:從零部署一套完整服務(上)— 12 步流程的前 6 步

📅 下一篇從零部署一套完整服務(上)— 12 步流程的前 6 步
把這 40 篇學的全部串起來——前端 + 後端 + DB 完整微服務,12 步從零到生產級。
📚 完整系列總覽K8s 系列教學首頁(共 40 課,按學習路徑順序排)