Mạng Linux & Container/NetworkPolicy — zero-trust giữa các pod
20/21
Bài 20 / 21~14 phútKubernetes Networking (nhập môn)Miễn phí lượt xem

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 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 0102 chốt flat pod IP + CNI. Bài 0304 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 modeltoà 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.

  1. Default allow = chưa gắn thẻ từ chối: mọi căn nói chuyện tự do.
  2. NetworkPolicy = luật khóa từng căn (hoặc cả tầng/namespace): “căn app=payment chỉ nhận cuộc từ căn app=frontend port 8080”.
  3. podSelector = căn nào bị luật này áp (subject).
  4. 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).
  5. 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ườngNetworkPolicy
Toà mở mặc địnhKhông policy → allow all
Luật khóa theo cănNetworkPolicy + podSelector
Chiều vào / raIngress / Egress
Thẻ căn / thẻ tầngpodSelector / namespaceSelector
Dải số ngoài toàipBlock CIDR
Bảo vệ thậtCNI policy engine
💡 Cách nhớ

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 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:

  1. Nếu A isolated egress → cần egress rule trên policy của A cho phép tới B (peer + port).
  2. Nếu B isolated ingress → cần ingress rule trên policy của B cho phép từ A.
  3. 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
FieldViệc
metadata.namespacePolicy sống trong ns; subject là pod cùng ns (trừ peer cross-ns qua namespaceSelector)
spec.podSelectorSubject — pod nào bị policy áp. {} = mọi pod trong ns
policyTypesIngress, Egress, hoặc cả hai — chiều cô lập
ingress[]Allow-list vào subject
egress[]Allow-list ra từ subject
from / toPeer (OR trong một rule khi nhiều phần tử mảng)
portsTCP/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/tocả namespaceSelector 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

Tự điền

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 frombị chặn (nếu CNI enforce + default-deny/isolation đã bật).

4. Selector peer — pod, namespace, ipBlock

4.1 Ba loại peer

PeerChọnGhi chú nhập môn
podSelector (một mình trong entry)Pod cùng namespace policyLabel workload, không “tên Service”
namespaceSelector (một mình)Mọi pod trong ns khớp labelCross-ns rộng — cẩn thận
namespaceSelector + podSelector (cùng entry)Pod khớp trong ns khớpCross-ns hẹp
ipBlockCIDR (+ 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?”

Tự chẩn trước đáp án

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 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.

  1. default-deny-ingress + default-deny-egress trên ns shop (hoặc deny từng chiều có chủ đích).
  2. allow-dns-egress (UDP/TCP 53 → CoreDNS).
  3. Ingress payment: from frontend + from order (hai peer hoặc hai policy), port app.
  4. 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ó objectChỉ etcd/API
kubectl describe rule đúngVẫn chỉ control-plane view
Pod A vẫn curl pod B mọi portKhông enforce hoặc selector/rule sai
CNI kindnet / Flannel-style labThườ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 02Ingress without controller: API desire ≠ dataplane.

6.2 Selector / label drift

  • podSelector không khớp label Deployment → policy không isolate đúng pod (pod vẫn non-isolated bởi policy đó).
  • Peer app=fronted typo → 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”

Tự chẩn class

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

📚 Deep Dive — tài liệu gốc

Spec / docs:

Nội bộ OLHub:

Liên hệ các bài khác

Tóm tắt

  • Default allow khi không isolated; policy + policyTypesallow-list only.
  • Design: subject podSelector, peer from/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

Tự kiểm tra
Q1
Pod 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ì?
Non-isolated: inbound được phép (model). Không có default deny toàn cục chỉ vì cài Kubernetes — deny chỉ khi policy isolate (vd default-deny với podSelector rỗng). Admin chưa apply → full allow east-west (trong model + CNI connectivity).
Q2
A 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?
Cần egress allow trên A (peer B + port) VÀ ingress allow trên B (peer A + port). Chỉ mở B: A không ra được → timeout/fail từ A; dễ đổ oan Service/DNS nếu chưa tách chiều.
Q3
Phâ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?
Hai phần tử mảng: OR (peer khớp một trong hai). Một phần tử chứa cả namespaceSelector và podSelector: AND (pod khớp trong ns khớp). Indent YAML sai đổi nghĩa — describe để verify.
Q4
Vì 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.
Object chỉ API. Cần CNI/plugin implement NetworkPolicy, rule/selector khớp pod thật, (nếu egress deny) DNS path. Test: exec từ pod không thuộc allow-list curl subject — vẫn OK = no enforce hoặc selector hỏng.
Q5
default-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)?
Deny egress chặn UDP/TCP 53 tới CoreDNS. Thêm egress allow DNS (namespaceSelector kube-system + podSelector DNS, port 53) trước hoặc cùng lúc siết app egress.
Q6
Hai 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?
Còn — additive union. Policy order mở thêm peer, không thu hẹp peer frontend. Muốn thu hẹp phải sửa/xoá rule allow không mong muốn, không 'policy mới đè'.
Q7
Design 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?
default-deny-ingress (podSelector rỗng chọn mọi pod + policyTypes Ingress) + allow-frontend-to-payment (subject payment, from frontend, TCP 8080). Không CNI enforce: cả hai object xanh nhưng attacker vẫn full-mesh — dạy pitfall, không phải API hỏng.

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

Đặt 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

Bài tiếp theo

Tổng kết module & track Networking