RBAC: Biến Danh Tính Thành Quyền

K
Kai··7 min read

Bài 51 kết thúc ở chặng authentication: API server gắn username + groups cho request, nhưng chưa cấp quyền gì. Chặng authorization quyết phần đó, và cụm ta chạy chế độ Node,RBAC (xem cờ --authorization-mode ở Bài 7). Bài này tập trung vào RBAC — cách biến một username hay group thành quyền cụ thể trên từng resource.

Bốn object, hai trục

RBAC có bốn loại object, chia theo hai trục: định nghĩa quyền và gắn quyền cho ai, mỗi trục lại có bản namespaced và bản cluster-scoped.

            ĐỊNH NGHĨA QUYỀN                GẮN QUYỀN CHO AI
          (làm được gì trên gì)          (user/group/SA nào)

namespaced   Role                          RoleBinding
             (quyền trong 1 namespace)     (gắn trong 1 namespace)

cluster      ClusterRole                   ClusterRoleBinding
             (resource cluster-scoped      (gắn toàn cluster)
              hoặc dùng lại nhiều ns)

Một quy tắc trong Role gồm ba phần: apiGroups (nhóm API, "" là core), resources (loại resource như pods, secrets), và verbs (get, list, watch, create, update, patch, delete). RBAC chỉ cộng dồn — không có quy tắc "cấm". Mặc định là từ chối tất; quyền chỉ đến từ các quy tắc cho phép. Hệ quả: muốn chặn ai đó, không thêm quyền chứ không viết luật cấm.

Một ServiceAccount chỉ đọc pod

Dựng một SA, một Role cho đọc pod, và một RoleBinding nối hai cái:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata: {namespace: rbac-demo, name: pod-reader}
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata: {namespace: rbac-demo, name: reader-binding}
subjects:
- kind: ServiceAccount
  name: reader
  namespace: rbac-demo
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

kubectl auth can-i kiểm tra quyền mà không phải thực sự gọi; --as để hỏi giúp một danh tính khác (cần quyền impersonate — admin có):

SA=system:serviceaccount:rbac-demo:reader
kubectl auth can-i list   pods    --as=$SA -n rbac-demo
kubectl auth can-i create pods    --as=$SA -n rbac-demo
kubectl auth can-i list   secrets --as=$SA -n rbac-demo
kubectl auth can-i list   pods    --as=$SA -n default     # namespace khác
yes      # list pods trong rbac-demo
no       # create pods — Role chỉ cho get/list/watch
no       # list secrets — Role không nhắc tới secrets
no       # list pods ở default — Role chỉ có hiệu lực trong rbac-demo

can-i chỉ là dự đoán; kiểm chứng bằng token thật của reader (cách lấy token ở Bài 51) để thấy API server thực sự chặn:

TOK=$(kubectl -n rbac-demo create token reader)
kubectl --kubeconfig=/dev/null --token="$TOK" --server=https://<apiserver> \
  --certificate-authority=ca.pem -n rbac-demo get pods
kubectl ... get secrets
kubectl ... run x --image=busybox
No resources found in rbac-demo namespace.        # list pods: được (chỉ là chưa có pod nào)
Error from server (Forbidden): secrets is forbidden: User "system:serviceaccount:rbac-demo:reader"
  cannot list resource "secrets" in API group "" in the namespace "rbac-demo"
Error from server (Forbidden): pods is forbidden: ... cannot create resource "pods" ...

Token enforcement khớp đúng can-i: list pod chạy (trả về rỗng vì namespace trống), list secret và tạo pod đều 403 Forbidden với thông báo nêu rõ username, verb, resource, namespace — bốn thứ RBAC dựa vào để quyết.

Authorizer quyết định thế nào

Vì sao can-i và token cho ra cùng kết quả? Cả hai hỏi cùng một bộ máy: chuỗi authorizer mà Bài 7 cấu hình qua --authorization-mode=Node,RBAC. Một request được cho qua nếu bất kỳ authorizer trong chuỗi (Node, rồi RBAC) chấp thuận; nếu tất cả đều "không có ý kiến" thì bị từ chối. Đó là lý do RBAC chỉ cộng dồn: nó không có khái niệm "từ chối", chỉ có "chấp thuận" hoặc "không ý kiến".

Bên trong, RBAC authorizer làm một việc đơn giản: với danh tính của request (user + groups + service account), nó quét mọi RoleBinding/ClusterRoleBinding trỏ tới danh tính đó, gom các quy tắc từ Role/ClusterRole được tham chiếu, và nếu một quy tắc khớp cả ba (apiGroup + resource + verb) thì chấp thuận ngay. Không quy tắc nào khớp thì "không ý kiến" → từ chối. Hỏi thẳng authorizer bằng SubjectAccessReview để thấy nó quyết và vì sao:

kubectl create -f - -o jsonpath='{.status.allowed} reason={.status.reason}' <<'EOF'
apiVersion: authorization.k8s.io/v1
kind: SubjectAccessReview
spec:
  user: system:serviceaccount:rbac-demo:reader
  resourceAttributes: {namespace: rbac-demo, verb: list, resource: pods}
EOF
true reason=RBAC: allowed by RoleBinding "reader-binding/rbac-demo" of Role "pod-reader" to ServiceAccount "reader/rbac-demo"

Authorizer trả true và nêu đích danh binding nào cho phép — đúng đường nó đi: từ danh tính → binding → role → quy tắc khớp. Hỏi về secrets (không quy tắc nào khớp):

false reason=

false với reason rỗng — vì không có quy tắc nào để dẫn ra, và RBAC không giải thích từ chối, nó chỉ im lặng không chấp thuận. (kubectl auth can-i bên trong cũng gửi một SelfSubjectAccessReview y như vậy; nó chỉ là lớp vỏ tiện cho cùng câu hỏi.) Hiểu cơ chế này thì hai tính chất "cộng dồn" và "mặc định từ chối" không còn là tiên đề học thuộc, mà là hệ quả trực tiếp của thuật toán quét-tìm-khớp.

RoleBinding trỏ vào ClusterRole

Không phải lúc nào cũng cần viết Role mới. Kubernetes có sẵn bốn ClusterRole: cluster-admin (toàn quyền), admin (toàn quyền trong một namespace), edit (đọc-ghi phần lớn resource), view (chỉ đọc). Một RoleBinding có thể trỏ vào ClusterRole thay vì Role — khi đó nó cấp quyền của ClusterRole đó nhưng chỉ trong namespace của RoleBinding:

kubectl -n rbac-demo create rolebinding view-binding \
  --clusterrole=view --serviceaccount=rbac-demo:viewer

Kiểm quyền của viewer:

SA=system:serviceaccount:rbac-demo:viewer
kubectl auth can-i get  pods     --as=$SA -n rbac-demo   # yes
kubectl auth can-i list services --as=$SA -n rbac-demo   # yes
kubectl auth can-i list secrets  --as=$SA -n rbac-demo   # no
kubectl auth can-i create pods   --as=$SA -n rbac-demo   # no
kubectl auth can-i get  pods     --as=$SA -n default     # no
yes      # đọc pod
yes      # đọc service
no       # KHÔNG đọc được secret — dù là "view"
no       # view chỉ đọc, không tạo
no       # RoleBinding giới hạn trong rbac-demo, không sang default

view cho đọc gần như mọi thứ nhưng cố tình bỏ secrets — vì đọc được secret là gần như đọc được mật khẩu, token, khóa. Kiểm lại bằng cách soi quy tắc của ClusterRole view:

kubectl get clusterrole view -o jsonpath='{range .rules[*]}{.resources}{"\n"}{end}' | grep secrets
(không in ra gì — view không có secrets)

Việc viewer không vượt khỏi rbac-demo cho thấy điểm hay nhầm: cùng một ClusterRole view, nhưng cách gắn quyết phạm vi. Gắn bằng RoleBinding thì quyền bó trong một namespace; gắn bằng ClusterRoleBinding thì có hiệu lực toàn cluster. ClusterRoleBinding cluster-admin ở Bài 51 là ví dụ sau — nó cấp toàn cụm cho group system:masters.

🧹 Dọn dẹp

kubectl delete namespace rbac-demo

Xóa namespace cuốn theo Role, RoleBinding và hai ServiceAccount. Các ClusterRole có sẵn (view, edit...) là object dựng sẵn của cụm, không đụng tới. Lệnh dùng trong bài ở github.com/nghiadaulau/kubernetes-from-scratch, thư mục 52-rbac.

Tổng kết

RBAC biến danh tính từ Bài 51 thành quyền cụ thể qua bốn object trên hai trục: Role/ClusterRole định nghĩa quyền (apiGroups + resources + verbs), RoleBinding/ClusterRoleBinding gắn quyền cho subject (User, Group, ServiceAccount). RBAC cộng dồn và mặc định từ chối, nên muốn hạn chế là bớt quyền chứ không viết luật cấm. Ta dựng một SA chỉ get/list/watch pod, xác nhận bằng cả kubectl auth can-i lẫn token thật rằng nó đọc pod được nhưng đọc secret hay tạo pod đều 403, và quyền không tràn sang namespace khác. RoleBinding có thể trỏ vào ClusterRole có sẵn (view) để cấp quyền gói trong một namespace; view cố tình loại secrets. Điểm cốt lõi: cùng một ClusterRole, gắn bằng RoleBinding thì bó trong namespace, gắn bằng ClusterRoleBinding thì toàn cluster.

Cả Bài 51 lẫn 52 đều xoay quanh ServiceAccount nhưng chưa mổ kỹ. Bài 53 đi vào chính nó: token được phát và xoay ra sao, pod nhận token tự động bằng cơ chế nào, và audience/expiry trong JWT ràng buộc token chặt đến đâu.

Related Posts