So sánh Kubernetes NetworkPolicy và CiliumNetworkPolicy
Kubernetes NetworkPolicy tiêu chuẩn (K8s Native) chỉ hỗ trợ lọc lưu lượng ở lớp mạng (L3/L4) dựa trên IP và Port, đồng thời bị giới hạn ở các trường hợp sử dụng cơ bản như Allow/Deny theo Namespace hoặc Pod selector.
CiliumNetworkPolicy mở rộng khả năng này bằng cách tận dụng eBPF để lọc lưu lượng chính xác hơn, hỗ trợ định tuyến dựa trên nhãn (Labels) phức tạp, Identity, và các trường HTTP (L7) mà không cần Sidecar proxy, giúp giảm độ trễ đáng kể.
Sử dụng CiliumNetworkPolicy cho phép bạn áp dụng các chính sách bảo mật granular hơn, ví dụ: chỉ cho phép Pod A truy cập Pod B trên cổng 8080 nhưng chỉ với phương thức GET, điều mà NetworkPolicy gốc không làm được.
Cấu trúc và cú pháp của CiliumNetworkPolicy
Để bắt đầu, chúng ta cần hiểu rõ cấu trúc YAML của CiliumNetworkPolicy. Khác với NetworkPolicy chuẩn, Cilium sử dụng các trường `endpointSelector` để chọn Pod nguồn và `toEndpoints` hoặc `fromEndpoints` để định nghĩa đích đến hoặc nguồn gốc của lưu lượng.
Dưới đây là một ví dụ cơ bản về việc cho phép lưu lượng HTTP (cổng 80) từ tất cả Pod có nhãn `role: frontend` đến Pod có nhãn `role: backend`.
File cấu hình sẽ được lưu tại đường dẫn: /home/admin/cilium-policies/frontend-to-backend.yaml
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: frontend-to-backend-allow
namespace: default
spec:
endpointSelector:
matchLabels:
role: backend
ingress:
- fromEndpoints:
- matchLabels:
role: frontend
toPorts:
- ports:
- port: "80"
protocol: TCP
Kết quả mong đợi: Khi áp dụng file này, Cilium sẽ tạo các luật eBPF trong kernel chỉ cho phép gói tin TCP từ Pod frontend đi vào Pod backend trên cổng 80, mọi lưu lượng khác đến cổng 80 từ nguồn khác sẽ bị Drop.
Áp dụng chính sách Deny-by-Default để cô lập dịch vụ
Nguyên tắc bảo mật tốt nhất là "Deny-by-Default": mặc định chặn mọi lưu lượng và chỉ cho phép những gì cần thiết. Kubernetes NetworkPolicy tiêu chuẩn không có cơ chế này; bạn phải tạo một policy riêng để chặn tất cả.
Với Cilium, chúng ta có thể tạo một CiliumNetworkPolicy áp dụng cho tất cả Pod trong namespace để chặn toàn bộ lưu lượng nhập (ingress) và xuất (egress), sau đó tạo các policy cho phép cụ thể để mở "cửa" cho các dịch vụ cần thiết.
Tạo file cấu hình chính sách mặc định chặn tại: /home/admin/cilium-policies/deny-all-default.yaml
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: deny-all-default
namespace: default
spec:
endpointSelector:
matchLabels:
k8s:app: "*"
ingress:
- {}
egress:
- {}
Kết quả mong đợi: Ngay sau khi áp dụng, tất cả các Pod trong namespace `default` sẽ mất kết nối mạng với nhau và với bên ngoài. Đây là trạng thái "cô lập" hoàn toàn, đảm bảo an toàn tuyệt đối trước khi cấu hình các rule cho phép.
Viết chính sách cho phép lưu lượng theo Label và Pod cụ thể
Sau khi đã áp dụng chính sách chặn tất cả, chúng ta cần viết các chính sách cho phép (Allow Policy) để phục hồi kết nối cho các luồng dữ liệu hợp lệ. Cilium cho phép kết hợp nhiều điều kiện: Label của Pod, IP Address cụ thể, hoặc thậm chí là Identity.
Giả sử chúng ta có một ứng dụng gồm 2 thành phần: `api-server` (cần truy cập database) và `worker` (cần truy cập api-server). Chúng ta sẽ tạo policy cho phép `api-server` kết nối đến `database` trên cổng 5432.
Lưu file cấu hình tại: /home/admin/cilium-policies/api-to-db.yaml
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: api-to-db-allow
namespace: default
spec:
endpointSelector:
matchLabels:
app: api-server
egress:
- toEndpoints:
- matchLabels:
app: database
toPorts:
- ports:
- port: "5432"
protocol: TCP
Kết quả mong đợi: Pod `api-server` sẽ có thể thực hiện kết nối TCP đến Pod `database` trên cổng 5432. Mọi kết nối từ `api-server` đến các đích khác (trừ database) hoặc từ các Pod khác đến database đều sẽ bị chặn do policy deny-all.
Áp dụng chính sách và kiểm tra trạng thái
Để áp dụng các chính sách vừa tạo, chúng ta sử dụng lệnh `kubectl apply`. Cần đảm bảo rằng Cilium Hubble hoặc Cilium Agent đang chạy để chính sách được biên dịch vào eBPF.
Thực thi lệnh áp dụng cả 3 file chính sách đã tạo (deny-all, frontend-to-backend, api-to-db).
kubectl apply -f /home/admin/cilium-policies/deny-all-default.yaml
kubectl apply -f /home/admin/cilium-policies/frontend-to-backend.yaml
kubectl apply -f /home/admin/cilium-policies/api-to-db.yaml
Kết quả mong đợi: Terminal trả về thông báo `ciliumnetworkpolicy.cilium.io "deny-all-default" created`, `... "frontend-to-backend-allow" created`, v.v. Không có lỗi syntax.
Để kiểm tra xem Cilium đã nhận diện và áp dụng chính sách vào các Pod chưa, chúng ta dùng lệnh `cilium policy` để xem danh sách các policy đang active.
cilium policy list
Kết quả mong đợi: Bạn sẽ thấy danh sách các policy với trạng thái `Status: Active` và số lượng Endpoint đã áp dụng (Enforced). Nếu thấy `Status: Invalid`, cần kiểm tra lại cú pháp YAML.
Kiểm tra và gỡ lỗi chính sách bằng cilium policy report
Khi chính sách không hoạt động như mong đợi, việc debug bằng cách xem log thông thường rất khó khăn. Lệnh `cilium policy report` là công cụ mạnh mẽ giúp bạn mô phỏng luồng truy cập giữa hai Pod và xem chính sách nào đang cho phép hoặc chặn.
Lệnh này cho phép bạn chỉ định Pod nguồn (source) và Pod đích (destination) để Cilium phân tích các rule đang áp dụng.
Giả sử bạn muốn kiểm tra xem Pod `frontend-xyz` có thể truy cập Pod `backend-abc` trên cổng 80 không.
cilium policy report --source "role=frontend" --destination "role=backend" --port 80
Kết quả mong đợi: Output sẽ hiển thị một bảng với các cột: Source, Destination, Port, Action. Nếu chính sách đúng, Action sẽ là `Allow` và có thể hiển thị tên policy (ví dụ: `frontend-to-backend-allow`). Nếu bị chặn, Action sẽ là `Deny` và có thể hiển thị `deny-all-default`.
Để kiểm tra chi tiết hơn về lý do bị chặn (Drop reason), bạn có thể thêm flag `--verbose` để xem chi tiết các rule eBPF đang tham gia quyết định.
cilium policy report --source "role=frontend" --destination "role=backend" --port 80 --verbose
Kết quả mong đợi: Bạn sẽ thấy thông tin chi tiết về Identity của Pod nguồn và đích, cũng như các rule cụ thể trong CiliumNetworkPolicy đã được khớp (matched) để đưa ra quyết định Allow hoặc Deny.
Verify kết quả bằng thực nghiệm trong môi trường thực
Để chắc chắn chính sách hoạt động đúng trong môi trường thực tế, chúng ta cần thực hiện các test case đơn giản bằng cách gửi gói tin từ Pod này sang Pod khác.
Sử dụng lệnh `kubectl exec` để chạy `curl` hoặc `nc` từ Pod nguồn đến Pod đích.
Test Case 1: Kiểm tra luồng được phép (Frontend -> Backend port 80).
kubectl exec -it -- curl -s :80
Kết quả mong đợi: Trả về HTTP 200 OK hoặc nội dung HTML của backend. Kết nối thành công.
Test Case 2: Kiểm tra luồng bị chặn (Frontend -> Backend port 8080 - không có policy).
kubectl exec -it -- curl -s --connect-timeout 2 :8080
Kết quả mong đợi: Lệnh bị timeout hoặc trả về lỗi "Connection refused" (nếu port không mở) hoặc "Connection timed out" (do packet bị drop bởi eBPF). Điều này chứng tỏ chính sách Deny-by-Default đang hoạt động.
Test Case 3: Kiểm tra luồng bị chặn (Pod không có label -> Backend).
kubectl exec -it -- curl -s --connect-timeout 2 :80
Kết quả mong đợi: Kết nối bị từ chối hoặc timeout, xác nhận rằng chỉ Pod có label `role: frontend` mới được truy cập.
Điều hướng series:
Mục lục: Series: Xây dựng nền tảng Secure Service Mesh với eBPF, Cilium và Policy Engine
« Phần 2: Triển khai Cilium và cấu hình nền tảng eBPF cơ bản
Phần 4: Cấu hình Policy Engine và tích hợp OPA (Open Policy Agent) »