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ì.

Phạm vi Role và ClusterRole, và ba quyền tương đương quản trị

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

RoleClusterRole 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, deletekhô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: /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 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 RoleClusterRole đượ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.