昨天,我們學會了用 RBAC 控制「誰能對 Kubernetes API 做什麼操作」。
但還有另一個重要問題:
Pod 之間的網路流量要怎麼限制?
在沒有 NetworkPolicy 的情況下,Pod 的 Ingress 與 Egress 流量預設都是允許的。
如果某個 Pod 被入侵,攻擊者就可能嘗試連線到同叢集中的其他服務,進一步擴大影響範圍,這類行為通常稱為橫向移動(Lateral Movement)。
要降低這類風險,就需要使用 NetworkPolicy(網路策略)。
NetworkPolicy 可以限制 Pod 的 Ingress / Egress 流量,讓不同應用之間只保留真正需要的網路連線,也是落實最小權限與 Zero Trust 思維的重要工具之一。
今天內容包含:
以下操作皆在 master 節點執行。
Kubernetes 的 Pod 網路設計,預設讓 Pod 之間可以直接互相通訊。
在沒有 NetworkPolicy 限制的情況下,如果某個 Pod 沒有被任何 Policy 選中,它的 Ingress 與 Egress 流量預設都是允許的。
先看一個典型的微服務架構:

| 流量方向 | 應該允許? | 原因 |
|---|---|---|
| Frontend → Backend | ✅ 允許 | 正常的 API 呼叫 |
| Backend → Database | ✅ 允許 | 正常的資料存取 |
| Frontend → Database | ❌ 拒絕 | 前端不該直接連資料庫 |
| 外部 → Database | ❌ 拒絕 | 資料庫不應直接暴露給外部 |
如果沒有額外的 NetworkPolicy 限制,上述不希望出現的連線也可能被允許,這在生產環境中會增加橫向移動與誤連線的風險。
💡 RBAC vs NetworkPolicy
- RBAC:控制對 Kubernetes API 的存取,例如誰能
get、create、delete資源- NetworkPolicy:控制 Pod 的網路流量,例如哪些 Pod 可以互相連線
兩者處理的是不同層次的安全問題,通常需要搭配使用。
NetworkPolicy 可以用一句話概括:
對符合條件的 Pod(
podSelector),控制它允許哪些 Ingress 與 Egress 流量。

💡
podSelector單獨使用時,只會匹配同一個 Namespace 的 Pod;跨 Namespace 需要搭配namespaceSelector。
| 欄位 | 作用 | 說明 |
|---|---|---|
podSelector |
這條 Policy 套用在哪些 Pod | 用 Label 選擇;空的 {} 代表該 Namespace 內所有 Pod |
policyTypes |
控制流量方向 | Ingress、Egress,或兩者 |
ingress.from |
允許哪些來源連進來 | 可使用 podSelector、namespaceSelector、ipBlock |
egress.to |
允許連到哪些目的地 | 同樣可使用 podSelector、namespaceSelector、ipBlock |
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 是分開判斷的,可以只隔離其中一個方向
⚠️ 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 |
可以先查看 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 是否真的有被執行。
我們用一個三層微服務架構來演練:Frontend → Backend → Database。
# 建立 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
# 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,再逐步只開放真正需要的連線。
這是建立零信任網路的第一步 — 先把所有流量都封鎖,再逐步開放需要的。
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
# → 超時

⚠️ 注意:Default Deny 也可能影響 DNS
如果 Egress 被封鎖,Pod 對 DNS Server 的查詢也可能一起被擋住。
因此接下來會先放通 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 名稱,例如
backend、database,需要透過叢集 DNS 解析成對應的 Service IP。如果 Egress Policy 封鎖了 DNS 查詢常用的 UDP / TCP 53 Port,Pod 就可能無法解析 Service 名稱,導致依賴名稱解析的連線失敗。
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
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
# ✅ 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
# → 超時


📝 現在的流量狀態
目前只保留應用真正需要的連線:
- Frontend → Backend ✅
- Backend → Database ✅
- Frontend → Database ❌
- Database → Backend ❌
這種「預設拒絕,只開放必要流量」的方式,就是 Zero Trust 網路設計中常見的做法之一。
除了用 podSelector 選擇同 Namespace 的 Pod,還可以用 namespaceSelector 控制跨 Namespace 的流量。
# 先建立 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
⚠️
podSelector和namespaceSelector的組合要注意情況 A:AND 邏輯
podSelector和namespaceSelector放在同一個from項目:ingress: - from: - podSelector: matchLabels: app: prometheus namespaceSelector: matchLabels: purpose: monitoring代表只允許:
purpose=monitoringNamespace 中,同時具有app=prometheusLabel 的 Pod。情況 B:OR 邏輯
podSelector和namespaceSelector分成兩個from項目:ingress: - from: - podSelector: matchLabels: app: prometheus - namespaceSelector: matchLabels: purpose: monitoring代表允許:
- Policy 所在 Namespace 中
app=prometheus的 Pod- 或
purpose=monitoringNamespace 中的所有 Pod同一個項目 = AND;分成不同項目 = OR。
kubectl apply -f allow-monitoring-ingress.yaml
# 確認 NetworkPolicy 已建立
kubectl get networkpolicy allow-monitoring-ingress -n netpol-demo
# 查看詳細規則,確認 ingress 來源和 port 是否正確
kubectl describe networkpolicy allow-monitoring-ingress -n netpol-demo

可以進一步用實際指令測試跨 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

💡 驗證重點
可以使用:
kubectl describe networkpolicy <policy-name> -n <namespace>查看 Policy 套用的 Pod、允許來源、目的地與 Port。
如果 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 |
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 安全設定。
# 列出 Namespace 中所有的 NetworkPolicy
kubectl get networkpolicy -n netpol-demo
# 查看某條 Policy 的詳細內容
kubectl describe networkpolicy default-deny-all -n netpol-demo


kubectl get pods -n netpol-demo --show-labels
kubectl get ns --show-labels
💡 除錯小技巧
可以直接使用 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 | 在 ingress 或 egress Rule 中加入 ports,指定 protocol 與 port |
今天我們學會了 Kubernetes 的網路隔離機制 —— NetworkPolicy。
搭配 Day 24 的 RBAC,我們現在已經能從 API 層與網路層兩個方向限制不必要的存取。
| 學到的東西 | 一句話總結 |
|---|---|
| 預設行為 | 沒有 NetworkPolicy 限制時,Pod 的 Ingress / Egress 流量預設允許 |
| Default Deny | 先封鎖不需要的流量,再逐步開放必要連線 |
| Ingress vs Egress | Ingress 控制「誰能連進來」,Egress 控制「可以連到哪裡」 |
| 三種選擇器 | podSelector、namespaceSelector、ipBlock |
| DNS 很重要 | 如果應用使用 Service Name,Egress 限制後要記得保留 DNS 查詢 |
| CNI 前置條件 | NetworkPolicy 必須由支援 Policy Enforcement 的 CNI / Network Plugin 執行 |
下一篇我們將進入 Gateway API —— Kubernetes 更現代的流量入口 API,學習 Gateway、HTTPRoute 等資源如何管理對外流量與路由規則!