ข้ามไปยังเนื้อหา

Network Policy

โดย default ทุก Pod ใน cluster คุยกับ Pod อื่นได้หมด และ NetworkPolicy คือสิ่งที่ให้คุณจำกัดให้เหลือแค่ traffic ที่อนุญาตไว้อย่างชัดเจนเท่านั้น

ตั้งแต่แกะกล่องมา networking ของ Kubernetes ไม่มี isolation เลย Pod ไหนก็ส่ง traffic ไปหา Pod อื่นได้หมด ไม่ว่าจะอยู่ namespace ไหน และแล้วแต่วิธี expose Service Pod ก็มักถูกเข้าถึงจากนอก cluster ได้ด้วย ไม่มี default-deny อยู่ที่ไหนเลย เรื่องนี้เข้าใจง่ายแต่หมายความว่า Pod ที่ถูก compromise หรือ config ผิดสามารถเข้าถึงส่วนอื่นของ cluster ได้มากกว่าที่ควรต้องการเยอะ

NetworkPolicy (API group networking.k8s.io/v1) เลือกกลุ่ม Pod ด้วย podSelector แล้วกำหนด rule ingress และ/หรือ egress ที่อนุญาตให้เฉพาะ Pod พวกนั้น

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-api
namespace: shop
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080

policy นี้มีผลแค่กับ Pod ที่ label app: api ใน namespace shop และอนุญาต inbound traffic แค่จาก Pod ที่ label app: frontend เท่านั้น บน TCP port 8080 เท่านั้น อย่างอื่นทั้งหมดที่ยิงเข้า Pod พวกนี้ถูก deny โดยปริยายทันทีที่มีอย่างน้อยหนึ่ง policy เลือก Pod เหล่านั้น

Terminal window
kubectl get networkpolicy -n shop
kubectl describe networkpolicy allow-frontend-to-api -n shop

นี่คือข้อควรระวังที่สำคัญที่สุด NetworkPolicy object เฉย ๆ ไม่มีผลอะไรเลยถ้า CNI plugin ของ cluster ไม่ได้ implement การบังคับใช้ NetworkPolicy จริง Kubernetes รับและเก็บ object นี้ได้เสมอ kubectl apply จะสำเร็จ แต่ถ้า CNI ไม่บังคับใช้ policy traffic จริงก็ไม่เปลี่ยนอะไรเลย ต้องเช็คก่อนเสมอว่า CNI ที่ติดตั้งอยู่รองรับ NetworkPolicy ก่อนจะพึ่ง NetworkPolicy เพื่อทำ isolation

จุดที่คนสับสนกันบ่อย Pod ที่ไม่มี NetworkPolicy ตัวไหนเลือกเลย จะยังคงเปิดเต็มที่ (allow-all) สำหรับทิศทาง traffic นั้น isolation เป็นแบบ opt-in ต่อ Pod ไม่ใช่ทั้ง cluster ถ้าจะปิด namespace หนึ่งให้แน่นหนา ต้องสร้าง policy ที่เลือก Pod ทุกตัวแต่ไม่อนุญาตอะไรเลยโดยตั้งใจ

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: shop
spec:
podSelector: {}
policyTypes:
- Ingress
ingress: []

podSelector: {} match ทุก Pod ใน namespace และ ingress: [] ที่ว่างเปล่าไม่อนุญาตอะไรเข้ามาเลย พอ policy นี้มีอยู่ ทุก Pod ใน shop จะถูก isolate ด้าน ingress โดย default แล้วคุณค่อยเพิ่ม NetworkPolicy object อื่นเข้าไปเพื่ออนุญาต traffic ที่ต้องการจริง ๆ แบบเจาะจง เช่นตัวอย่าง allow-frontend-to-api ด้านบน

การใส่ทั้ง podSelector และ namespaceSelector ไว้ใน entry from เดียวกัน จะจำกัด rule ให้แคบลงเหลือแค่ Pod เจาะจงใน namespace เจาะจงเท่านั้น ไม่ใช่ “Pod ไหนก็ได้ใน namespace นี้” หรือ “label นี้ที่ไหนก็ได้ในทั้ง cluster”

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-monitoring-scrape
namespace: shop
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: monitoring
podSelector:
matchLabels:
app: prometheus
ports:
- protocol: TCP
port: 9090

rule นั้นอนุญาต traffic เฉพาะจาก Pod ที่ label app: prometheus และรันอยู่ใน namespace monitoring เท่านั้น เป็น rule ควบคุมการเข้าถึงข้าม namespace ที่เจาะจงมาก

flowchart LR
  subgraph before["Before: default flat network"]
    a1["Pod A"] --- a2["Pod B"]
    a2 --- a3["Pod C"]
    a1 --- a3
  end
  subgraph after["After: NetworkPolicy applied"]
    b1["frontend"] -->|"allowed"| b2["api"]
    b3["random Pod"] -.->|"blocked"| b2
  end
Network แบบแบนราบที่ทุก Pod คุยกันได้หมดโดย default เทียบกับหลังใช้ NetworkPolicy จำกัด path ที่อนุญาต
ถ้าไม่มี NetworkPolicy object ถูกสร้างเลยสักตัว Pod ใน cluster คุยกันยังไงโดย default
อะไรที่จำเป็นเพื่อให้ NetworkPolicy object จำกัด traffic ได้จริง
จะสื่อความหมาย deny-all-ingress ให้กลุ่ม Pod ด้วย NetworkPolicy ยังไง
การรวม namespaceSelector กับ podSelector ไว้ใน NetworkPolicy rule เดียวให้ผลอะไร