昨天在了解 CNI !今天要來進入 NetworkPolicy 的實戰!
目前我們的:
cka-lab Namespace
裡面,只要 Service、DNS 和網路都正常,Pod 基本上可以互相連線。
例如我們現在可能有:
frontend
api
redis
postgres
在學習環境裡,這非常方便,因為不用特別設定網路權限,大家就能互相溝通。
但到了 Production,這種「所有 Pod 都可以互連」其實風險很高。
正常的系統架構可能是:
Frontend
│
▼
API
│
├── Redis
└── PostgreSQL
Frontend 只需要呼叫 API,真正需要存取 Redis 和 PostgreSQL 的應該是 API。
也就是說,我們真正希望的不是:
任何 Pod
↓
任何 Pod
而是:
只有需要連線的人
才能連線
Kubernetes 用來處理這件事情的功能,就叫做:
NetworkPolicy
NetworkPolicy 可以把它理解成 Kubernetes 裡的「Pod 網路存取規則」。
它可以控制兩種方向:
Ingress
代表:
哪些流量可以「進入」Pod。
以及:
Egress
代表:
Pod 可以把流量「送到哪裡」。
這一篇我們先專心看:
Ingress
也就是控制:
誰可以連到 Redis?
不過這裡有一個非常重要的前提。
NetworkPolicy 本身只是 Kubernetes API 裡的一份「規則」,真正幫你攔截網路流量的是 CNI Plugin。
CNI,也就是:
Container Network Interface
它負責 Kubernetes Pod 的網路。
像我們前面安裝過的:
Calico
就支援 NetworkPolicy。
因此如果你的 Cluster 已經有 Calico:
kubectl get pods -n calico-system
看到 Calico Pod 正常 Running,就可以直接做今天的實驗,不需要再安裝其他東西。
如果 CNI 根本不支援 NetworkPolicy,就算:
kubectl apply -f networkpolicy.yaml
成功建立 Resource,流量也可能完全不會被擋住。
在還沒建立 NetworkPolicy 以前,我們先做一個對照實驗。
建立一個臨時 Pod,名字叫:
attacker
它不是真的在做攻擊,只是模擬:
一個原本不應該有權限連 Redis 的 Pod。
執行:
kubectl run attacker \
-n cka-lab \
--image=redis:7-alpine \
--restart=Never \
-- \
sleep 3600
這裡使用:
redis:7-alpine
是因為這個 Image 裡面本身就有:
redis-cli
等等測試 Redis 會很方便。
sleep 3600 則只是讓這個 Pod 不要馬上結束,維持一個小時。
確認它正常:
kubectl get pods -n cka-lab
接著進入 attacker:
kubectl exec \
-it attacker \
-n cka-lab \
-- sh
然後測試 Redis:
redis-cli \
-h redis \
ping
這裡的:
-h redis
代表連線到 hostname:
redis
如果你的 Redis Service 名字就是:
redis
Kubernetes DNS 就會幫我們解析到 Redis Service。
正常情況會看到:
PONG

也就是:
attacker
│
▼
Redis
✅
這證明目前沒有任何 NetworkPolicy 限制 attacker。
接下來我們要設定:
Redis 只能接受
app=api的 Pod 連入 6379 Port。
建立:
vim k8s/redis-networkpolicy.yaml
內容:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: redis-ingress
namespace: cka-lab
spec:
podSelector:
matchLabels:
app: redis
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: api
ports:
- protocol: TCP
port: 6379
這份 YAML 最重要的是弄清楚兩個:
podSelector
分別在選誰。
先看:
spec:
podSelector:
matchLabels:
app: redis
很多人第一次看會誤解成:
app=redis的 Pod 可以連進來。
不是。
這一段是在回答:
這份 NetworkPolicy 要套用到哪些 Pod?
答案是:
app=redis
因此它代表:
誰被這份 Policy 保護?
↓
Redis Pod
你可以先確認 Redis 的 Label:
kubectl get pods \
-n cka-lab \
--show-labels
可以看到:
如果你的 Redis Pod 根本沒有:
app=redis
那這份 NetworkPolicy 就選不到它,自然也不會產生效果。
接下來看:
ingress:
- from:
- podSelector:
matchLabels:
app: api
這裡才是在回答:
誰可以連到 Redis?
答案是:
app=api
的 Pod。
因此整份 Policy 可以直接翻譯成人話:
保護:
app=redis
允許來源:
app=api
允許 Port:
TCP 6379
最後:
ports:
- protocol: TCP
port: 6379
表示即使來源符合:
app=api
也只允許它連 Redis 的:
TCP 6379
而不是 Redis Pod 上的所有 Port。
另外,由於這裡只有:
podSelector:
沒有 namespaceSelector,所以這個 app=api 指的是:
同一個 Namespace,也就是
cka-lab裡的app=apiPod。
執行:
kubectl apply \
-f k8s/redis-networkpolicy.yaml
確認:
kubectl get networkpolicy \
-n cka-lab
也可以使用簡寫:
kubectl get netpol \
-n cka-lab
就會看到:
redis-ingress

重新進入 attacker:
kubectl exec \
-it attacker \
-n cka-lab \
-- sh
再次執行:
redis-cli \
-h redis \
ping
這次通常不會再收到:
PONG

而是卡住一段時間,最後 Timeout。
原因很簡單。
attacker 沒有:
app=api
所以:
attacker
│
X
Redis
現在變成:
attacker
↓
Redis
❌
這就表示 NetworkPolicy 已經開始作用。
現在 Policy 規定:
app=api
可以連 Redis。
因此先確認 API Label:
kubectl get pods \
-n cka-lab \
--show-labels

API Pod 應該包含:
app=api
如果你的 FastAPI 本身有某個 endpoint 會真的讀寫 Redis,可以直接呼叫那個 endpoint:
curl localhost:8080
但這裡要注意:
單純看到 API 回傳 HTTP 200,不一定就能證明 API → Redis 正常。
因為那個 API Endpoint 必須真的有使用 Redis 才算。
更直接的思考應該是:
API Pod
│
▼
Redis
✅
只要來源 Pod 符合:
app=api
而且連的是:
TCP 6379
NetworkPolicy 就會允許。
NetworkPolicy 有一個非常重要的特性:
Additive
Additive 的意思就是:
多份 NetworkPolicy 的「允許條件」會累加在一起。
它不像某些傳統 Firewall:
Rule 1
Rule 2
Rule 3
找到第一個 Match
↓
停止
NetworkPolicy 不是這種:
First Match Wins
的模式。
假設 Redis 同時被兩份 Policy 選到:
Policy A
允許 API
Policy B
允許 Monitoring
最後的結果會是:
API
┐
├──→ Redis
Monitor
┘
兩邊都可以。
也就是允許集合會合併。
剛剛我們只保護了:
Redis
但 Production 常見的安全思維是反過來:
一開始全部都不准,再逐一開放真正需要的連線。
這叫做:
Default Deny
建立:
vim k8s/default-deny-ingress.yaml
內容:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: cka-lab
spec:
podSelector: {}
policyTypes:
- Ingress
這裡最重要的是:
podSelector: {}
空的 Selector 代表:
cka-labNamespace 裡的所有 Pod。
而這份 Policy 又宣告:
policyTypes:
- Ingress
但完全沒有:
ingress:
允許規則。
因此意思就是:
cka-lab 裡所有 Pod
↓
Ingress 預設全部拒絕
套用:
kubectl apply \
-f k8s/default-deny-ingress.yaml
這時你就不能再假設:
Frontend → API
API → Redis
API → PostgreSQL
會自動成功。
而是要一條一條建立:
Frontend → API
✅
API → Redis
✅
API → PostgreSQL
✅
其他沒有明確需要的流量
❌
這種安全原則叫:
Least Privilege
也就是:
最小權限原則。
一個 Component 只取得完成工作所需要的最低權限。
所以 Production 常見的網路安全設計就是:
Default Deny
+
明確 Allow Rule
+
Least Privilege
導入 NetworkPolicy 之後,Kubernetes Networking Troubleshooting 就會多一層。
有一天你可能看到:
API Pod
Running
Redis Pod
Running
Service
正常
DNS
正常
EndpointSlice
正常
但 Application 還是:
Connection Timeout
以前你可能只會一直查:
kubectl get svc
kubectl get pods
kubectl get endpointslice
但現在還要想到:
kubectl get netpol \
-n cka-lab
因為很可能:
Application 沒壞
Service 沒壞
DNS 也沒壞
只是 NetworkPolicy 不讓你過。
這也是為什麼 NetworkPolicy 設定錯誤時,常常看起來很像應用程式本身出了問題。
學到這裡之後,遇到:
Pod A 連不到 Pod B
可以開始按照這個順序思考:
DNS 能不能解析?
↓
Service 設定正不正確?
↓
EndpointSlice 有沒有 Backend?
↓
Backend Pod 是否 Running / Ready?
↓
NetworkPolicy 是否允許這個流量?
這會比看到:
Connection Timeout
就直接猜 Application 壞掉有效率很多。
今天 NetworkPolicy 最重要的不是把 YAML 背起來,而是先建立一個網路安全觀念。
原本 Kubernetes 是:
Pod
↕
Pod
彼此可以自由溝通。
加入 NetworkPolicy 之後,我們可以變成:
Frontend
│
▼
API
│ │
▼ ▼
Redis PostgreSQL
只有真正需要的網路路徑才被允許。
記住 NetworkPolicy YAML 最重要的閱讀順序:
spec.podSelector
↓
這份 Policy 保護誰?
ingress.from
↓
誰可以進來?
ports
↓
可以連哪個 Port?
以及一個實務上很重要的前提:
NetworkPolicy
只是規則
CNI,例如 Calico
才是真正執行規則的人
因此今天我們學到的是:
控制:
Pod → Pod
下一個很自然的問題則是:
誰可以操作 Kubernetes?
例如:
誰可以 get Pod?
誰可以 delete Pod?
誰可以修改 Deployment?
這就不再是網路權限,而是:
Identity
↓
Kubernetes API
所以下一篇就可以進入 Kubernetes 另一個非常重要的權限控制機制:
RBAC
也就是:
Role-Based Access Control
「以角色為基礎的存取控制」。
我們明天見!