K8s NetworkPolicy 網路存取控制

K8s NetworkPolicy 網路存取控制的重點整理。

K8s NetworkPolicy 網路存取控制

TL;DR

NetworkPolicy 決定哪些連線可以進入或離開指定的 Pod。 例如:資料庫只接受 backend Pod 連入 TCP 3306。

機制 管什麼
K8s RBAC 與 ServiceAccount 哪個身分可以操作 Kubernetes API
K8s 中的 Pod 是什麼 容器執行身分與 Linux 權限
NetworkPolicy Pod 的網路連線,常見是 IP、TCP/UDP port
K8s Ingress 與 Gateway API 將外部請求路由到後端服務

NetworkPolicy 需要支援政策執行的網路元件才會生效。物件建立成功,不代表流量已受限制。 見 K8s CNI 比較:Cilium、Calico 與 Flannel。

Flannel:提供網路連通,本身不執行 NetworkPolicy

單獨使用 Flannel(flanneld)時,不會執行 NetworkPolicy 的封包限制。 即使 kubectl apply 成功,沒有政策執行元件,流量也不會因此被阻擋。

方案 NetworkPolicy 執行能力
單獨 Flannel 本身不執行,主要提供跨 Node 的 Pod 網路連通
Calico/Cilium 提供 NetworkPolicy 執行能力,仍需正確安裝與配置
Flannel +政策控制器 Flannel 負責連通,額外元件負責執行政策

考前記法:Flannel 接通網路;NetworkPolicy 還需要有人執行。 不能只看到 Flannel 就斷定整個叢集一定沒有政策功能,因為可能另裝了控制器。

目前官方 Flannel Helm chart 提供 netpol.enabled 選項,可一起部署政策控制器;也能搭配其他政策方案。確認實際部署元件並測試允許/拒絕流量,才能判定政策是否生效。

來源:Flannel 官方 Network Policy 說明。

Ingress 與 Egress:以被選中的 Pod 為視角

flowchart LR
    A[來源 Pod] -->|Ingress:進入| B[被政策選中的 Pod]
    B -->|Egress:離開| C[目的 Pod/外部位址]

此處的 Ingress 是流量方向,與 kind: Ingress 路由物件不同。

欄位 意思
metadata.namespace 這份政策所在的 Namespace
spec.podSelector 政策管哪些 Pod,只選本 Namespace
policyTypes 管 Ingress、Egress,或兩者;建議明寫
ingress[].from 允許哪些來源連入
egress[].to 允許連往哪些目的地
ports 允許的目的 port 與協定

範例:資料庫只允許同 Namespace 的 backend 連入

假設 default 內資料庫 Pod 有 app: db,後端 Pod 有 app: backend,資料庫監聽 TCP 3306。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: db-allow-backend
  namespace: default
spec:
  podSelector:
    matchLabels:
      app: db
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: backend
    ports:
    - protocol: TCP
      port: 3306

選中 db → 限制進入流量 → 允許 backend 連入 3306。 這份政策不限制 db 的 Egress,也不會限制未選中的其他 Pod。若有其他政策也選中 db,允許範圍會合併。

外層選「管誰」,內層選「允許誰」:不是覆蓋

外層 spec.podSelector 選中被管理的 Pod;內層 ingress[].from 列出允許連入的來源。內層不會覆蓋外層,這與 Pod/container 的 securityContext 覆蓋規則不同。

以下完整範例允許 backend Pod 或(OR) 指定來源 CIDR 連入 db 的 TCP 3306:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: db-allow-backend-or-cidr
  namespace: default
spec:
  podSelector:
    matchLabels:
      role: db
  policyTypes:
  - Ingress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          role: backend
    - ipBlock:
        cidr: 192.168.1.0/24
    ports:
    - protocol: TCP
      port: 3306
位置 讀法
外層 spec.podSelector 管誰? default Namespace 裡 role: db 的 Pod
policyTypes: [Ingress] 管哪個方向? 進入 db 的流量
第一個 from 項目的 podSelector 允許誰? 同 Namespace 裡 role: backend 的 Pod
第二個 from 項目的 ipBlock 也允許誰? 來源 IP 在 192.168.1.0/24 的流量
同一條 ingress 規則的 ports 上述任一來源都只能連入 TCP 3306
flowchart LR
    A[同 Namespace:role=backend 的 Pod] -->|TCP 3306| D[外層選中:role=db 的 Pod]
    B[來源 CIDR:192.168.1.0/24] -->|TCP 3306| D

兩個來源各有一個 -,所以是 OR:(backend Pod OR 來源 CIDR)AND TCP 3306。 來源只需符合其中一項;不需要同時具有 backend label 且位於該 CIDR。db 仍是被管的目標,不會被內層改成 backend。

ipBlock 比對的是來源 IP,不是 Pod label。若路徑上有 NAT/負載均衡,實際比對到的來源 IP 可能受網路實作與轉換順序影響。

預設行為與常考規則

  • 沒有政策選中某 Pod 的某個方向時,該方向預設不隔離;Namespace 本身不會自動隔離網路。
  • 一旦政策選中 Pod 並隔離某方向,該方向只允許符合規則的流量。
  • 多份政策的允許規則是聯集,沒有先後順序;default-deny 不會覆蓋另一份政策的 allow。
  • A 連到 B 時,A 的 Egress 與 B 的 Ingress 都必須允許;未隔離的方向視為允許。已允許連線的回應流量會隱含放行。
  • 一般 NetworkPolicy 主要控制 L3/L4,不用來依 HTTP URL、使用者登入身分做授權。

全部 Pod 拒絕進入:default-deny ingress

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: default
spec:
  podSelector: {}
  policyTypes:
  - Ingress

podSelector: {} 選中本 Namespace 全部 Pod;沒有任何 ingress allow 規則,所以隔離進入流量。若要同時隔離出去的流量,把 Egress 也加入 policyTypes,且不加 egress allow 規則。

限制 Egress 後,DNS 查詢也需要放行,通常涉及 UDP/TCP 53;目的地依叢集 DNS 架構配置。DNS 被擋時,使用 Service 名稱的連線也會失敗。

matchLabels:裡面的條件必須全部符合(AND)

Selector 常用 matchLabels 比對 labels,拼字是 matchLabels。也能用 matchExpressions 表達 In、NotIn、Exists 等條件。

同一個 matchLabels 裡,每一個 label 條件都必須同時符合,才會選中該物件;不是符合其中一個就可以。

podSelector:
  matchLabels:
    app: backend
    env: production

意思是:app = backend AND env = production。

Pod 的 labels 是否選中 原因
app: backend、env: production ✅ 兩個條件都符合
app: backend、env: dev ❌ env 不符合
只有 app: backend ❌ 缺少 env
app: frontend、env: production ❌ app 不符合
app: backend、env: production、team: payment ✅ 要求的條件全部符合,額外的 labels 不影響

這個 AND 規則也適用於 namespaceSelector.matchLabels,只是比對的物件變成 Namespace。

跨 Namespace:selector 的 AND/OR

同一個 from 項目內,同時放兩種 selector 是 AND:只允許 payroll Namespace 裡的 backend。

from:
- namespaceSelector:
    matchLabels:
      kubernetes.io/metadata.name: payroll
  podSelector:
    matchLabels:
      app: backend

分成兩個 - 項目則是 OR:允許 payroll 的所有 Pod,或政策所在 Namespace 的 backend。

from:
- namespaceSelector:
    matchLabels:
      kubernetes.io/metadata.name: payroll
- podSelector:
    matchLabels:
      app: backend

namespaceSelector 比對 Namespace 的 labels;podSelector 比對 Pod 的 labels。單獨的 podSelector 只選政策所在 Namespace 的 Pod。

重要指令與官方關鍵詞

kubectl get networkpolicy -A
kubectl describe networkpolicy db-allow-backend -n default
kubectl get pods -n default --show-labels
kubectl get namespaces --show-labels
kubectl explain networkpolicy.spec
kubectl explain networkpolicy.spec.ingress
kubectl apply --dry-run=server -f networkpolicy.yaml
kubectl apply -f networkpolicy.yaml

套用後,要從「允許的來源」與「不允許的來源」各測一次真正的應用連線;不要只看 YAML,也不要只用 ping 判斷 TCP/UDP 規則。

官方:Network Policies。搜尋關鍵詞:network policies;頁內找 The NetworkPolicy resource、Behavior of to and from selectors、Default deny all ingress traffic。