Hướng Dẫn Toàn Diện Kubernetes RBAC: Cấu Hình Role, ClusterRole và ServiceAccount Theo Nguyên Tắc Least Privilege

Security tutorial - IT technology blog
Security tutorial - IT technology blog

Ngày Suýt Sập Cả Hệ Thống

Một kỹ sư DevOps junior trong nhóm tôi từng triển khai CI/CD pipeline với build pod có quyền list, create và delete node trên toàn cluster. Mọi thứ chạy ngon lành suốt nhiều tháng — cho đến khi ai đó vô tình kích hoạt một cleanup script xóa sạch workload production. Nguyên nhân gốc rễ? ServiceAccount có quyền cluster-admin vì chẳng ai thèm giới hạn phạm vi từ đầu.

Một sự cố. Ba tiếng đồng hồ khôi phục. Một buổi post-mortem rất khó chịu.

Kinh nghiệm đó khắc vào đầu tôi một nguyên tắc: trong Kubernetes, những gì bạn không hạn chế sẽ sớm quay lại cắn bạn. Hướng dẫn này sẽ đi qua cách thiết lập RBAC đúng cách — không giả định kiến thức trước, không bỏ qua bước nào.

Tại Sao Quyền Truy Cập Quá Rộng Vẫn Cứ Xảy Ra

Hầu như luôn bắt nguồn từ một trong ba nguyên nhân:

  • Ưu tiên tốc độ hơn bảo mật — cấp cluster-admin để gỡ blocker cho ai đó trong lúc cấp bách
  • Không ai chịu trách nhiệm rõ ràng — không ai biết ServiceAccount thực sự cần gì, nên cấp hết cho xong
  • Cấu hình mặc định sai — ServiceAccount default trong namespace âm thầm tích lũy nhiều quyền hơn dự định

Kubernetes đã bật RBAC mặc định từ v1.6. Nhưng bật không có nghĩa là đã cấu hình. API server sẽ vui vẻ chấp nhận một RoleBinding trao cho pod quyền ghi toàn bộ secret trên tất cả namespace — không cảnh báo, không xác nhận, không rào chắn gì cả.

Kubernetes RBAC Hoạt Động Như Thế Nào

Toàn bộ hệ thống RBAC chỉ gồm bốn đối tượng. Nắm chắc bốn cái này trước, mọi thứ còn lại sẽ tự khắc rõ ràng:

  • Role — định nghĩa quyền trong một namespace cụ thể
  • ClusterRole — định nghĩa quyền trên toàn cluster, hoặc có thể tái sử dụng qua nhiều namespace
  • RoleBinding — gán một Role (hoặc ClusterRole) cho user, group, hoặc ServiceAccount trong một namespace
  • ClusterRoleBinding — gán một ClusterRole trên toàn cluster, không giới hạn namespace

ServiceAccount chỉ là một định danh mà pod dùng để xác thực với API server. Bản thân nó không làm được gì — quyền hạn chỉ tồn tại khi bạn gắn một binding vào.

Cách hình dung giúp tôi nhớ nhất: Role như bản mô tả công việc, còn Binding như hợp đồng lao động đã ký.

Ba Pattern Phổ Biến (Và Lý Do Hai Cái Có Vấn Đề)

Pattern 1: Dùng Luôn cluster-admin

Con đường ít kháng cự nhất. Bind ClusterRole cluster-admin có sẵn vào ServiceAccount rồi thôi.

# Đừng làm thế này trong production
kubectl create clusterrolebinding my-app-admin \
  --clusterrole=cluster-admin \
  --serviceaccount=production:my-app

Toàn quyền kiểm soát cluster. Tạo, xóa, sửa bất cứ thứ gì. Giải quyết được vấn đề trước mắt, nhưng từ đây bất kỳ ai exec được vào pod đó coi như làm chủ cluster của bạn. Một container bị compromise là xong.

Pattern 2: Dùng ClusterRole Có Sẵn

Kubernetes đi kèm khoảng 50 role có sẵn. Những cái thông dụng là view, editadmin. Tốt hơn cluster-admin, nhưng vẫn rộng hơn hầu hết workload cần.

# Tốt hơn, nhưng thường vẫn còn quá rộng
kubectl create rolebinding my-app-viewer \
  --clusterrole=view \
  --serviceaccount=production:my-app \
  --namespace=production

view cho quyền đọc hầu hết tài nguyên. edit thêm quyền ghi. Dùng được cho cài đặt nhanh — nhưng chúng bao gồm cả tài nguyên mà ứng dụng của bạn chắc chắn không bao giờ đụng đến, và mỗi quyền thừa đều là một rủi ro tiềm ẩn.

Pattern 3: Custom Role Theo Nguyên Tắc Least Privilege

Tốn công hơn lúc đầu. Nhưng lúc nào cũng đáng.

Bạn định nghĩa chính xác verb nào (get, list, watch, create, update, delete) áp dụng cho tài nguyên nào trong namespace nào. Không hơn không kém. Đây là cách duy nhất thực sự giới hạn phạm vi thiệt hại khi có sự cố — mà sự cố thì sớm muộn cũng xảy ra.

Thiết Lập RBAC Từng Bước

Hãy lấy một ví dụ cụ thể: ứng dụng monitoring đọc metrics của pod và node nhưng không được phép sửa bất cứ thứ gì.

Bước 1: Tạo Namespace và ServiceAccount Riêng Biệt

kubectl create namespace monitoring
kubectl create serviceaccount metrics-reader -n monitoring

Đừng bao giờ dùng ServiceAccount default cho workload thật. ServiceAccount riêng giúp việc audit rõ ràng và phạm vi quyền tường minh — bạn luôn biết chính xác cái gì được bind vào cái gì.

Bước 2: Định Nghĩa Role Với Quyền Tối Thiểu

Đọc metrics của node cần quyền truy cập trên toàn cluster, nên ở đây cần ClusterRole thay vì Role giới hạn namespace:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: metrics-reader
rules:
  - apiGroups: [""]
    resources: ["pods", "nodes", "nodes/metrics"]
    verbs: ["get", "list", "watch"]
  - apiGroups: ["metrics.k8s.io"]
    resources: ["pods", "nodes"]
    verbs: ["get", "list"]
kubectl apply -f metrics-reader-clusterrole.yaml

Ba điểm đáng lưu ý:

  • apiGroups: [""] nhắm vào core API group — pods, nodes, services, configmaps
  • Chỉ có get, list, watch — không có create, update hay delete
  • Tài nguyên được liệt kê tường minh, không dùng wildcard

Bước 3: Bind Role Vào ServiceAccount

Metrics của node cần tầm nhìn toàn cluster, nên ClusterRoleBinding là phù hợp ở đây:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: metrics-reader-binding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: metrics-reader
subjects:
  - kind: ServiceAccount
    name: metrics-reader
    namespace: monitoring
kubectl apply -f metrics-reader-binding.yaml

Bước 4: Gán ServiceAccount Cho Pod

apiVersion: v1
kind: Pod
metadata:
  name: metrics-collector
  namespace: monitoring
spec:
  serviceAccountName: metrics-reader
  containers:
    - name: collector
      image: your-metrics-image:latest

Bước 5: Kiểm Tra Thực Tế ServiceAccount Có Thể Làm Gì

Chạy lệnh này trước khi deploy — đừng chờ đến lúc có sự cố mới hỏi:

# Phải trả về: yes
kubectl auth can-i list pods \
  --as=system:serviceaccount:monitoring:metrics-reader

# Phải trả về: no
kubectl auth can-i delete pods \
  --as=system:serviceaccount:monitoring:metrics-reader

# Xem toàn bộ danh sách quyền
kubectl auth can-i --list \
  --as=system:serviceaccount:monitoring:metrics-reader

Nếu lệnh thứ hai trả về yes, Role của bạn đang quá rộng. Sửa ngay bây giờ, đừng chờ đến post-mortem.

Bước 6: Tắt Automount Khi Không Cần Thiết

Kubernetes tự động mount token của ServiceAccount vào mọi pod theo mặc định. Nếu pod của bạn không bao giờ gọi trực tiếp Kubernetes API, cái token đó chỉ là bề mặt tấn công nằm chờ trên filesystem:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: metrics-reader
  namespace: monitoring
automountServiceAccountToken: false

Hoặc tắt ở cấp độ từng pod:

spec:
  automountServiceAccountToken: false

Không có token trên disk đồng nghĩa không có cách nào xác thực với API server, dù role binding có tồn tại. Phòng thủ theo chiều sâu.

Những Lỗi Phổ Biến Cần Tránh

  • Dùng wildcard cho resources hoặc verbsresources: ["*"] cấp quyền truy cập mọi thứ. Đó chính là cluster-admin dưới vỏ bọc khác.
  • Dùng ClusterRoleBinding khi RoleBinding là đủ — nếu ứng dụng chỉ hoạt động trong một namespace, hãy giới hạn binding ở đó, dù Role có là ClusterRole đi nữa
  • Không bao giờ audit binding hiện có — chạy kubectl get clusterrolebindings -o wide định kỳ; quyền hạn tích lũy âm thầm theo thời gian là có thật
  • Bỏ qua bước kiểm tra auth can-i — năm phút xác minh còn hơn hai tiếng xử lý sự cố

Lưu Ý Về Thông Tin Xác Thực ServiceAccount

Khi cấu hình quyền truy cập cluster cho người dùng thật (không chỉ pod), bạn sẽ tạo file kubeconfig kèm thông tin xác thực. Với token và mật khẩu, tôi dùng công cụ tạo mật khẩu tại toolcraft.app/vi/tools/security/password-generator — hoạt động hoàn toàn trên trình duyệt, không có gì được gửi lên server. Với bất cứ thứ gì liên quan đến bảo mật, chạy phía client là điều bắt buộc.

Audit Cấu Hình RBAC

Hãy thiết lập audit định kỳ. Ba lệnh này bao phủ hầu hết những gì bạn cần:

# Tìm mọi ServiceAccount có quyền cluster-admin
kubectl get clusterrolebindings -o json | \
  jq '.items[] | select(.roleRef.name=="cluster-admin") | \
  {name: .metadata.name, subjects: .subjects}'

# Liệt kê tất cả RoleBinding trong một namespace
kubectl get rolebindings -n production -o wide

# Kiểm tra ai có thể thực hiện các thao tác nhạy cảm
kubectl auth can-i create pods --all-namespaces --list

Với cluster lớn hơn, rbac-lookupkubectl-who-can giúp việc này bớt đau đầu hơn đáng kể.

Nguyên Tắc Trong Thực Tế

Least privilege không phải một ô checkbox để đánh dấu. Đó là thói quen bạn xây dựng vào mỗi lần deploy.

Mỗi khi thêm workload mới, hãy hỏi: pod này thực sự cần gọi gì? Bắt đầu từ không có quyền gì. Chỉ thêm quyền khi có thứ gì đó hỏng và bạn có thể giải thích rõ lý do. Ví dụ monitoring ở trên còn đơn giản — ứng dụng thực tế sẽ phức tạp hơn. Operator quản lý CRD, pipeline deploy qua nhiều namespace, admission webhook đọc secret. Cách tiếp cận vẫn giữ nguyên dù bối cảnh có phức tạp đến đâu.

Cấu hình RBAC sai liên tục xuất hiện trong top những vấn đề bảo mật Kubernetes phổ biến nhất trên cluster production — không phải vì tooling khó, mà vì không ai dành thời gian làm đúng ngay từ đầu. Buổi post-mortem nhóm tôi phải ngồi qua hoàn toàn có thể tránh được. Của bạn cũng vậy.

Share: