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).
flowchart LR
A["Pod A"] -->|"needs egress allow"| MID["Connection"]
MID -->|"needs ingress allow"| B["Pod B"]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 HOẶC mọi pod ns label team=payments
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
- namespaceSelector:
matchLabels:
team: payments
# AND: chỉ 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.
flowchart TB
DENY["default deny ns"] --> DNS["allow DNS egress"]
DENY --> PAY_IN["payment ingress from frontend order"]
DENY --> FE_OUT["frontend egress to payment"]
DENY --> OR_OUT["order egress to payment"]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”.
flowchart TD
SYM["Traffic still full mesh"] --> Q1{"CNI documents NetworkPolicy?"}
Q1 -->|No| C1["Install policy-capable CNI or accept open"]
Q1 -->|Yes| Q2{"podSelector matches subject pods?"}
Q2 -->|No| C2["Fix labels / selector"]
Q2 -->|Yes| Q3{"Both sides allow + DNS if egress deny?"}
Q3 -->|No| C3["Add peer rules / DNS egress"]
Q3 -->|Yes| C4["Plugin lag / hostNetwork / advanced path"]📚 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 đáng gửi cho bạn học cùng?
Copy link đã gắn nguồn — dán group, chat, hoặc LinkedIn.
Bài này có giúp bạn hiểu bản chất không?
Hỏi đáp về bài này
Chưa có câu hỏi
Có gì chưa rõ trong bài? Đặt câu hỏi đầu tiên — câu trả lời từ cộng đồng giúp bạn (và người sau).
Đặt câu hỏi đầu tiên