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
defaulttrong 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, edit và admin. 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,updatehaydelete - 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 verbs —
resources: ["*"]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-lookup và kubectl-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.

