一座叢集中,會部署很多應用,然而在叢集內,彼此預設是互通的。 我們不能假設每個在使用叢集的程式、人都會乖乖地待在自己被允許的 namespace 中。 有些時候不安分的流量會掃描、試探 Cluster IP 去偷襲別人。 接著來理解 NetworkPolicy 核心觀念、常見範本、以及OCP 特有資源(EgressFirewall/EgressIP)。
多個 Policy 疊加時:允許的規則會合併成 聯集(Union),也就是「誰放行,就能通過」。
但是 你不能寫一條規則來明確拒絕某來源,因為 NetworkPolicy 沒有 deny 語法。
api
Pod 被以下兩條 policy 選中:web
Pod 連進來
ingress:
- from:
- podSelector:
matchLabels:
app: web
ingress:
- from:
- podSelector:
matchLabels:
app: debug
web
可以連debug
可以連db
(資料庫 Pod)不能連,因為沒被任何 policy 放行以下範例,假設存在某前端、後端的 Pod 在某 namespace 中,各自都帶上 label 了。 根據下列範本建立 Lab 吧。
# 建兩個專案(Namespace)
oc new-project team-a
oc new-project team-b
# 在 team-a 部署兩個角色:api 與 web
oc -n team-a create deploy api --image=ghcr.io/jmalloc/echo-server:latest --port=8080
oc -n team-a expose deploy/api --port=8080 --target-port=8080
oc -n team-a label deploy api app=api tier=backend --overwrite=true
oc -n team-a create deploy web --image=nginx:alpine
oc -n team-a label deploy web app=web tier=frontend --overwrite=true
# 在 team-b 放一個 busybox 當「外人」
oc -n team-b run dbg --image=busybox:1.36 --restart=Never --command -- sh -c 'sleep 1d'
設定 NetworkPolicy 前,來測測看什麼叫做暢行無阻。
# 先找出 team-a 內部服務的 ClusterIP 與 Port
oc -n team-a get svc api
# 從 team-a 的 web Pod 嘗試連 api
WEB_POD=$(oc -n team-a get po -l app=web -o jsonpath='{.items[0].metadata.name}')
oc -n team-a exec -ti $WEB_POD -- wget -qO- http://api:8080
# 從 team-b 的 dbg 嘗試連 team-a 的 api(預期成功,因為還沒隔離)
oc -n team-b exec -ti dbg -- wget -qO- http://api.team-a.svc.cluster.local:8080
- 前端,可以類比成餐館的用餐區,有外場服務生服務客人,客人可以依照菜單上提供的項目點餐,或者是當一個奧客搞破壞。
- 後端,可以類比成餐館的廚房,和內場廚師。 餐飲業稱這個叫做後場或是內場。
- 資料庫,可類比成食材庫房和倉庫。
# deny-all-backend.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-backend
namespace: team-a
spec:
podSelector:
matchLabels:
tier: backend
policyTypes:
- Ingress
- Egress
寫完 yaml 記得套用
team-a
namespace 的 Pod 訪問 8080 port# allow-frontend-to-backend-8080.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-backend-8080
namespace: team-a
spec:
podSelector:
matchLabels:
tier: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
tier: frontend
ports:
- protocol: TCP
port: 8080
寫完 yaml 之後也要套用
# 同 NS 前端打後端 -> 成功
oc -n team-a exec -ti $WEB_POD -- wget -qO- http://api:8080
# team-b 外部打後端 -> 失敗
oc -n team-b exec -ti dbg -- wget -qO- http://api.team-a.svc.cluster.local:8080 || echo "blocked"
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-same-namespace
namespace: team-a
spec:
podSelector: {} # 選到本 NS 所有 Pod → 都開始被隔離
policyTypes: [Ingress]
ingress:
- from:
- podSelector: {} # 同 NS 內任何 Pod
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-partner-ns
namespace: team-a
spec:
podSelector:
matchLabels:
app: api
policyTypes: [Ingress]
ingress:
- from:
- namespaceSelector:
matchLabels:
access: partner
ports:
- protocol: TCP
port: 8080
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: egress-to-cidr
namespace: team-a
spec:
podSelector:
matchLabels:
app: api
policyTypes: [Egress]
egress:
- to:
- ipBlock:
cidr: 10.0.0.0/8
except:
- 10.10.0.0/16
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-80-443-only
namespace: team-a
spec:
podSelector:
matchLabels:
app: web
policyTypes: [Ingress]
ingress:
- ports:
- protocol: TCP
port: 80
- protocol: TCP
port: 443
dnsName
。apiVersion: k8s.ovn.org/v1
kind: EgressFirewall
metadata:
name: default
namespace: team-a
spec:
egress:
- type: Allow
to:
dnsName: api.github.com
- type: Allow
to:
cidrSelector: 0.0.0.0/0
ports:
- protocol: TCP
port: 443
備註一: EgressFirewall 設定的目標是 namespace,不是 pod
備注二: 通常會先一條允許特定 FQDN,再用第二條收斂到 443,剩下由 DNS 名稱控管。
為了讓某些 Namespace 的對外連線,NAT 成指定的 Egress IP(給防火牆白名單或第三方 API 綁定)。 在 Namespace 打上 k8s.ovn.org/egress-ips annotation,並在叢集層配置可用 Egress IP 與負載承載節點(此段通常由叢集管理員配置)。
oc describe networkpolicy
看 PodSelector 是否真的覆蓋到你的 Pod。NetworkPolicy 就是在上述的最後一段,判斷要不要放行。
解得出 Service Name 不代表有被允許連線喔!!