Sổ tay kỹ thuật

Kubernetes NetworkPolicy – default deny và allow theo label

how-to#kubernetes#security#networking· cập nhật 2026-11-06

Dùng khi cần cô lập namespace bằng NetworkPolicy, chặn mặc định rồi mở theo label Pod/namespace, kèm egress DNS để Pod vẫn phân giải tên.

Sửa trang này
Mục lục

Tóm tắt: Pod mặc định nhận/gửi mọi traffic. Khi một Pod bị ít nhất một NetworkPolicy chọn (podSelector) cho chiều Ingress hoặc Egress, chiều đó chuyển thành chỉ cho phép những gì được liệt kê; các policy cộng dồn (OR), không có rule deny. Cách làm chuẩn: default deny cho namespace → allow DNS → allow từng luồng theo label. Cần CNI có enforce NetworkPolicy (Calico, Cilium, kube-router trong k3s); Flannel đơn thuần thì policy bị bỏ qua.

Luồng hoạt động

flowchart LR ING["ingress-nginx (ns ingress-nginx)"] -->|"TCP 8000"| API["Pod app=api (ns app)"] API -->|"TCP 5432"| DB["Pod app=postgres (ns app)"] API -->|"UDP/TCP 53"| DNS["CoreDNS (ns kube-system)"] X["Pod khác"] -.->|"bị chặn"| DB

Cấu hình

1. Default deny cho namespace

00-default-deny.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: app
spec:
  podSelector: {}            # mọi Pod trong namespace
  policyTypes: ["Ingress", "Egress"]

Chỉ muốn chặn chiều vào thì bỏ "Egress" khỏi policyTypes. Chặn egress mà không mở DNS là mọi Pod lỗi phân giải tên ngay lập tức.

2. Cho phép egress DNS

01-allow-dns.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns-egress
  namespace: app
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 }

Label kubernetes.io/metadata.name được Kubernetes tự gắn cho mọi namespace (từ 1.21), dùng được để chọn namespace theo tên. CoreDNS ở hầu hết distro (kể cả k3s) có label k8s-app=kube-dns; kiểm tra bằng kubectl get pods -n kube-system --show-labels.

3. Allow theo label Pod và namespace

10-api.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api
  namespace: app
spec:
  podSelector:
    matchLabels: { app: api }
  policyTypes: ["Ingress", "Egress"]
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: ingress-nginx
      ports:
        - { protocol: TCP, port: 8000 }
  egress:
    - to:
        - podSelector:
            matchLabels: { app: postgres }
      ports:
        - { protocol: TCP, port: 5432 }
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: postgres
  namespace: app
spec:
  podSelector:
    matchLabels: { app: postgres }
  policyTypes: ["Ingress"]
  ingress:
    - from:
        - podSelector:
            matchLabels: { app: api }
      ports:
        - { protocol: TCP, port: 5432 }

Port trong policy là port của Pod (containerPort), không phải port của Service.

AND và OR trong from/to

# OR: hai phần tử list → Pod app=api trong CÙNG namespace, HOẶC mọi Pod của namespace monitoring
- from:
    - podSelector: { matchLabels: { app: api } }
    - namespaceSelector: { matchLabels: { kubernetes.io/metadata.name: monitoring } }
 
# AND: một phần tử có cả hai selector → chỉ Pod app=prometheus TRONG namespace monitoring
- from:
    - namespaceSelector: { matchLabels: { kubernetes.io/metadata.name: monitoring } }
      podSelector: { matchLabels: { app: prometheus } }

Khác nhau đúng một dấu -. Đây là lỗi NetworkPolicy phổ biến nhất.

Egress ra ngoài cluster

egress:
  - to:
      - ipBlock:
          cidr: 203.0.113.0/24        # API đối tác
    ports:
      - { protocol: TCP, port: 443 }

NetworkPolicy chuẩn không lọc theo tên miền; cần FQDN policy của Cilium/Calico nếu muốn.

Kiểm tra

kubectl get networkpolicy -n app
kubectl describe networkpolicy api -n app
 
# Thử từ Pod có label app=api (phải thông) và Pod không label (phải bị chặn)
kubectl run t-api -n app --rm -it --image=nicolaka/netshoot --labels=app=api --restart=Never -- nc -zv -w 3 postgres 5432
kubectl run t-x   -n app --rm -it --image=nicolaka/netshoot --restart=Never -- nc -zv -w 3 postgres 5432
kubectl run t-dns -n app --rm -it --image=busybox:1.36 --restart=Never -- nslookup kubernetes.default

Lỗi thường gặp

Triệu chứng Nguyên nhân Cách xử lý
Apply policy xong không có gì bị chặn CNI không enforce (Flannel thuần, một số cấu hình mặc định) Kiểm tra CNI; dùng Calico/Cilium, hoặc k3s (có kube-router netpol)
Mọi Pod lỗi could not resolve host sau default deny Chặn egress nhưng chưa mở DNS Apply policy allow DNS (UDP và TCP 53)
Rule cho namespace không khớp Namespace không có label như selector kubectl get ns --show-labels, dùng kubernetes.io/metadata.name
Mở quá rộng: mọi Pod của namespace kia đều vào được Viết thành OR (hai phần tử) thay vì AND Gộp namespaceSelector và podSelector vào cùng một phần tử
Mở port 80 mà vẫn bị chặn Policy ghi port Service thay vì containerPort Dùng port của container (vd 8000)
Ingress controller, kubelet probe hoặc Prometheus bị chặn Default deny ingress Allow namespace của controller/monitoring; probe từ kubelet thường được CNI cho qua nhưng tuỳ CNI
Pod với hostNetwork: true không bị policy chọn Spec không định nghĩa hành vi cho host network, đa số CNI bỏ qua Lọc bằng firewall của node

Tham khảo