Skip to main content

7.2. Security - Authorization, RBAC and Service Accounts

Mục lục


1. Kubernetes API và API Groups

Phần trước đã bàn về authentication — cách xác minh danh tính của một người hoặc một máy đang gõ lệnh vào cluster. Trước khi sang authorization (người đã xác thực xong được phép làm gì), cần dừng lại một chút để hiểu cấu trúc của chính Kubernetes API, vì toàn bộ RBAC sắp học đều xoay quanh đúng 3 khái niệm nằm ngay trong cấu trúc đó: API group, resource, và verb.

Mọi thao tác đã làm từ đầu khóa học tới giờ, dù gõ qua kubectl hay gọi REST trực tiếp, thực chất đều là một request gửi tới kube-apiserver. kubectl chỉ là một client tiện lợi — nó dịch lệnh gõ tay thành HTTP request rồi gửi tới đúng cùng một API đó.

Thử gọi trực tiếp sẽ thấy rõ cấu trúc này. API server lắng nghe tại địa chỉ control plane node, port 6443. Dưới đường dẫn /version, nó trả về version của cluster. Dưới /api/v1/pods, nó trả về danh sách pod. Nhìn hai đường dẫn cạnh nhau là thấy ngay: một phần chỉ version, phần còn lại chỉ API group.

Resource được nhóm theo API group

Toàn bộ resource trong Kubernetes được nhóm vào các API group khác nhau theo mục đích — có group cho version, group cho health/metrics, group cho logs (để tích hợp với hệ thống logging bên thứ ba). Nhưng nhóm quan trọng nhất, chịu trách nhiệm cho phần lớn chức năng thật của cluster, chia làm 2 loại:

  • Core group (thường ghi là v1, không có tên riêng): nơi chứa các resource nền tảng nhất — namespaces, pods, replication controllers, events, endpoints, nodes, bindings, persistent volumes, persistent volume claims, configmaps, secrets, services.
  • Named group: có tổ chức hơn, và mọi tính năng mới của Kubernetes về sau đều được thêm vào dưới dạng named group thay vì nhét chung vào core. Ví dụ, nhóm apps chứa Deployments, ReplicaSets, StatefulSets; nhóm networking chứa NetworkPolicies; nhóm certificates chứa CertificateSigningRequests (đã gặp ở phần trước, lúc ký certificate cho user mới).

Dưới mỗi resource là một tập hành động được phép gọi là verb — về bản chất chỉ là tên gọi khác của các HTTP method quen thuộc: list/get/watch ứng với GET, create ứng với POST, update/patch ứng với PUT/PATCH, delete ứng với DELETE. Một Deployment, chẳng hạn, hỗ trợ verb list (liệt kê tất cả), get (xem chi tiết một cái cụ thể), create, update, delete, và watch (theo dõi thay đổi theo thời gian thực thay vì phải hỏi lại liên tục).

Cách nhanh nhất để tra API group của một resource là trang Kubernetes API reference — chọn đúng object, phần đầu trang luôn ghi rõ group của nó (v1 nghĩa là core). Cũng có thể tra ngay trên cluster: gọi tới kube-apiserver ở port 6443 không kèm path nào, nó liệt kê toàn bộ API group hiện có, kèm theo mọi resource thuộc từng named group.

Vì sao gọi thẳng bằng curl lại bị từ chối? Vì chưa cung cấp bất kỳ cơ chế authentication nào — API server từ chối mọi request, trừ một vài path công khai như /version. Muốn gọi thành công, phải tự truyền certificate vào từng lệnh curl, hoặc dùng cách gọn hơn nhiều: chạy kubectl proxy.

kubectl proxy khởi động một proxy service local ở port 8001, tự lấy credentials và certificate từ kubeconfig hiện tại để xác thực hộ — nhờ vậy các lệnh curl gọi vào localhost:8001 không cần kèm certificate nào nữa, proxy lo phần xác thực rồi mới forward request thật tới kube-apiserver.

Đừng nhầm kube-proxy với kubectl proxy. Tên gần giống nhau nhưng là hai thứ khác hẳn nhau về việc: kube-proxy (đã bàn ở phần Core Concepts) là thành phần chạy trên mọi node, lo việc route traffic giữa Pod và Service; còn kubectl proxy chỉ là một HTTP proxy tạm thời do chính utility kubectl dựng lên để tiện gọi API server từ máy local, không liên quan gì tới networking giữa các Pod.

Tóm lại: mỗi resource trong Kubernetes nằm trong core group hoặc named group, và có một tập verb riêng đi kèm. Authorization — phần sắp bàn tới — chính là cơ chế quyết định user nào được gọi verb nào trên resource nào.


2. Tổng quan về Authorization trong Kubernetes

Authentication trả lời câu hỏi "đây là ai". Authorization trả lời câu hỏi tiếp theo, quan trọng không kém: "người này được phép làm gì". Hai việc này tách bạch hoàn toàn trong Kubernetes — xác thực thành công không có nghĩa là muốn làm gì cũng được.

Vì sao cluster cần authorization? Một mình admin thì không cần, vì admin vốn đã có toàn quyền. Vấn đề nảy sinh ngay khi cluster có thêm người dùng khác: developer chỉ nên deploy ứng dụng, không nên có quyền sửa cấu hình network hay thêm/xóa node; một service account của Jenkins chỉ nên đủ quyền deploy đúng ứng dụng nó phụ trách, không nên đọc được secret của namespace khác; một team dùng chung cluster qua namespace chỉ nên thấy đúng namespace của mình. Không có authorization, mọi danh tính xác thực thành công đều mặc nhiên có toàn quyền như admin — rủi ro rất lớn một khi lên production.

Kubernetes hỗ trợ 4 cơ chế authorization: Node Authorizer, ABAC, RBAC, và Webhook — cộng thêm 2 chế độ đặc biệt AlwaysAllow/AlwaysDeny.

Node Authorizer — chỉ dành cho kubelet

kube-apiserver không chỉ nhận request từ người dùng, nó còn nhận request nội bộ từ kubelet trên mọi node: kubelet cần đọc thông tin service/endpoint để cấu hình network, đọc thông tin pod được gán cho node của nó, và report ngược lại status của chính node đó. Các request loại này được xử lý bởi Node Authorizer. Ở phần trước, lúc cấp certificate cho kubelet, đã quy ước mỗi kubelet phải có Common Name dạng system:node:<tên-node> và thuộc group system:nodes — đây chính là điều kiện Node Authorizer dùng để nhận diện. Bất kỳ request nào đến từ danh tính khớp đúng pattern đó đều được cấp đúng quyền cần thiết cho một kubelet, không hơn không kém.

ABAC — gán quyền theo attribute, khó bảo trì

Attribute-Based Access Control gán một tập permission trực tiếp cho một user hoặc group, viết dưới dạng policy file định dạng JSON rồi nạp vào API server lúc khởi động. Ví dụ, có thể viết một dòng policy cho phép user dev view/create/delete pod. Vấn đề nằm ở khâu vận hành: mỗi lần cần thêm hay sửa quyền, phải sửa tay file JSON đó rồi restart kube-apiserver để áp dụng — không có cách nào update động. Trên một cluster có hàng chục user, việc này nhanh chóng trở thành gánh nặng thật sự, và đó là lý do ABAC ngày nay ít được chọn làm cơ chế chính.

RBAC — gán quyền qua vai trò, dễ bảo trì hơn nhiều

Role-Based Access Control giải quyết đúng điểm yếu đó bằng một lớp gián tiếp: thay vì gán permission thẳng cho từng user, định nghĩa một Role đại diện cho một vai trò (ví dụ "developer"), gán permission cho Role đó, rồi bind nhiều user vào cùng một Role. Về sau, cần đổi quyền cho toàn bộ developer chỉ cần sửa đúng 1 Role — thay đổi có hiệu lực ngay với mọi user đang bind vào nó, không phải sửa từng dòng JSON và restart apiserver như ABAC. RBAC là cơ chế phổ biến nhất trong thực tế, và cũng là trọng tâm của phần 3 và phần 5 sắp tới.

Webhook — outsource quyết định ra ngoài Kubernetes

Nếu muốn toàn bộ logic authorization nằm ở một hệ thống bên ngoài Kubernetes, dùng chế độ Webhook: kube-apiserver đóng gói thông tin request (ai, verb gì, resource nào) thành một object gọi là SubjectAccessReview, gửi qua HTTP POST tới một service ngoài, rồi dùng đúng câu trả lời allowed: true/false từ service đó để quyết định. Open Policy Agent (OPA) là ví dụ hay được nhắc tới nhất cho việc này.

Cần phân biệt rõ một điểm dễ nhầm: OPA ngày nay được biết đến nhiều hơn ở vai trò admission controller (qua OPA Gatekeeper, kiểm tra manifest có hợp policy hay không trước khi nó được ghi vào etcd). Webhook authorization mode ở đây là một cơ chế khác, chạy trước cả bước admission đó, và chỉ trả lời đúng một câu hỏi: request này có được phép thực hiện hay không.

AlwaysAllow, AlwaysDeny, và cách nhiều mode kết hợp thành một chain

Ngoài 4 cơ chế trên còn 2 chế độ đặc biệt: AlwaysAllow cho qua mọi request mà không kiểm tra gì, AlwaysDeny từ chối tất cả. Toàn bộ các mode này được cấu hình bằng flag --authorization-mode trên kube-apiserver, và nếu không set flag này, giá trị mặc định là AlwaysAllow — nghĩa là một cluster quên cấu hình flag này coi như không hề có authorization, ai xác thực được cũng có toàn quyền ngay lập tức. Đây là lý do các bộ hardening checklist như CIS Benchmark luôn liệt kê việc không được để --authorization-modeAlwaysAllow như một mục bắt buộc phải kiểm tra trên production.

Có thể truyền nhiều mode cùng lúc, phân tách bằng dấu phẩy, ví dụ --authorization-mode=Node,RBAC,Webhook. Khi đó các mode tạo thành một chain, và cần hiểu đúng cơ chế xử lý: mỗi module trong chain trả về 1 trong 3 kết quả — Allow, Deny, hoặc No opinion (không thuộc phạm vi nó phụ trách nên không có ý kiến gì). Ngay khi một module trả Allow hoặc Deny rõ ràng, chain dừng lại lập tức và trả kết quả đó — không module nào phía sau còn được hỏi ý kiến nữa. Chỉ khi một module trả No opinion, request mới được chuyển tiếp cho module kế tiếp trong chain. Nếu đi hết toàn bộ chain mà không module nào đưa ra quyết định rõ ràng, kết quả mặc định là Deny (HTTP 403 Forbidden).

Ví dụ với Node,RBAC,Webhook: một request tạo Deployment tới từ dev-user (không phải kubelet) — Node Authorizer thấy ngay đây không phải request của node, trả No opinion, chuyển cho RBAC. RBAC kiểm tra Role/RoleBinding của dev-user, tìm thấy quyền phù hợp, trả Allow — chain dừng ngay tại đây, Webhook không bao giờ được gọi tới trong trường hợp này.

Chuỗi Authorization: Node → RBAC → WebhookRequest tới API serverNode Authorizercheck request kubelet(group system:nodes)RBACáp Role/ClusterRoleđã bind cho userWebhookgọi service ngoài(SubjectAccessReview)No opinionNo opinionAllow hoặc Deny rõ ràng, ở bất kỳ bước nàodừng ngay, trả kết quả về clientCả 3 No opinionDeny (403)Node Authorizer luôn chạy trước, nhưng chỉ "có ý kiến" với request từ kubelet.Với user thường, nó im lặng nhường lượt cho RBAC ngay lập tức.

💡 Hình dung: Chain authorization giống 3 lớp bảo vệ ở cổng ra vào một tòa nhà. Bảo vệ lớp 1 chỉ kiểm tra thẻ nhân viên tòa nhà (Node Authorizer) — khách không có thẻ này, anh ta không từ chối thẳng mà chỉ lắc đầu "không phải việc của tôi" rồi đẩy khách sang bảo vệ lớp 2. Bảo vệ lớp 2 (RBAC) tra đúng danh sách khách mời — thấy tên, cho qua ngay, lớp 3 thậm chí không cần hỏi tới. Chỉ khi cả 3 lớp đều lắc đầu "không biết", khách mới bị mời quay ra.

Đã nắm được framework chung của authorization. Phần tiếp theo đi sâu vào cơ chế được dùng phổ biến nhất trong thực tế: RBAC, bắt đầu từ 2 object cốt lõi của nó — Role và RoleBinding.


3. RBAC: Roles và RoleBindings

Tạo Role

Làm sao tạo một Role? Bằng cách tạo một object Role, apiVersion set là rbac.authorization.k8s.io/v1 — API version ổn định duy nhất hiện nay, bản v1beta1 cũ đã bị loại bỏ hoàn toàn từ Kubernetes 1.22 và không còn được apiserver phục vụ nữa. kind set là Role, và đặt tên Role là developer.

Phần quan trọng nhất nằm trong rules. Mỗi rule có đúng 3 phần: apiGroups, resources, và verbs — 3 khái niệm đã bàn ở phần 1. Với core group, để trống apiGroups. Với các group khác, ghi tên group vào. Resource muốn cấp cho developer là pods, verb tương ứng là list, get, create, delete. Muốn cho developer tạo thêm configmaps, thêm một rule khác — một Role hoàn toàn có thể chứa nhiều rule như vậy:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: developer
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["list", "get", "create", "delete"]
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["create"]

Tạo Role bằng lệnh kubectl create role (hoặc kubectl create -f, với file như trên).

Gắn Role vào user bằng RoleBinding

Có Role rồi vẫn chưa đủ — cần liên kết nó với một user cụ thể, và đó là việc của RoleBinding. Đặt tên nó là devuser-developer-binding, kindRoleBinding. RoleBinding có 2 phần: subjects chỉ định user (hoặc group, hoặc ServiceAccount) nào được gắn, và roleRef trỏ tới Role vừa tạo. Tạo bằng lệnh kubectl create.

Một điểm quan trọng: Role và RoleBinding đều là resource namespaced, nghĩa là chúng luôn thuộc về một namespace cụ thể. Trong ví dụ trên, nếu không chỉ định namespace, dev user chỉ được cấp quyền trên pods và configmaps trong namespace default. Muốn giới hạn ở namespace khác, khai báo namespace ngay trong metadata của cả Role lẫn RoleBinding.

Xem, kiểm tra Role/RoleBinding

Để xem các Role đã tạo, chạy kubectl get roles. Để liệt kê RoleBinding, chạy kubectl get rolebindings. Muốn xem chi tiết một Role, chạy kubectl describe role developer — sẽ thấy đủ resource và permission tương ứng. Tương tự, kubectl describe rolebinding devuser-binding cho thấy chi tiết binding hiện có.

Kiểm tra quyền bằng kubectl auth can-i

Muốn biết một user có quyền truy cập resource cụ thể hay không, dùng kubectl auth can-i, ví dụ hỏi có được create deployments hay delete nodes hay không. Nếu là administrator, còn có thể impersonate user khác để kiểm tra hộ quyền của họ bằng option --as, thay vì phải tự authenticate với danh tính của họ mới test được. Ví dụ, vì chưa cấp quyền tạo deployments cho developer, lệnh sẽ trả về "no"; nhưng dev user có quyền tạo pod. Cũng có thể chỉ định namespace trong lệnh — dev user không có quyền tạo pod trong namespace test nếu Role chỉ áp dụng cho default.

Giới hạn theo resourceNames

Có thể đi sâu hơn một mức, cấp quyền không phải trên toàn bộ resource mà chỉ trên vài instance cụ thể. Ví dụ, nếu namespace có 5 pod nhưng chỉ muốn cấp access tới đúng pod blueorange, thêm field resourceNames vào rule để giới hạn.

Đã đủ nền tảng lý thuyết — phần lab tiếp theo áp dụng đúng những gì vừa học vào một cluster thật, kèm vài tình huống debug hay gặp khi RBAC "không hoạt động như mong đợi".


4. Lab: Thực hành RBAC

Xác định authorization mode đang cấu hình trên cluster. Xem file /etc/kubernetes/manifests/kube-apiserver.yaml, tìm option --authorization-mode — thấy giá trị Node,RBAC. Cách khác là xem trực tiếp process trên control plane (vì mọi static pod control plane thực chất chạy như process trên node):

ps -aux | grep authorization-mode

Cả hai cách đều cho cùng kết quả: authorization-mode đang set là Node,RBAC.

Có bao nhiêu Role tồn tại trong namespace default? Chạy kubectl get roles — namespace default không có Role nào.

Có bao nhiêu Role tồn tại trên tất cả namespace? Thêm -A để lấy mọi namespace, --no-headers để bỏ dòng header, rồi đếm dòng bằng wc -l:

kubectl get roles -A --no-headers | wc -l

Kết quả là 12.

Role kube-proxy trong namespace kube-system được cấp access tới resource nào, với verb nào? Chạy kubectl describe role kube-proxy -n kube-system — resource duy nhất là configmaps, verb duy nhất được phép là get.

Trong các phát biểu sau, phát biểu nào đúng?

  • "Role kube-proxy có thể get chi tiết một ConfigMap tên kube-proxy." — đúng, vì Role này có quyền get trên configmap với resourceNames: [kube-proxy].
  • "Role kube-proxy có thể delete configmap." — sai, Role không có verb delete.
  • "Role kube-proxy chỉ có thể view và update configmap theo tên." — sai, Role này không có verb update.

Vậy điểm cần nhớ: Role kube-proxy chỉ có thể get đúng 1 ConfigMap tên kube-proxy, không hơn.

Role kube-proxy được gán cho account nào? Xem RoleBinding trong kube-system bằng kubectl get rolebindings -n kube-system — Role nằm trong RoleBinding cùng tên kube-proxy. Xem chi tiết bằng kubectl describe rolebindings kube-proxy -n kube-system — group được gán là system:bootstrappers:kubeadm:default-node-token.

Một user dev-user đã được tạo, thông tin đã có sẵn trong kubeconfig. Xác nhận bằng kubectl config view — thấy user dev-user.

User này có thể list pods trong namespace default không? Chạy kubectl get pods --as dev-user — kết quả bị forbidden: user dev-user cannot list resource "pods" in API group "" in the default namespace. Chưa có permission nào.

Tạo Role và RoleBinding để dev-user có thể create, list, delete pods trong namespace default. Tạo Role trước — cú pháp kubectl create role gồm tên, --verb (nhận comma-separated list), và --resource:

kubectl create role developer-role --verb=list,create,delete --resource=pods

Role developer-role được tạo với đúng 3 verb trên pods. Tiếp theo tạo RoleBinding và bind vào:

kubectl create rolebinding dev-user-binding --role=developer-role --user=dev-user

Kiểm tra lại dev-user-bindingdev-user đã được associate đúng với developer-role.

dev-user gặp lỗi khi lấy chi tiết pod dark-blue-app trong namespace blue — điều tra và fix. Chạy kubectl get pod dark-blue-app -n blue --as dev-user — báo lỗi forbidden: User dev-user cannot get resource pods in the API group in the namespace blue. Đề bài xác nhận Role/RoleBinding cần thiết đã có sẵn, nên vấn đề không phải là thiếu quyền hoàn toàn. Kiểm tra namespace blue, thấy Role developer và RoleBinding dev-user-binding đều tồn tại. Xem chi tiết bằng kubectl describe role developer -n blue — resource là pods, resourceNamesblue-app, verb gồm get, watch, create, delete. Nhưng pod thật tên là dark-blue-app, không phải blue-app — đúng là nguyên nhân. Sửa lại resourceNames của Role developer trong namespace blue thành dark-blue-app, chạy lại lệnh get — pod ở trạng thái Running, đã fix xong.

Cấp quyền cho dev-user tạo Deployment trong namespace blue. Thử tạo Deployment nginx (image nginx) bằng dev-user — báo lỗi user dev-user cannot create resource deployments in API group apps in the blue namespace. Sửa Role developer trong namespace blue: giữ nguyên rule cũ (get, watch, create, delete trên pod dark-blue-app), thêm một rule mới với apiGroups: ["apps"], resources: ["deployments"], verb get, watch, create, deletekhông khai resourceNames lần này, vì muốn tạo deployment với bất kỳ tên nào. Verify lại Role, thấy đã có rule cho deployments.apps. Thử tạo lại — thành công.

Vậy là đã dùng thành thạo Role và RoleBinding cho resource namespaced. Nhưng nodes, persistent volumes, hay chính RoleBinding lại không thuộc namespace nào cả — làm sao cấp quyền cho những resource kiểu đó? Đó là lúc cần tới ClusterRole và ClusterRoleBinding.


5. Cluster Roles và Cluster Role Bindings

Role và RoleBinding, như vừa thấy, đều là resource namespaced — nếu không chỉ định namespace, chúng được tạo trong default và chỉ kiểm soát access bên trong đúng namespace đó.

Nhưng không phải resource nào cũng gắn được với một namespace. Có thể nói "node01 thuộc namespace dev" không? Không — node là một resource ở cấp toàn cluster, không thể cô lập vào riêng một namespace nào. Những resource kiểu này gọi là cluster-scoped, đối lập với resource namespaced như pod, Deployment, Service, Secret đã quen thuộc từ trước.

Ngoài nodespersistentvolumes, chính ClusterRoleClusterRoleBinding (sắp học trong phần này) cũng là resource cluster-scoped, cùng với CertificateSigningRequests (đã gặp ở phần trước) và bản thân object Namespace. Đây không phải danh sách đầy đủ — muốn xem chính xác resource nào namespaced, resource nào không, chạy:

kubectl api-resources --namespaced=false

💡 Hình dung: Role giống chiếc thẻ từ chỉ mở được các phòng trên đúng 1 tầng của tòa nhà — sang tầng khác, thẻ vô dụng. ClusterRole giống chiếc chìa khóa tổng của cả tòa: nếu áp cho resource cấp-tòa-nhà (thang máy, phòng kỹ thuật — tương đương Node, PersistentVolume), nó chỉ có đúng 1 bản cho toàn bộ tòa nhà. Nhưng nếu ai đó lỡ dùng chìa khóa tổng để mở phòng-trên-từng-tầng (Pod, Deployment — vốn là resource namespaced), nó sẽ mở được đúng phòng đó ở mọi tầng cùng lúc, không giới hạn riêng tầng nào.

Phần trước đã thấy cách cấp quyền cho resource namespaced bằng Role/RoleBinding. Với resource cluster-scoped, dùng ClusterRoleClusterRoleBinding — về cấu trúc gần như giống hệt Role/RoleBinding, chỉ khác kind và phạm vi áp dụng. Ví dụ, có thể tạo một ClusterRole cho phép cluster administrator view/create/delete node, hoặc một ClusterRole khác cho phép storage administrator tạo persistent volumes và persistent volume claims.

Namespaced (Role) vs Cluster-scoped (ClusterRole)Role + RoleBinding (namespaced)Namespace: devRole: developerget, list, create podsRoleBindinguser: dev-userChỉ có hiệu lực bên trong namespace dev.Namespace khác (vd prod) không hề bị ảnh hưởng.VSClusterRole + ClusterRoleBindingClusterRole: storage-adminget, list, create pv/scClusterRoleBindinguser: michelleNodePersistentVolumeKhông bị giới hạn bởi namespace nào.Cùng 1 binding áp dụng cho toàn cluster.

Một điều dễ gây nhầm lẫn cần lưu ý: ClusterRole/ClusterRoleBinding thường dùng cho resource cluster-scoped, nhưng đó không phải quy tắc bắt buộc. Hoàn toàn có thể tạo ClusterRole cho resource namespaced như pods — khi đó, user sẽ có quyền trên pods ở mọi namespace cùng lúc, đúng như minh họa "chìa khóa tổng" ở trên. Đây là cách chuẩn để cấp quyền "xem toàn cluster" cho một monitoring tool, chẳng hạn.

Kubernetes cũng đã tạo sẵn một số ClusterRole mặc định ngay khi cluster được thiết lập — phần lab tiếp theo sẽ khám phá chúng, đồng thời tự tạo thêm ClusterRole mới cho vài tình huống cụ thể.


6. Lab: Thực hành Cluster Roles

Có bao nhiêu ClusterRole được định nghĩa trong cluster?

kubectl get clusterroles --no-headers | wc -l

Kết quả là 69 — bao gồm cluster-admin, một loạt system ClusterRole, và vài cái do người dùng tự thêm.

Có bao nhiêu ClusterRoleBinding tồn tại trên hệ thống? Thực hiện tương tự với kubectl get clusterrolebindings.

ClusterRole cluster-admin thuộc namespace nào? Chạy kubectl get clusterroles — đúng như đã biết, ClusterRole và ClusterRoleBinding không thuộc về bất kỳ namespace nào, đó chính là đáp án.

User hoặc group nào được bind với cluster-admin? ClusterRoleBinding cho role này trùng tên. Xem kubectl describe clusterrolebinding cluster-admin — nó được associate với group system:masters. Một vài binding khác trong cluster cũng gán quyền này cho service account cụ thể.

Mức permission mà cluster-admin cấp là gì? Chạy kubectl describe clusterrole cluster-admin — resource là *, verb cũng là *, nghĩa là mọi action trên mọi resource trong cluster. Đây là ClusterRole rộng nhất có thể có, nên chỉ nên bind cho đúng những identity thật sự cần toàn quyền.

Michelle vừa gia nhập team, phụ trách nodes trong cluster — tạo ClusterRole và ClusterRoleBinding cần thiết. Kiểm tra trước: kubectl get nodes --as michelle — báo lỗi nodes forbidden: user Michelle cannot list resource nodes in the API group. Đề bài không yêu cầu tên cụ thể, nên có thể tự đặt miễn lệnh chạy đúng. Tạo ClusterRole:

kubectl create clusterrole michelle-role --verb=get,list,watch --resource=nodes

Tạo ClusterRoleBinding tương ứng:

kubectl create clusterrolebinding michelle-binding --clusterrole=michelle-role --user=michelle

Kiểm tra lại: kubectl describe clusterrole michelle-role cho thấy quyền get/list/watch trên nodes; ClusterRoleBinding cho thấy michelle-role đã associate đúng với user michelle. Chạy lại kubectl get nodes --as michelle — thành công.

Michelle được giao thêm việc quản lý storage — cấp thêm quyền, lấy đúng tên API group/resource từ kubectl api-resources. Lệnh kubectl api-resources liệt kê mọi resource cùng short name (ví dụ deploy cho deployments, rs cho replicasets), API version của từng resource, và cột cho biết resource đó namespaced hay không — rất hữu ích khi không nhớ chính xác tên hoặc apiVersion cần dùng.

kubectl create clusterrole storage-admin --resource=persistentvolumes,storageclasses --verb=list,create,get,watch

Kiểm tra bằng kubectl describe clusterrole storage-admin, hoặc xem YAML đầy đủ bằng kubectl get clusterrole storage-admin -o yaml — ClusterRole có 2 rule, một cho persistentvolumes, một cho storageclasses.

kubectl create clusterrolebinding storage-admin --clusterrole=storage-admin --user=michelle

Kiểm tra lại — storage-admin đã associate với michelle. Thử kubectl get storageclasses --as michelle — hoạt động đúng như kỳ vọng.

Đã xử lý xong 2 loại resource: namespaced (Role/RoleBinding) và cluster-scoped (ClusterRole/ClusterRoleBinding). Phần cuối cùng chuyển sang một khái niệm khác hẳn — không phải "ai được làm gì" mà là "máy móc chứng minh danh tính của nó bằng cách nào": Service Account.


7. Service Accounts trong Kubernetes

Kubernetes có 2 loại account: user account, dùng bởi con người (admin, developer), và service account, dùng bởi máy móc. Một monitoring tool như Prometheus dùng service account để poll Kubernetes API lấy metrics; một CI/CD tool như Jenkins dùng service account để tự động deploy ứng dụng lên cluster.

Lấy ví dụ cụ thể: một Kubernetes Dashboard application đơn giản tên my-kubernetes-dashboard — ứng dụng Python nhỏ, khi chạy chỉ làm đúng 1 việc là gọi Kubernetes API lấy danh sách Pod rồi hiển thị lên web. Để gọi được API, ứng dụng phải tự chứng minh danh tính — và đó chính là việc của service account. Service account là identity của một service, gắn liền với một token: chuỗi ký tự dùng để authenticate, được gửi kèm request dưới dạng Bearer token trong HTTP header Authorization.

💡 Hình dung: Nếu service account là một ID card được cấp riêng cho một service, thì token chính là mã vạch (barcode) in trên tấm thẻ đó — mã vạch này được các cổng kiểm soát (ở đây là kube-apiserver) quét để xác minh danh tính trước khi cho qua. Token này cũng chính là thứ sẽ được dán vào ô đăng nhập của Kubernetes Dashboard nói trên.

Service account mặc định và cách nó gắn vào Pod

Mặc định, mọi cluster mới thiết lập đều tự tạo một service account tên default trong mọi namespace. Xem danh sách bằng kubectl get serviceaccount, xem chi tiết bằng kubectl describe serviceaccount. Mỗi khi một Pod được tạo mà không chỉ định service account nào khác, default tự động được gắn vào — thấy rõ trong field Service Account khi chạy kubectl describe pod.

Service account được đưa vào Pod dưới dạng một projected volume.

💡 Hình dung: Có thể hình dung projected volume này như một thư mục "ảo" được Kubernetes tự động tạo và mount sẵn bên trong Pod, tại đường dẫn /var/run/secrets/kubernetes.io/serviceaccount. Liệt kê thư mục đó sẽ thấy token nằm dưới dạng một file — mở file ra là thấy đúng chuỗi token.

Service account default đi kèm rất nhiều hạn chế (gần như không có quyền gì ngoài việc tự xác thực), nên khi cần quyền cụ thể, phải tạo service account riêng. Tạo bằng lệnh, theo sau bởi tên:

kubectl create serviceaccount dashboard-sa

Hoặc theo cách declarative với file YAML:

apiVersion: v1
kind: ServiceAccount
metadata:
name: dashboard-sa

Xem lại bằng kubectl get serviceaccounts, xem chi tiết bằng kubectl describe. Một service account mới tạo gần như "trống" — chưa gắn quyền gì, cần RBAC (Role/RoleBinding hoặc ClusterRole/ClusterRoleBinding đã học ở 2 phần trước) để cấp quyền cho nó, y hệt cách cấp quyền cho một user account.

Để gắn service account vào Pod, khai field serviceAccountName trong Pod spec. Describe lại Pod sẽ thấy service account mới xuất hiện đúng vị trí, và thư mục mount cũng chứa token tương ứng.

Vòng đời token: ngắn hạn, tự rotate, gắn với Pod

Đây là phần hay bị hiểu nhầm nhất nếu chỉ đọc tài liệu cũ: từ Kubernetes 1.22 trở đi (và là hành vi mặc định ổn định từ 1.24), token của service account không còn là một chuỗi tĩnh sống mãi mãi lưu trong một Secret. Thay vào đó, kubelet gọi một API nội bộ gọi là TokenRequest API để xin một token ngắn hạn (mặc định hết hạn sau khoảng 1 giờ), gắn chặt với đúng Pod đang yêu cầu nó. Token này được tự động rotate định kỳ trước khi hết hạn, và quan trọng nhất: nó hết hiệu lực ngay khi Pod bị xóa, không cần chờ tới thời điểm ghi trong token.

Vòng đời Token của ServiceAccount trong PodPod được tạo, gán ServiceAccount(qua field spec.serviceAccountName)kubelet gọi TokenRequest APIxin token ngắn hạn, gắn đúng với Pod nàyToken mount vào Pod dạng projected volumetại /var/run/secrets/kubernetes.io/serviceaccount/tokenContainer đọc token, gửi làm Bearer tokentrong header Authorization khi gọi kube-apiserverkubelet tự động rotate token định kỳtrước khi hết hạn — Pod không cần khởi động lạilặp lạiPod bị xóa → token invalid ngay lập tứckhông thể dùng lại, kể cả khi chưa tới hạn hết hiệu lựcToàn bộ vòng đời này diễn ra tự động, không cần thao tác thủ công,trừ khi cần tạo token dùng bên ngoài cluster bằng lệnh kubectl create token.

Nếu không muốn token được tự động tạo và mount cho một service account, set field automountServiceAccountToken: false — khai ở mức ServiceAccount thì áp dụng cho mọi Pod dùng nó, khai ở mức Pod thì chỉ áp dụng riêng cho Pod đó (và override giá trị ở mức ServiceAccount nếu có xung đột).

Dùng service account từ bên ngoài cluster

Cơ chế mount tự động ở trên chỉ hoạt động với Pod chạy bên trong cluster. Nhưng đôi khi cần một token để dùng bên ngoài — dán vào dashboard đã tạo ở ví dụ đầu bài, hoặc cấu hình cho một CI/CD tool, một external monitoring tool. Trường hợp đó, tạo token thủ công bằng:

kubectl create token dashboard-sa

Lệnh này in token ra màn hình và không lưu nó vào bất kỳ Secret nào. Mặc định, token có hiệu lực 1 giờ; muốn kéo dài, thêm flag --duration, ví dụ --duration=24h (thời hạn tối đa vẫn bị giới hạn bởi cấu hình --service-account-max-token-expiration trên kube-apiserver, nếu cluster có set). Decode token bằng jqbase64 sẽ thấy rõ thời điểm hết hạn, tên service account, và các claim khác đi kèm. Token này dùng làm Bearer token cho mọi request curl hay dán trực tiếp vào ô đăng nhập của ứng dụng dashboard.

Tóm lại: service account là identity dành cho máy móc, gắn với token để chứng minh danh tính. Tạo mới bằng kubectl create serviceaccount <tên>. Với ứng dụng chạy trong Pod, gắn qua serviceAccountName, Kubernetes tự lo phần còn lại — tạo token, mount, rotate, và expire đúng lúc. Với ứng dụng bên ngoài cluster, tự tạo token bằng kubectl create token rồi dùng như Bearer token.

Phần lab cuối cùng của bài này áp dụng toàn bộ chuỗi khái niệm trên vào một tình huống thực tế: một dashboard application bị lỗi vì thiếu quyền, và cần fix đúng bằng service account.


8. Lab: Thực hành Service Accounts

Có bao nhiêu service account tồn tại trong namespace default? Chạy kubectl get sa — có tổng cộng 2: defaultdev.

Secret token nào đang được dùng bởi service account default? Chạy kubectl describe cho service account đó, xem mục Tokens — đang set là None, đúng như hành vi mặc định hiện nay (không còn Secret token dài hạn tự sinh cho mỗi ServiceAccount).

Kiểm tra deployment dashboard vừa được deploy — image nào đang dùng? kubectl get deployments, rồi kubectl describe deployment web-dashboard, xem xuống Pod Template → Containers — tên container là web-dashboard.

Dashboard có load được Pod details không? Truy cập ứng dụng dashboard — thấy dòng lỗi màu đỏ: pods is forbidden, system:serviceaccount:default:default cannot list resource pods in the API group. Dashboard load thất bại.

Dashboard đang dùng loại account nào để gọi API? Từ chính message lỗi trên (system:serviceaccount:default:default) đã đủ xác định: nó đang dùng ServiceAccount default.

Inspect Pod của dashboard, xác định ServiceAccount đang mount trên đó. kubectl get pod, rồi kubectl describe pod <tên-pod> — field serviceAccount ở đầu output đang set là default.

Credential của service account này nằm ở đâu bên trong Pod? Xem mục mount trong output describe — mount tại /var/run/secrets/kubernetes.io/serviceaccount, đúng như đã bàn ở phần lý thuyết.

Tạo service account mới dashboard-sa với đúng quyền cho dashboard.

kubectl create serviceaccount dashboard-sa

Quyền cần thiết cho dashboard-sa đã được cấu hình sẵn qua RBAC trong môi trường lab này (đường dẫn /var/rbac nếu muốn xem lại cấu hình Role/RoleBinding cụ thể — đây là quy ước riêng của môi trường lab, không phải một path chuẩn của Kubernetes).

Lấy access token và nhập vào UI dashboard.

kubectl create token dashboard-sa

Copy token, dán vào ứng dụng dashboard, load lại — toàn bộ Pod trên cluster hiển thị thành công.

Sửa Deployment để dùng thẳng service account, thay vì phải dán token thủ công mỗi lần. Lấy deployment hiện có ra file YAML:

kubectl get deployments web-dashboard -o yaml > dashboard.yaml

Mở dashboard.yaml, tìm đúng phần spec của Pod template — không phải spec ngoài cùng của Deployment, mà là spec nằm dưới spec.template (Deployment có 2 tầng spec lồng nhau). Thêm field serviceAccountName: dashboard-sa vào đó, lưu lại rồi apply:

kubectl apply -f dashboard.yaml

(Có thể thấy một warning xuất hiện — không đáng lo, chỉ vì deployment ban đầu không được tạo bằng apply nên chưa có annotation last-applied-configuration.) Kiểm tra bằng kubectl get deployments, xác nhận rollout thành công. Quay lại dashboard, refresh — không cần nhập token thủ công nữa, ứng dụng vẫn hoạt động vì token của dashboard-sa giờ đã được tự động mount ngay từ khi Pod khởi động.

Vậy là đã đi hết authorization, RBAC, và service account — 3 mảnh ghép cuối cùng để trả lời trọn vẹn câu hỏi "ai, xác thực bằng cách nào, và được phép làm gì" trong một Kubernetes cluster. Phần tiếp theo chuyển sang khía cạnh bảo mật ở tầng thấp hơn: image security, Security Context, Network Policies, và Custom Resources, trong Part 7.3.


Nguồn tham khảo

Nguồn gốc: Khóa "Certified Kubernetes Administrator (CKA)" — phần "Security", nối tiếp Part 7.1 (Authentication và TLS Certificates) (API Groups; Authorization — tổng quan các cơ chế Node Authorizer, ABAC, RBAC, Webhook; RBAC — Roles và RoleBindings; Lab thực hành RBAC; Cluster Roles và Cluster Role Bindings; Lab thực hành Cluster Roles; Service Accounts; Lab thực hành Service Accounts), nền tảng KodeKloud. Giảng viên: Mumshad Mannambeth.

Repo ghi chú, link tài liệu, và đáp án các practice question của toàn bộ khóa học: kodekloudhub/certified-kubernetes-administrator-course.

Fact-check:

  • Chain nhiều --authorization-mode: mỗi module trả Allow/Deny/No opinion; chain dừng ngay khi có Allow hoặc Deny rõ ràng, chỉ chuyển tiếp module kế tiếp khi module hiện tại trả No opinion — 2026-09-23, Kubernetes: Authorization Overview.
  • --authorization-mode mặc định là AlwaysAllow nếu không được set — 2026-09-23, kube-apiserver Reference.
  • ServiceAccount token là short-lived, cấp qua TokenRequest API, mount dạng projected volume, tự rotate, hết hạn theo vòng đời Pod (mặc định hết hạn sau 1 giờ, tự invalid ngay khi Pod bị xóa) từ Kubernetes 1.22; việc tự tạo Secret token dài hạn cho mỗi ServiceAccount mới bị tắt mặc định từ 1.24 (feature gate LegacyServiceAccountTokenNoAutoGeneration, GA ở 1.27) — 2026-09-23, Kubernetes: Service Accounts, KEP-2799: Reduction of Secret-based ServiceAccount Tokens.
  • rbac.authorization.k8s.io/v1 là apiVersion ổn định hiện hành cho Role/RoleBinding/ClusterRole/ClusterRoleBinding; v1beta1 ngừng được apiserver phục vụ từ Kubernetes 1.22 — 2026-09-23, Kubernetes: Deprecated API Migration Guide.
  • Cú pháp kubectl create role/clusterrole/rolebinding/clusterrolebinding (--verb, --resource, --resource-name, --clusterrole, --role, --user, --group, --serviceaccount) khớp với kubectl hiện hành — 2026-09-23, kubectl create role Reference.