iT邦幫忙

2026 iThome 鐵人賽

DAY 25
0
Kubernetes

從零到 CKA:30 天掌握 Kubernetes 核心觀念與實作系列 第 25

Day 25|NetworkPolicy — Kubernetes 的網路策略與零信任隔離

  • 分享至 

  • xImage
  •  

前言

昨天,我們學會了用 RBAC 控制「誰能對 Kubernetes API 做什麼操作」。

但還有另一個重要問題:

Pod 之間的網路流量要怎麼限制?

在沒有 NetworkPolicy 的情況下,Pod 的 Ingress 與 Egress 流量預設都是允許的。

如果某個 Pod 被入侵,攻擊者就可能嘗試連線到同叢集中的其他服務,進一步擴大影響範圍,這類行為通常稱為橫向移動(Lateral Movement)

要降低這類風險,就需要使用 NetworkPolicy(網路策略)

NetworkPolicy 可以限制 Pod 的 Ingress / Egress 流量,讓不同應用之間只保留真正需要的網路連線,也是落實最小權限與 Zero Trust 思維的重要工具之一。

今天內容包含:

  1. 為什麼需要 NetworkPolicy?
  2. NetworkPolicy 核心概念
  3. CNI 前置條件
  4. 實戰:Default Deny → 逐步開放
  5. Namespace 層級的隔離
  6. 常見的 NetworkPolicy 設計模式
  7. 查看與除錯 NetworkPolicy
  8. 常見問題與注意事項

以下操作皆在 master 節點執行。


一、為什麼需要 NetworkPolicy?

Kubernetes 的 Pod 網路設計,預設讓 Pod 之間可以直接互相通訊。

在沒有 NetworkPolicy 限制的情況下,如果某個 Pod 沒有被任何 Policy 選中,它的 Ingress 與 Egress 流量預設都是允許的。

先看一個典型的微服務架構:

https://ithelp.ithome.com.tw/upload/images/20260821/20181928AkhgxfHS0Q.png

流量方向 應該允許? 原因
Frontend → Backend ✅ 允許 正常的 API 呼叫
Backend → Database ✅ 允許 正常的資料存取
Frontend → Database ❌ 拒絕 前端不該直接連資料庫
外部 → Database ❌ 拒絕 資料庫不應直接暴露給外部

如果沒有額外的 NetworkPolicy 限制,上述不希望出現的連線也可能被允許,這在生產環境中會增加橫向移動與誤連線的風險。

💡 RBAC vs NetworkPolicy

  • RBAC:控制對 Kubernetes API 的存取,例如誰能 getcreatedelete 資源
  • NetworkPolicy:控制 Pod 的網路流量,例如哪些 Pod 可以互相連線

兩者處理的是不同層次的安全問題,通常需要搭配使用。


二、NetworkPolicy 核心概念

NetworkPolicy 可以用一句話概括:

對符合條件的 Pod(podSelector),控制它允許哪些 Ingress 與 Egress 流量。

https://ithelp.ithome.com.tw/upload/images/20260821/20181928oQGh1lV94m.png

💡 podSelector 單獨使用時,只會匹配同一個 Namespace 的 Pod;跨 Namespace 需要搭配 namespaceSelector

關鍵欄位說明

欄位 作用 說明
podSelector 這條 Policy 套用在哪些 Pod 用 Label 選擇;空的 {} 代表該 Namespace 內所有 Pod
policyTypes 控制流量方向 IngressEgress,或兩者
ingress.from 允許哪些來源連進來 可使用 podSelectornamespaceSelectoripBlock
egress.to 允許連到哪些目的地 同樣可使用 podSelectornamespaceSelectoripBlock
ports 限制 Port 與 Protocol 可以放在 Ingress 或 Egress Rule 中

三種流量選擇器

選擇器 作用 範例
podSelector 選擇特定 Label 的 Pod matchLabels: {app: backend}
namespaceSelector 選擇特定 Label 的 Namespace matchLabels: {env: production}
ipBlock 選擇特定 IP CIDR 範圍 cidr: 10.0.0.0/8,可搭配 except

⚠️ NetworkPolicy 是「允許規則疊加」的邏輯

  • 如果某個 Pod 沒有被任何 NetworkPolicy 選中,該方向的流量預設允許
  • 一旦 Pod 在某個方向(Ingress 或 Egress)被 Policy 選中,就只允許符合規則的流量
  • 多條 NetworkPolicy 同時選中同一個 Pod 時,允許規則會聯集合併,不會互相覆蓋
  • Ingress 與 Egress 是分開判斷的,可以只隔離其中一個方向

三、前置條件 — CNI 必須支援 NetworkPolicy

⚠️ NetworkPolicy 需要底層網路實作支援

建立 NetworkPolicy Resource 本身不代表流量一定會被限制。

叢集使用的 CNI / Network Plugin 必須支援並實際執行 NetworkPolicy,否則 Policy 雖然可以成功建立,卻不會產生預期的隔離效果。

常見支援 NetworkPolicy 的方案包括:

CNI NetworkPolicy 支援 備註
Calico ✅ 支援 支援標準 Kubernetes NetworkPolicy,也提供額外的 Calico Policy 功能
Cilium ✅ 支援 支援標準 NetworkPolicy,也提供 CiliumNetworkPolicy 等擴充能力
Flannel ⚠️ 本身不提供完整 Policy Enforcement 若需要 NetworkPolicy,通常需要搭配其他 Policy Engine

確認目前使用的 CNI

可以先查看 kube-system Namespace 中的網路元件:

kubectl get pods -n kube-system | grep -E "calico|cilium|flannel"

也可以到 Node 上查看 CNI 設定:

ls /etc/cni/net.d/

💡 確認 CNI 名稱還不夠

最後仍建議實際建立一條簡單的 NetworkPolicy,再用 Pod 之間的連線測試確認 Policy 是否真的有被執行。


四、實戰:Default Deny → 逐步開放

我們用一個三層微服務架構來演練:Frontend → Backend → Database。

Step 1:建立測試環境

# 建立 Namespace
kubectl create namespace netpol-demo
vim netpol-test-apps.yaml
# Frontend
apiVersion: v1
kind: Pod
metadata:
  name: frontend
  namespace: netpol-demo
  labels:
    app: frontend
    tier: frontend
spec:
  containers:
    - name: nginx
      image: nginx:latest
      ports:
        - containerPort: 80
---
# Backend
apiVersion: v1
kind: Pod
metadata:
  name: backend
  namespace: netpol-demo
  labels:
    app: backend
    tier: backend
spec:
  containers:
    - name: nginx
      image: nginx:latest
      ports:
        - containerPort: 80
---
# Database(模擬)
apiVersion: v1
kind: Pod
metadata:
  name: database
  namespace: netpol-demo
  labels:
    app: database
    tier: database
spec:
  containers:
    - name: nginx
      image: nginx:latest
      ports:
        - containerPort: 80
---
# 對應的 Service
apiVersion: v1
kind: Service
metadata:
  name: frontend
  namespace: netpol-demo
spec:
  selector:
    app: frontend
  ports:
    - port: 80
---
apiVersion: v1
kind: Service
metadata:
  name: backend
  namespace: netpol-demo
spec:
  selector:
    app: backend
  ports:
    - port: 80
---
apiVersion: v1
kind: Service
metadata:
  name: database
  namespace: netpol-demo
spec:
  selector:
    app: database
  ports:
    - port: 80
kubectl apply -f netpol-test-apps.yaml

Step 2:確認預設全通

# Frontend → Backend(應該通)
kubectl exec -n netpol-demo frontend -- curl -s --max-time 3 backend
# → 回傳 nginx 預設頁面

# Frontend → Database(目前也能連通,但這不符合安全設計)
kubectl exec -n netpol-demo frontend -- curl -s --max-time 3 database
# → 回傳 nginx 預設頁面

# Backend → Database(應該通)
kubectl exec -n netpol-demo backend -- curl -s --max-time 3 database
# → 回傳 nginx 預設頁面

💡 目前尚未套用任何 NetworkPolicy

因此這些 Pod 的 Ingress / Egress 流量目前都是允許的。

接下來我們會先套用 Default Deny,再逐步只開放真正需要的連線。

Step 3:Default Deny — 封鎖所有流量

這是建立零信任網路的第一步 — 先把所有流量都封鎖,再逐步開放需要的。

vim default-deny.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: netpol-demo
spec:
  podSelector: {}          # 空的 = 選中所有 Pod
  policyTypes:
    - Ingress
    - Egress
kubectl apply -f default-deny.yaml
# 再次測試 — 目前連線會失敗
kubectl exec -n netpol-demo frontend -- curl -s --max-time 3 backend
# → 超時(流量被封鎖)

kubectl exec -n netpol-demo frontend -- curl -s --max-time 3 database
# → 超時

https://ithelp.ithome.com.tw/upload/images/20260821/20181928IeDuV71qyd.png

⚠️ 注意:Default Deny 也可能影響 DNS

如果 Egress 被封鎖,Pod 對 DNS Server 的查詢也可能一起被擋住。

因此接下來會先放通 DNS,再逐步開放應用真正需要的流量。

Step 4:允許 DNS 出站(關鍵!)

vim allow-dns.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns
  namespace: netpol-demo
spec:
  podSelector: {}          # 所有 Pod
  policyTypes:
    - Egress
  egress:
    - to:
        - namespaceSelector: {}    # kube-system namespace
      ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53
kubectl apply -f allow-dns.yaml

💡 為什麼 DNS 這麼重要?

Kubernetes 裡的 Service 名稱,例如 backenddatabase,需要透過叢集 DNS 解析成對應的 Service IP。

如果 Egress Policy 封鎖了 DNS 查詢常用的 UDP / TCP 53 Port,Pod 就可能無法解析 Service 名稱,導致依賴名稱解析的連線失敗。

Step 5:允許 Frontend → Backend

vim allow-frontend-to-backend.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-to-backend
  namespace: netpol-demo
spec:
  podSelector:
    matchLabels:
      app: backend         # 套用在 backend Pod
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: frontend   # 只允許來自 frontend
      ports:
        - protocol: TCP
          port: 80
---
# 同時需要允許 frontend 的 egress 到 backend
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: frontend-egress-to-backend
  namespace: netpol-demo
spec:
  podSelector:
    matchLabels:
      app: frontend
  policyTypes:
    - Egress
  egress:
    - to:
        - podSelector:
            matchLabels:
              app: backend
      ports:
        - protocol: TCP
          port: 80
kubectl apply -f allow-frontend-to-backend.yaml

Step 6:允許 Backend → Database

vim allow-backend-to-database.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-backend-to-database
  namespace: netpol-demo
spec:
  podSelector:
    matchLabels:
      app: database        # 套用在 database Pod
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: backend    # 只允許來自 backend
      ports:
        - protocol: TCP
          port: 80
---
# backend 的 egress 到 database
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: backend-egress-to-database
  namespace: netpol-demo
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
    - Egress
  egress:
    - to:
        - podSelector:
            matchLabels:
              app: database
      ports:
        - protocol: TCP
          port: 80
kubectl apply -f allow-backend-to-database.yaml

Step 7:驗證流量規則

# ✅ Frontend → Backend
kubectl exec -n netpol-demo frontend -- curl -s --max-time 3 backend
# → 回傳 nginx 頁面

# ❌ Frontend → Database
kubectl exec -n netpol-demo frontend -- curl -s --max-time 3 database
# → 超時

# ✅ Backend → Database
kubectl exec -n netpol-demo backend -- curl -s --max-time 3 database
# → 回傳 nginx 頁面

# ❌ Database → Backend
kubectl exec -n netpol-demo database -- curl -s --max-time 3 backend
# → 超時

https://ithelp.ithome.com.tw/upload/images/20260821/20181928NNOnge9W9U.png
https://ithelp.ithome.com.tw/upload/images/20260821/20181928C4AU1BxK60.png

📝 現在的流量狀態

目前只保留應用真正需要的連線:

  • Frontend → Backend ✅
  • Backend → Database ✅
  • Frontend → Database ❌
  • Database → Backend ❌

這種「預設拒絕,只開放必要流量」的方式,就是 Zero Trust 網路設計中常見的做法之一。


五、Namespace 層級的隔離

除了用 podSelector 選擇同 Namespace 的 Pod,還可以用 namespaceSelector 控制跨 Namespace 的流量。

場景:只允許 monitoring Namespace 的 Prometheus 來抓指標

# 先建立 monitoring Namespace(如果你的叢集還沒有的話)
kubectl create namespace monitoring

# 幫它加上 label
kubectl label namespace monitoring purpose=monitoring
vim allow-monitoring-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-monitoring-ingress
  namespace: netpol-demo
spec:
  podSelector: {}          # 所有 Pod
  policyTypes:
    - Ingress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              purpose: monitoring
      ports:
        - protocol: TCP
          port: 9090        # Prometheus metrics port

⚠️ podSelectornamespaceSelector 的組合要注意

情況 A:AND 邏輯

podSelectornamespaceSelector 放在同一個 from 項目:

ingress:
  - from:
      - podSelector:
          matchLabels:
            app: prometheus
        namespaceSelector:
          matchLabels:
            purpose: monitoring

代表只允許:

purpose=monitoring Namespace 中,同時具有 app=prometheus Label 的 Pod。

情況 B:OR 邏輯

podSelectornamespaceSelector 分成兩個 from 項目:

ingress:
  - from:
      - podSelector:
          matchLabels:
            app: prometheus
      - namespaceSelector:
          matchLabels:
            purpose: monitoring

代表允許:

  • Policy 所在 Namespace 中 app=prometheus 的 Pod
  • purpose=monitoring Namespace 中的所有 Pod

同一個項目 = AND;分成不同項目 = OR。

kubectl apply -f allow-monitoring-ingress.yaml

驗證:確認 NetworkPolicy 已建立並檢查規則內容

# 確認 NetworkPolicy 已建立
kubectl get networkpolicy allow-monitoring-ingress -n netpol-demo

# 查看詳細規則,確認 ingress 來源和 port 是否正確
kubectl describe networkpolicy allow-monitoring-ingress -n netpol-demo

https://ithelp.ithome.com.tw/upload/images/20260821/20181928ni7BPzRiBl.png

可以進一步用實際指令測試跨 Namespace 的流量是否被正確隔離:

# 先在 default Namespace 建立一個測試用的 Pod
kubectl run test-pod --namespace=default --image=nginx --restart=Never

# 等待 Pod 就緒
kubectl wait --for=condition=Ready pod/test-pod -n default --timeout=60s

# 取得 netpol-demo 中任一 Pod 的 IP
kubectl get pod frontend -n netpol-demo -o wide
# 將 frontend 的 Pod IP 存到變數中(請替換為上一步查到的 IP)
FRONTEND_IP=$(kubectl get pod frontend -n netpol-demo -o jsonpath='{.status.podIP}')

# 從 default Namespace 嘗試連線 netpol-demo 的 Pod(port 9090)
kubectl exec -n default test-pod -- curl -s --max-time 3 ${FRONTEND_IP}:9090
# → 超時(exit code 28)— 因為 default Namespace 沒有 purpose=monitoring label,流量被拒絕

# 測試完畢後清理
kubectl delete pod test-pod -n default

https://ithelp.ithome.com.tw/upload/images/20260821/20181928RXyj13BAnM.png

💡 驗證重點

可以使用:

kubectl describe networkpolicy <policy-name> -n <namespace>

查看 Policy 套用的 Pod、允許來源、目的地與 Port。

如果 NetworkPolicy 沒有照預期生效,先確認這些規則是否符合你的設定。


六、常見的 NetworkPolicy 設計模式

模式 用途 重點
Default Deny All 最小權限起點 先封鎖 Ingress / Egress,再逐步開放必要流量
Allow DNS 允許 DNS 解析 Default Deny 後通常需要額外開放 DNS
Allow Same App 同應用 Pod 互通 使用 podSelector 選擇允許來源
Allow From Namespace 跨 Namespace 白名單 使用 namespaceSelector
Egress to External 允許存取外部服務 使用 ipBlock 指定允許的 IP 範圍
Block Cloud Metadata 降低 Metadata Endpoint 被濫用的風險 排除 169.254.169.254/32

實用範例:封鎖雲端 Metadata Endpoint

AWS、GCP、Azure 等雲端平台都有 Instance Metadata 服務,常見 IPv4 Endpoint 為:

169.254.169.254

Metadata Service 可能提供 Instance 資訊,也可能與雲端身份 Token / Credential 取得機制有關。

如果 Pod 可以存取 Metadata Endpoint,而應用程式又存在 SSRF 等漏洞,攻擊者就可能利用應用程式向 Metadata Service 發送請求,因此可以考慮限制不需要此功能的 Pod 存取 Metadata Endpoint。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-metadata
  namespace: netpol-demo
spec:
  podSelector: {}
  policyTypes:
    - Egress
  egress:
    - to:
        - ipBlock:
            cidr: 0.0.0.0/0
            except:
              - 169.254.169.254/32

⚠️ 注意

Pod 是否能直接存取 Metadata Endpoint,會依雲端平台、CNI 與節點設定而不同。

這條 Policy 適合作為防禦措施之一,但不應取代雲端平台本身的 Metadata / Identity 安全設定。


七、查看與除錯 NetworkPolicy

查看目前的 NetworkPolicy

# 列出 Namespace 中所有的 NetworkPolicy
kubectl get networkpolicy -n netpol-demo

# 查看某條 Policy 的詳細內容
kubectl describe networkpolicy default-deny-all -n netpol-demo

https://ithelp.ithome.com.tw/upload/images/20260821/20181928CIzcBnB7qn.png
https://ithelp.ithome.com.tw/upload/images/20260821/20181928DHZVenzNCJ.png

除錯流量問題的步驟

  1. 確認 CNI 支援 NetworkPolicy —— 避免 Policy 建立成功,但實際沒有被執行
  2. 確認 Pod Label 是否正確 —— kubectl get pods -n netpol-demo --show-labels
  3. 確認 Namespace Label 是否正確 —— kubectl get ns --show-labels
  4. 縮小問題範圍 —— 暫時停用部分 Policy,確認是哪一條規則造成連線異常
  5. 檢查 DNS —— Default Deny Egress 後,常見問題是 DNS 查詢也被封鎖

💡 除錯小技巧

可以直接使用 Pod IP 測試連線,幫助判斷問題是在 DNS,還是 NetworkPolicy 本身:

# 查看 Backend Pod IP
kubectl get pod backend -n netpol-demo -o wide

# 直接使用 Pod IP 連線
kubectl exec -n netpol-demo frontend -- \
  curl -s --max-time 3 <BACKEND_POD_IP>

如果 Pod IP 可以連,但 Service Name 不能連,通常就要優先檢查 DNS。


八、常見問題與注意事項

問題 原因與解法
NetworkPolicy 建了但流量沒被封鎖 先確認 CNI 是否支援並執行 NetworkPolicy,可用 kubectl get pods -n kube-system 查看目前的網路元件
Default Deny 後 Service 名稱連不上 如果封鎖了 Egress,可能連 DNS 查詢也被擋住,需要開放叢集 DNS 所需的 UDP / TCP 53
namespaceSelector 沒生效 確認目標 Namespace 是否具有對應 Label,可用 kubectl get ns --show-labels 檢查
Policy 的 AND / OR 邏輯搞混 同一個 from / to 項目中的多個 Selector 是 AND;不同項目之間是 OR
Egress Policy 後應用無法連外 檢查是否有開放應用需要的外部 API、DNS 或其他必要目的地
想限制特定 Port ingressegress Rule 中加入 ports,指定 protocolport

小結

今天我們學會了 Kubernetes 的網路隔離機制 —— NetworkPolicy。

搭配 Day 24 的 RBAC,我們現在已經能從 API 層網路層兩個方向限制不必要的存取。

學到的東西 一句話總結
預設行為 沒有 NetworkPolicy 限制時,Pod 的 Ingress / Egress 流量預設允許
Default Deny 先封鎖不需要的流量,再逐步開放必要連線
Ingress vs Egress Ingress 控制「誰能連進來」,Egress 控制「可以連到哪裡」
三種選擇器 podSelectornamespaceSelectoripBlock
DNS 很重要 如果應用使用 Service Name,Egress 限制後要記得保留 DNS 查詢
CNI 前置條件 NetworkPolicy 必須由支援 Policy Enforcement 的 CNI / Network Plugin 執行

下一篇我們將進入 Gateway API —— Kubernetes 更現代的流量入口 API,學習 Gateway、HTTPRoute 等資源如何管理對外流量與路由規則!


參考資源


上一篇
Day 24|RBAC — Kubernetes 的角色權限控制
下一篇
Day 26|Gateway API — Kubernetes 的下一代流量入口管理
系列文
從零到 CKA:30 天掌握 Kubernetes 核心觀念與實作26
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言