NetworkPolicy — zero-trust giữa các pod
Default allow mọi pod; NetworkPolicy ingress/egress + podSelector/namespaceSelector; cần CNI enforce; pitfall policy YAML xanh nhưng không chặn traffic.
TL;DR: Kubernetes default allow: không có NetworkPolicy chọn pod thì pod non-isolated — mọi chiều ingress/egress được phép (trong model cluster). NetworkPolicy (networking.k8s.io/v1) là luật L3/L4 (TCP/UDP/SCTP): chọn pod bằng podSelector, khai báo policyTypes Ingress/Egress, rồi allow-list from/to (podSelector, namespaceSelector, ipBlock) + ports. Policy additive (union); kết nối A→B cần cả egress phía A và ingress phía B. Object API luôn tạo được; enforcement chỉ khi CNI/plugin hỗ trợ. Pitfall cứng: apply xanh + kubectl get networkpolicies có row, traffic vẫn full-mesh — class “API no-op” giống Ingress thiếu controller.
On-call lab kind: junior apply default-deny-ingress + “chỉ frontend → payment”, kubectl get networkpolicy OK, kubectl exec pod attacker vẫn curl payment. Ticket: “NetworkPolicy K8s hỏng”. Bạn hỏi: “CNI nào? Plugin document enforce NetworkPolicy chưa? Label podSelector khớp pod thật? Có rule DNS egress nếu đã deny egress?” Nhiều cluster lab chỉ lo connectivity — policy object tồn tại, dataplane filter = 0.
Bài 01–02 chốt flat pod IP + CNI. Bài 03–04 chốt đường vào. Bài này chốt ai được nói chuyện với ai sau khi đã vào cluster — micro-segment L4, không thay authn/L7 mesh.
Bài faded: default-deny + allow frontend worked; bạn tự điền selector YAML và tự chẩn class “policy im lặng”.
1. Analogy — Toà mở cửa, rồi mới khóa theo thẻ
Pod model là toà có số máy phẳng: căn nào cũng gọi căn khác được (east-west open). Service/Ingress là tổng đài và lễ tân — chưa phải khóa phòng.
- Default allow = chưa gắn thẻ từ chối: mọi căn nói chuyện tự do.
- NetworkPolicy = luật khóa từng căn (hoặc cả tầng/namespace): “căn
app=paymentchỉ nhận cuộc từ cănapp=frontendport 8080”. podSelector= căn nào bị luật này áp (subject).from/to= ai được gọi vào / gọi ra (peer: thẻ căn, thẻ tầng/namespace, hoặc dải số ngoài).- CNI enforce = bảo vệ thật trên cửa — không có đội bảo vệ (plugin policy) thì tờ luật dán tường không chặn ai.
| Đời thường | NetworkPolicy |
|---|---|
| Toà mở mặc định | Không policy → allow all |
| Luật khóa theo căn | NetworkPolicy + podSelector |
| Chiều vào / ra | Ingress / Egress |
| Thẻ căn / thẻ tầng | podSelector / namespaceSelector |
| Dải số ngoài toà | ipBlock CIDR |
| Bảo vệ thật | CNI policy engine |
NetworkPolicy = allow-list sau khi isolated — không phải firewall “deny rule” tường minh từng nguồn. Default cluster mở; bạn cô lập rồi mở có chọn.
2. Default allow — khi nào pod bị cô lập?
2.1 Hai chiều isolation độc lập
Theo Network Policies (Kubernetes docs):
| Trạng thái | Ý nghĩa |
|---|---|
| Non-isolated (ingress) | Mọi inbound connection được phép (chiều vào) |
| Isolated (ingress) | Chỉ connection allow bởi ít nhất một policy Ingress áp pod + traffic từ node của pod (luôn được phép theo model) |
| Non-isolated (egress) | Mọi outbound được phép |
| Isolated (egress) | Chỉ outbound match allow-list egress của policy áp pod |
Default: pod non-isolated cả hai chiều cho đến khi có NetworkPolicy vừa chọn pod (spec.podSelector) vừa khai báo chiều đó trong policyTypes (hoặc suy ra theo rule egress — xem dưới).
2.2 Isolation không tuyệt đối “cấm hết mọi protocol”
NetworkPolicy nhắm TCP/UDP/SCTP (L4 connection). Hành vi ARP/ICMP… không đảm bảo đồng nhất giữa plugin — đừng assume “deny-all = im lặng ping”.
2.3 Cả hai phía phải allow
Gói từ pod A → pod B:
- Nếu A isolated egress → cần egress rule trên policy của A cho phép tới B (peer + port).
- Nếu B isolated ingress → cần ingress rule trên policy của B cho phép từ A.
- Một phía deny (không match allow-list) → connection không thành.
Đây là nguồn bug “policy frontend đúng nhưng payment vẫn timeout”: bạn mở ingress payment, quên egress frontend (hoặc ngược).

3. Cấu trúc NetworkPolicy — subject, type, rules
3.1 Skeleton tối thiểu (minh hoạ)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-payment
namespace: shop
spec:
podSelector:
matchLabels:
app: payment
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
| Field | Việc |
|---|---|
metadata.namespace | Policy sống trong ns; subject là pod cùng ns (trừ peer cross-ns qua namespaceSelector) |
spec.podSelector | Subject — pod nào bị policy áp. {} = mọi pod trong ns |
policyTypes | Ingress, Egress, hoặc cả hai — chiều cô lập |
ingress[] | Allow-list vào subject |
egress[] | Allow-list ra từ subject |
from / to | Peer (OR trong một rule khi nhiều phần tử mảng) |
ports | TCP/UDP/SCTP + port (hoặc range endPort nếu plugin support) |
policyTypes rỗng / omit: docs — mặc định luôn coi có Ingress; Egress chỉ được set nếu policy có rule egress. Production: khai tường minh policyTypes để khỏi “tưởng deny egress”.
3.2 Default deny ingress (namespace)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: shop
spec:
podSelector: {}
policyTypes:
- Ingress
# no ingress rules → isolated, zero allow-list
Sau object này, mọi pod trong shop isolated ingress — chỉ traffic node→pod + rule allow khác còn sống.
3.3 Default deny egress — nhớ DNS
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-egress
namespace: shop
spec:
podSelector: {}
policyTypes:
- Egress
Deny-all egress cũng chặn DNS tới CoreDNS (thường ns kube-system). Workload curl payment-api.shop.svc fail từ resolve trước cả TCP app — triệu chứng hay bị gán nhầm “Service hỏng”.
Allow DNS (minh hoạ — label CoreDNS/cluster thực tế có thể khác; đo trên cluster bạn):
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns-egress
namespace: shop
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
Lưu ý: một entry from/to có cả namespaceSelector và podSelector = AND (pod trong ns khớp). Hai phần tử mảng riêng = OR. Sai indent YAML đổi hẳn nghĩa — kubectl describe networkpolicy để đối chiếu.
3.4 Faded — viết allow frontend → payment
Namespace shop. Subject: pod app=payment. Chỉ Ingress. Cho phép từ pod cùng ns app=frontend, TCP 8080.
Viết podSelector subject + một rule ingress.from + ports (không cần full file).
Sau default-deny-ingress, chỉ policy này — pod app=order curl payment được không?
Đáp án kỳ vọng:
spec:
podSelector:
matchLabels:
app: payment
policyTypes: [Ingress]
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
Pod order không match from → bị chặn (nếu CNI enforce + default-deny/isolation đã bật).
4. Selector peer — pod, namespace, ipBlock
4.1 Ba loại peer
| Peer | Chọn | Ghi chú nhập môn |
|---|---|---|
| podSelector (một mình trong entry) | Pod cùng namespace policy | Label workload, không “tên Service” |
| namespaceSelector (một mình) | Mọi pod trong ns khớp label | Cross-ns rộng — cẩn thận |
| namespaceSelector + podSelector (cùng entry) | Pod khớp trong ns khớp | Cross-ns hẹp |
| ipBlock | CIDR (+ optional except) | IP ổn định ngoài / đặc biệt; đừng pin pod IP ephemeral |
NetworkPolicy không target Service by name. Target label pod (backend thật) hoặc ns/CIDR. Service chỉ là VIP — filter thường nhìn pod identity / IP sau rewrite tùy plugin (edge case Service external — ngoài depth bài).
4.2 OR vs AND trong from
# OR: frontend local HOAC moi pod ns label team=payments
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
- namespaceSelector:
matchLabels:
team: payments
# AND: chi pod app=client TRONG ns user=alice
ingress:
- from:
- namespaceSelector:
matchLabels:
user: alice
podSelector:
matchLabels:
app: client
4.3 Namespace theo tên
API không có field “namespace name” thuần. Control plane gắn label bất biến kubernetes.io/metadata.name=<tên-ns> — dùng trong namespaceSelector khi cần target đúng một ns theo tên.
4.4 Traffic node và hostNetwork
- Traffic từ node hosting pod → pod: model docs luôn allow (probe/kubelet path) — không “deny hết kể cả node”.
- Pod
hostNetwork: true: hành vi NetworkPolicy undefined / implementation-defined — nhiều plugin coi như traffic node. Đừng thiết kế zero-trust giả định hostNetwork bị filter như pod network.
4.5 Faded — đọc policy “có chặn attacker không?”
Có default-deny-ingress + policy allow from.podSelector app=frontend tới app=payment:8080.
(1) Pod app=frontend curl payment:8080?
(2) Pod app=frontend curl payment:9090?
(3) Pod app=attacker cùng ns curl 8080?
(4) Pod frontend ns khác (label khác) curl 8080?
Giả sử CNI có enforce.
Đáp án kỳ vọng: (1) allow. (2) deny (sai port). (3) deny. (4) deny — podSelector alone = cùng ns policy; cross-ns cần namespaceSelector.
5. Additive policy — union, không “conflict ghi đè”
5.1 Nhiều policy cùng subject
Nếu nhiều NetworkPolicy áp cùng pod cùng chiều: allow-list là hợp (union) các rule. Không có “policy sau ghi đè policy trước”. Order apply không đổi kết quả allow.
Hệ quả:
- Policy “hep” không thu hẹp policy “rộng” đã allow-all ingress (
ingress: [{}]). - Muốn siết: đừng để allow-all song song; review toàn bộ policy chọn pod.
5.2 Allow-all ingress (mở lại cả ns)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-ingress
namespace: shop
spec:
podSelector: {}
policyTypes:
- Ingress
ingress:
- {} # empty rule = allow all ingress
Với object này, không policy khác trong ns còn deny được inbound tới subject (union luôn include “all”).
5.3 Worked path — design tối thiểu shop
Mục tiêu: payment chỉ nhận frontend; order gọi payment; DNS ok; default deny còn lại.
default-deny-ingress+default-deny-egresstrên nsshop(hoặc deny từng chiều có chủ đích).allow-dns-egress(UDP/TCP 53 → CoreDNS).- Ingress payment: from frontend + from order (hai peer hoặc hai policy), port app.
- Egress frontend/order: to payment (và DNS đã cover).
Production còn probe, ingress-controller ns, metrics — bài nhập môn dừng ở mental model; checklist ops dài hơn recipes cộng đồng.

6. Pitfall — policy YAML xanh, traffic vẫn full-mesh
6.1 Class cứng: không có implementation
Docs Kubernetes: Creating a NetworkPolicy resource without a controller that implements it will have no effect.
| Quan sát | Ý nghĩa |
|---|---|
kubectl get networkpolicies có object | Chỉ etcd/API |
kubectl describe rule đúng | Vẫn chỉ control-plane view |
| Pod A vẫn curl pod B mọi port | Không enforce hoặc selector/rule sai |
| CNI kindnet / Flannel-style lab | Thường connectivity-only |
Verify hướng:
# What CNI / policy engine?
kubectl get pods -A | grep -iE 'calico|cilium|weave|kube-router|antrea|kindnet|flannel'
kubectl -n kube-system get ds,deploy 2>/dev/null | head
# Policy objects (API only)
kubectl get networkpolicies -A
kubectl describe networkpolicy -n shop allow-frontend-to-payment
# Effect test from a real client pod
kubectl exec -n shop deploy/attacker -- curl -sS -m 3 http://payment:8080/ || echo "blocked-or-fail"
Cùng class CNI bài 02 và Ingress without controller: API desire ≠ dataplane.
6.2 Selector / label drift
podSelectorkhông khớp label Deployment → policy không isolate đúng pod (pod vẫn non-isolated bởi policy đó).- Peer
app=frontedtypo → allow-list trống thực tế → “chặn nhầm” frontend hợp lệ. - Đổi label rolling update → peer/subject lệch tạm thời.
6.3 Chỉ mở một phía
Isolated cả hai đầu nhưng chỉ viết ingress payment, frontend deny-all egress → timeout. Checklist: egress client + ingress server + DNS.
6.4 Nhầm NetworkPolicy với authn / L7
- NetworkPolicy không TLS, không user identity HTTP, không “Service name ACL” đầy đủ.
- Không log “blocked connection” chuẩn API — quan sát thường bằng probe fail / metric CNI (Hubble…) / tcpdump.
- Ingress L7 không thay east-west zero-trust pod-to-pod.
6.5 Faded — ticket “policy không ăn”
Triệu chứng: get networkpolicy OK; describe rule frontend→payment; mọi pod ns vẫn curl payment.
Cluster: kind + CNI mặc định lab (không Calico/Cilium).
(A) Sai port trong rule? (B) CNI không enforce? (C) Quên DNS?
Chọn class chính + một lệnh/câu hỏi xác nhận.
Đáp án kỳ vọng: (B) — full-mesh mọi source (kể cả attacker) trỏ no implementation hơn “sai port” (sai port thì frontend đúng port vẫn có thể OK). Xác nhận: docs/CNI feature NetworkPolicy; test có/không engine; không “restart API server”.

📚 Deep Dive — tài liệu gốc
Spec / docs:
- Network Policies — isolation, selectors, default deny/allow, additive.
- Declare Network Policy — walkthrough.
- Network plugins — enforcement thuộc plugin.
- Network policy recipes — pattern cộng đồng (default-deny, DNS…).
Nội bộ OLHub:
- 02 — CNI — interface vs implementation; ai enforce policy.
- 01 — Pod networking — flat IP east-west baseline.
- 03 — Service — VIP vẫn đi tới pod; policy filter path pod.
- 04 — Ingress — cùng class API no-op khi thiếu controller.
Liên hệ các bài khác
- 02 — CNI — điều kiện policy có effect; Flannel-style vs Calico/Cilium.
- 01 — Pod networking — full-mesh là default model, policy là lớp siết sau.
- 03 — Service & kube-proxy — client→VIP→pod; isolate pod, không “tên Service”.
- 04 — Ingress — cửa ngoài L7; NetworkPolicy lo east-west (và egress) L4.
- 00 — Tổng quan M3 — NetworkPolicy chốt pipeline capstone.
- M1 — iptables/netfilter — họ filter L4 dưới dataplane (implementation-specific).
Tóm tắt
- Default allow khi không isolated; policy +
policyTypes→ allow-list only. - Design: subject
podSelector, peerfrom/to,ports; cả hai phía connection. - Union policies; allow-all
ingress: [{}]“bẻ” nỗ lực siết khác. - CNI phải enforce — object API xanh ≠ traffic bị chặn.
- Deny egress → DNS; label typo → no-op hoặc over-block.
7. Tự kiểm tra
- Q1Pod không bị NetworkPolicy nào chọn — ingress từ pod lạ trong cluster được phép không? 'Default deny' đến từ đâu nếu admin chưa apply gì?
- Q2A isolated egress, B isolated ingress. A→B: điều kiện nào? Chỉ mở ingress B, A đang default-deny-egress — triệu chứng?
- Q3Phân biệt from gồm hai phần tử mảng (podSelector + namespaceSelector riêng) với một phần tử có cả hai field. OR hay AND?
- Q4Vì sao kubectl get networkpolicy + describe rule đẹp chưa chứng minh traffic bị chặn? Liệt kê dependency dataplane + một test effect.
- Q5default-deny-egress xong app không resolve Service DNS. Root class? Hướng policy tối thiểu (không cần YAML đầy đủ production)?
- Q6Hai NetworkPolicy cùng chọn app=payment: một allow from frontend, một allow from order. Frontend còn vào được không sau khi thêm policy order? Vì sao?
- Q7Design tối thiểu: ns shop, payment chỉ nhận frontend:8080, còn lại deny ingress. Liệt kê object/policyTypes cần (tên logic). CNI lab không policy — expected effect?
Bài tiếp theo: Tổng kết module — Kubernetes Networking
Bài này có giúp bạn hiểu bản chất không?
Bài này đáng gửi cho bạn học cùng?
Copy link đã gắn nguồn — dán group, chat, hoặc LinkedIn.
Ingress & load balancing — L4 vs L7 vào cluster
Bài tiếpTổng kết module & track Networking
Thảo luận & góp ý
Hỏi đáp về bài này