RBAC quyết định ai được làm gì trong cụm. Bài này cấp quyền từng lớp một cho một ServiceAccount và đo chính xác nó mở ra được gì.
Bốn object, hai cặp
Role quyền, phạm vi MỘT namespace
ClusterRole quyền, phạm vi TOÀN CỤM
RoleBinding gắn quyền cho ai đó, trong một namespace
ClusterRoleBinding gắn quyền cho ai đó, toàn cụm
Role và ClusterRole chỉ mô tả quyền — chúng không cấp cho ai cả. Việc cấp là của Binding.
Và có một tổ hợp hay dùng: ClusterRole gắn bằng RoleBinding. Khi ấy quyền được định nghĩa một lần ở cấp cụm nhưng chỉ có hiệu lực trong một namespace — cách đúng để tái sử dụng định nghĩa quyền cho nhiều đội.
Đo từng lớp
ServiceAccount r/ung, chưa cấp gì:
list nodes -> no
Cấp Role "doc-pod" (get,list,watch trên pods) trong namespace r:
ns=r get pods |
yes |
ns=r list pods |
yes |
ns=r create pods |
no |
ns=r delete pods |
no |
ns=r get secrets |
no |
ns=r get deployments |
no |
ns=default get pods |
no |
ns=kube-system get pods |
no |
Phạm vi rất chặt: đúng động từ đã khai, đúng tài nguyên đã khai, đúng namespace chứa RoleBinding.
Đổi sang ClusterRole cùng nội dung, gắn bằng ClusterRoleBinding:
list pods ở r, default, kube-system -> yes / yes / yes
Mọi namespace, kể cả kube-system.
Đây là lý do ClusterRoleBinding phải được xem xét kỹ hơn nhiều: nó không có ranh giới nào.
Quyền đọc Secret là quyền đọc mật khẩu
Cấp Role "doc-secret" (get,list trên secrets), rồi đọc thử:
get secrets -> yes
giá trị đọc được: SieuBiMat123
Base64 không phải mã hoá (phần 61). Cấp "chỉ đọc" Secret là cấp quyền đọc mọi mật khẩu, khoá API và chứng chỉ trong namespace đó.
"Chỉ đọc" nghe như quyền an toàn nhất có thể. Với secrets thì không.
Cách xử lý: đừng cấp secrets ở mức tài nguyên. Cấp theo tên cụ thể:
rules:
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["cau-hinh-cua-toi"]
verbs: ["get"]
resourceNames chỉ hoạt động với get, update, delete — không với list. Đó là chủ ý: list trả về mọi thứ, nên không lọc theo tên được.
Ba quyền tương đương quyền quản trị
create pods — tạo pod với hostPath: / và hostNetwork: true là có quyền root trên node đó (phần 64 đã nêu về DaemonSet). Từ đó đọc được /var/lib/kubelet và mọi Secret đang gắn trên node.
get secrets — như đo ở trên. Và trong đó thường có token của ServiceAccount khác, tức là leo thang tiếp.
create rolebindings — tự cấp thêm quyền cho chính mình.
Kubernetes có chặn một phần cái thứ ba: bạn không thể tạo RoleBinding cấp quyền cao hơn quyền mình đang có, trừ khi có động từ escalate. Nhưng nếu bạn có create rolebindings và một ClusterRole rộng nào đó, đường leo thang vẫn mở.
Cả ba đều nghe vô hại trong một bản kê YAML. Cả ba đều là đường leo thang quyền trong thực tế.
kubectl auth can-i là công cụ đúng
kubectl auth can-i <verb> <resource> \
--as=system:serviceaccount:<ns>:<sa> --namespace=<ns>
Một lệnh, trả lời yes hoặc no.
Đây là cách duy nhất kiểm được quyền thực tế thay vì đọc YAML rồi đoán — vì quyền là hợp của mọi Role và ClusterRole được gắn qua mọi Binding, và với một ServiceAccount đã tồn tại lâu thì danh sách đó không ai nhớ hết.
Xem toàn bộ:
kubectl auth can-i --list --as=system:serviceaccount:r:ung --namespace=r
Ba lỗi hay gặp
Dùng cluster-admin cho tiện. Đây là ClusterRole cho phép mọi thứ. Gắn nó cho một ServiceAccount của ứng dụng nghĩa là một lỗ hổng trong ứng dụng đó thành quyền toàn cụm.
verbs: ["*"] hoặc resources: ["*"]. Dấu sao trong RBAC bao gồm cả những tài nguyên chưa tồn tại — cài một toán tử mới là nó tự có quyền trên CRD của toán tử đó.
Quên ServiceAccount mặc định. Pod không khai serviceAccountName sẽ dùng default của namespace. Nếu ai đó từng gắn quyền cho default, mọi pod trong namespace có quyền đó.
spec:
automountServiceAccountToken: false
Với pod không gọi API Kubernetes — tức phần lớn ứng dụng — dòng này nên có. Không có token gắn vào thì không có gì để đánh cắp.
Nhóm mặc định đáng biết
system:authenticated mọi người đã xác thực
system:serviceaccounts mọi ServiceAccount
system:masters bỏ qua RBAC hoàn toàn
system:masters là nhóm mà chứng chỉ quản trị của kubeadm thuộc về. Thành viên nhóm này không đi qua RBAC — không Role nào giới hạn được, và không cách nào thu hồi ngoài việc xoay chứng chỉ CA.
Đó là lý do tệp kubeconfig của quản trị viên phải được coi như khoá gốc của cụm.
Thử ba mươi giây
Tìm những gì được gắn cluster-admin trong cụm của bạn:
kubectl get clusterrolebindings -o json | python3 -c '
import sys,json
for b in json.load(sys.stdin)["items"]:
if b["roleRef"]["name"]=="cluster-admin":
for s in b.get("subjects") or []:
print(b["metadata"]["name"], "->", s["kind"], s.get("namespace",""), s["name"])
'
Ngoài system:masters và vài thành phần lõi, mỗi dòng còn lại là một danh tính có toàn quyền trên cụm — và danh sách đó thường dài hơn mọi người nghĩ.
Phần sau đo ServiceAccount và token: thứ pod dùng để tự xưng danh với API server.