Network Policies trong Kubernetes

Trong k8s, việc kiểm soát luồng traffic giữa các pod rất quan trọng để đảm bảo an toàn và hiệu suất của ứng dụng. Network Policies là một công cụ giúp bạn định nghĩa các quy tắc mạng để kiểm soát việc trao đổi dữ liệu giữa các pod. Trong bài viết này, chúng ta sẽ tìm hiểu về cách hoạt động của network Policies trong kubernetes, cách áp dụng chúng và một số ví dụ cụ thể.

1. Luồng traffic và quy tắc

Khi bạn triển khai các dịch vụ như web server, API server, và database server trong Kubernetes, mỗi dịch vụ cần có các quy tắc mạng riêng để đảm bảo chỉ những dịch vụ được phép giao tiếp với nhau.

Ví dụ về các quy tắc mạng giữa các server:

  • Ingress (Traffic đến): Quy tắc cho phép web server nhận traffic HTTP trên cổng 80.
  • Egress (Traffic đi): Quy tắc cho phép web server gửi traffic tới API server trên cổng 5000.
  • Ingress cho API server: Cho phép API server nhận traffic trên cổng 5000.
  • Egress cho API server: Cho phép API server gửi traffic tới database server trên cổng 3306.
  • Ingress cho database server: Cho phép database server nhận traffic trên cổng 3306.

Network policies trong kubernestes

2. Mạng trong Kubernetes

Trong Kubernetes, mạng được cấu thành bởi các thành phần như node, pod, và service. Mỗi node, pod, và service đều có địa chỉ IP riêng biệt. Các pod giao tiếp với nhau thông qua một mạng riêng ảo (Virtual Private Network) mà không cần cấu hình thêm.

Mặc định, Kubernetes cho phép tất cả traffic giữa các pod và service mà không có bất kỳ hạn chế nào (tất cả đều được phép).

3. Network Policies trong Kubernetes

Network Policies là các đối tượng trong Kubernetes cho phép người dùng kiểm soát luồng traffic giữa các pod. Ví dụ, bạn có thể tạo một chính sách mạng để chặn web server truy cập trực tiếp vào database server.

3.1 Cách hoạt động của Network Policies

Một Network Policy sẽ được tạo trong một namespace cụ thể trong Kubernetes, tương tự như pod, replica set, hay service.

  • Nhãn (Label)Bộ chọn (Selector) được sử dụng để liên kết chính sách mạng với các pod.
  • Network Policies cho phép người dùng định nghĩa các quy tắc, ví dụ: chỉ cho phép ingress traffic từ API server đến database server trên cổng 3306.
  • Sau khi tạo, Network Policy sẽ chặn tất cả traffic không hợp lệ, chỉ cho phép traffic đáp ứng các quy tắc đã được định nghĩa.

3.2 Cách áp dụng Network Policies

  • Nhãn và Bộ chọn: Network Policy có thể liên kết với pod qua nhãn và bộ chọn để xác định các pod cụ thể mà chính sách sẽ áp dụng.
  • Quy Tắc (Rules): Các quy tắc trong Network Policy có thể bao gồm ingress (traffic đến), egress (traffic đi), và port (cổng giao tiếp).

Ví dụ: Bảo vệ Pod Database

Giả sử chúng ta muốn bảo vệ database pod, chỉ cho phép API pod truy cập vào cổng 3306 của nó. Cách thực hiện như sau:

  1. Tạo Network Policy với tên db-policy.
  2. Liên kết Network Policy với database pod thông qua nhãn role=db và bộ chọn matchLabels.
  3. Thêm Ingress:
    • Quy tắc ingress trong Network Policy sẽ định nghĩa từ pod nào (sử dụng podSelector cho API pod) và cổng nào (port 3306 với giao thức TCP) được phép truy cập.

Các Loại Selector trong from:

Có ba loại selector trong trường from của ingress:

  • podSelector: Chọn pod dựa trên nhãn.
  • namespaceSelector: Chọn namespace dựa trên nhãn.
  • ipBlock: Chọn dải IP.

Những selector này có thể được sử dụng riêng lẻ hoặc kết hợp để tạo các quy tắc chi tiết hơn.

4. Network Policies và các giải pháp mạng

Các giải pháp mạng trong Kubernetes, như Calico, Cube Router, Romana, và WaveNet, hỗ trợ Network Policies, giúp triển khai và áp dụng các chính sách này. Tuy nhiên, một số giải pháp mạng như Flannel không hỗ trợ Network Policies.

Lưu ý: Bạn vẫn có thể tạo Network Policies ngay cả khi giải pháp mạng không hỗ trợ, nhưng chúng sẽ không được áp dụng. Việc cấu hình các chính sách mạng cần thận trọng để không ảnh hưởng đến kết nối và chức năng của ứng dụng.

Ví dụ về Network Policy:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: internal-policy
  namespace: default
spec:
  podSelector:
    matchLabels:
      name: internal
  policyTypes:
  - Egress
  - Ingress
  ingress:
    - {}
  egress:
  - to:
    - podSelector:
        matchLabels:
          name: mysql
    ports:
    - protocol: TCP
      port: 3306
  - to:
    - podSelector:
        matchLabels:
          name: payroll
    ports:
    - protocol: TCP
      port: 8080
  - ports:
    - port: 53
      protocol: UDP
    - port: 53
      protocol: TCP

5. Hand-On Network Policy

Kịch bản

Giả sử chúng ta đang có một hệ thống gồm:

  • Namespace frontend chạy Nginx (web server).
  • Namespace backend chạy MongoDB (database).
  • Namespace default dùng để test.

Hand on Network Policy

Theo mặc định, nếu không cấu hình Network Policy thì tất cả các Pod trong tất cả namespace đều có thể giao tiếp với nhau. Điều này tiềm ẩn rủi ro bảo mật.

Mục tiêu của bài này là:

  • Chỉ cho phép Pod trong frontend (Nginx) truy cập vào backend (MongoDB).
  • Chặn toàn bộ các Pod từ namespace khác (như default) truy cập vào backend.
  • Namespace default vẫn có thể truy cập frontend (ví dụ truy cập web).

Hướng dẫn chi tiết

Bước 1: Tạo namespace

Chúng ta chia môi trường Kubernetes thành 2 không gian riêng biệt là frontendbackend.

kubectl create namespace frontend
kubectl create namespace backend
truongnt@masternode:~$ kubectl create namespace frontend
namespace/frontend created
truongnt@masternode:~$ kubectl create namespace backend
namespace/backend created

Bước 2: Gắn nhãn cho namespace frontend

Gắn nhãn cho namespace giúp chúng ta sau này lọc hoặc áp dụng policy dễ dàng hơn.

kubectl label namespace frontend name=frontend
truongnt@masternode:~$ kubectl label namespace frontend namspace=frontend
namespace/frontend labeled

Bước 3: Tạo deployment cho frontend (nginx)

Triển khai nginx trong namespace frontend

kubectl -n frontend create deployment frontend --image=nginx --port=80
kubectl -n frontend get deployments.apps
kubectl -n frontend get pods --watch
truongnt@masternode:~$ kubectl -n frontend create deployment frontend --image nginx --port 80
deployment.apps/frontend created
truongnt@masternode:~$ kubectl -n frontend get deployments.apps
NAME       READY   UP-TO-DATE   AVAILABLE   AGE
frontend   1/1     1            1           19s
truongnt@masternode:~$ kubectl -n frontend get pods --watch
NAME                        READY   STATUS    RESTARTS   AGE
frontend-6df94c895b-5dh6f   1/1     Running   0          41s

Bước 4: Tạo deployment cho backend (MongoDB)

kubectl -n backend create deployment backend --image=mongo --port=27017
kubectl -n backend get deployments.apps
kubectl -n backend get pods --watch
truongnt@masternode:~$ kubectl -n backend create deployment backend --image mongo --port 27017
deployment.apps/backend created
truongnt@masternode:~$ kubectl -n backend get deployments.apps
NAME      READY   UP-TO-DATE   AVAILABLE   AGE
backend   1/1     1            1           18s
truongnt@masternode:~$ kubectl -n backend get pods
NAME                       READY   STATUS    RESTARTS   AGE
backend-5fc6bb6f65-sdwft   1/1     Running   0          32s

Bước 5: Lấy tên và IP của các Pod

FRONTENDPODNAME=`kubectl -n frontend get pods -o jsonpath='{.items[0].metadata.name}'`
echo $FRONTENDPODNAME

BACKENDPODNAME=`kubectl -n backend get pods -o jsonpath='{.items[0].metadata.name}'`
echo $BACKENDPODNAME

FRONTENDPODIP=`kubectl -n frontend get pods -o jsonpath='{.items[0].status.podIP}'`
echo $FRONTENDPODIP

BACKENDPODIP=`kubectl -n backend get pods -o jsonpath='{.items[0].status.podIP}'`
echo $BACKENDPODIP
truongnt@masternode:~$ FRONTENDPODNAME=`kubectl -n frontend get pods -o jsonpath='{.items[0].metadata.name}'`
truongnt@masternode:~$ echo $FRONTENDPODNAME
frontend-6df94c895b-5dh6f

truongnt@masternode:~$ BACKENDPODNAME=`kubectl -n backend get pods -o jsonpath='{.items[0].metadata.name}'`
truongnt@masternode:~$ echo $BACKENDPODNAME
backend-5fc6bb6f65-sdwft

truongnt@masternode:~$ FRONTENDPODIP=`kubectl -n frontend get pods -o jsonpath='{.items[0].status.podIP}'`
truongnt@masternode:~$ echo $FRONTENDPODIP
192.168.166.129

truongnt@masternode:~$ BACKENDPODIP=`kubectl -n backend get pods -o jsonpath='{.items[0].status.podIP}'`
truongnt@masternode:~$ echo $BACKENDPODIP
192.168.104.1

Bước 7: Cài mongo shell trong frontend pod

kubectl -n frontend exec $FRONTENDPODNAME --stdin --tty -- sh

curl -O https://downloads.mongodb.com/compass/mongosh-2.5.3-linux-x64.tgz

tar -xvzf mongosh-2.5.3-linux-x64.tgz
cd mongosh-2.5.3-linux-x64

cp bin/mongosh /usr/local/bin/
mongosh --version

exit
truongnt@masternode:~$ kubectl -n frontend exec $FRONTENDPODNAME --stdin --tty -- sh
# mongosh --version

Bước 8: Kết nối thử từ các pod

kubectl run nginx --image=nginx

kubectl -n frontend exec $FRONTENDPODNAME --stdin --tty -- mongosh $BACKENDPODIP
exit

kubectl exec nginx --stdin --tty -- mongosh $BACKENDPODIP
exit

Bước 9: Tạo file NetworkPolicy

Tạo file tên networkpolicy-ingress.yaml với nội dung sau:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-to-backend
  namespace: backend
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
  - Ingress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          name: frontend
      podSelector:
        matchLabels:
          app: frontend

Bước 10: Áp dụng NetworkPolicy

kubectl create -f networkpolicy-ingress.yaml
kubectl get netpol -A

Bước 11: Kiểm tra kết nối sau khi áp dụng policy

kubectl -n frontend exec $FRONTENDPODNAME --stdin --tty -- mongosh $BACKENDPODIP

kubectl exec nginx --stdin --tty -- mongosh $BACKENDPODIP
kubectl exec nginx --stdin --tty -- curl $FRONTENDPODIP

Sau bước này bạn sẽ thấy chỉ có Pod frontend mới truy cập được backend, còn nginx (nằm ngoài namespace frontend) sẽ bị chặn — chính là nhờ NetworkPolicy đó!

Nguyễn Tiến Trường

Mình viết về những điều nhỏ nhặt trong cuộc sống, Viết về câu chuyện những ngày không có em