Skip to content

RBAC cho Multi-Tenancy

Tại sao RBAC là trục bảo mật chính của soft multi-tenancy

Trong soft multi-tenancy, RBAC (Role-Based Access Control) là cơ chế quyết định ai được làm gì với tài nguyên nào. Nếu NetworkPolicy kiểm soát luồng dữ liệu thì RBAC kiểm soát luồng quyền. Một cấu hình RBAC sai không gây lỗi ngay lập tức — nó âm thầm cấp quá nhiều quyền, và chỉ lộ ra khi một tenant (hoặc kẻ tấn công đã chiếm credential của tenant) khai thác quyền dư thừa đó để đọc secret của tenant khác, leo thang lên cluster-admin, hoặc đọc dữ liệu cross-namespace.

Điểm khó của RBAC trong multi-tenancy là các con đường escalation tinh vi: một quyền tưởng vô hại (như create pods hoặc escalate trên role, hoặc impersonate) có thể là cửa hậu để vượt qua mọi ranh giới namespace. Hiểu các con đường này là khác biệt giữa một cluster thực sự cô lập và một cluster chỉ trông có vẻ cô lập.

Internal model: bốn primitive của RBAC

RBAC trong Kubernetes có đúng bốn loại đối tượng, và hiểu sự khác biệt scope là nền tảng:

Đối tượngScopeĐịnh nghĩa quyền hay gán quyềnÁp dụng ở đâu
RoleNamespacedĐịnh nghĩa quyềnTrong một namespace cụ thể
ClusterRoleClusterĐịnh nghĩa quyềnToàn cluster (hoặc tái dùng trong namespace)
RoleBindingNamespacedGán quyền cho subjectTrong một namespace cụ thể
ClusterRoleBindingClusterGán quyền cho subjectToàn cluster

Sự kết hợp tạo ra hành vi quan trọng:

  1. Role + RoleBinding: Quyền giới hạn trong một namespace. Đây là khối xây dựng chính cho multi-tenancy — tenant admin có Role edit được bind trong namespace của họ.

  2. ClusterRole + RoleBinding (kết hợp quan trọng nhất cho multi-tenancy): Định nghĩa quyền một lần ở cấp cluster (ví dụ ClusterRole tenant-admin), rồi bind nó trong từng namespace qua RoleBinding. Quyền chỉ áp dụng trong namespace của RoleBinding đó, dù ClusterRole là cluster-scoped. Đây là cách tái sử dụng định nghĩa role mà không cấp quyền cluster-wide. Pattern chuẩn.

  3. ClusterRole + ClusterRoleBinding: Quyền áp dụng toàn cluster, mọi namespace. Đây là điều bạn gần như không bao giờ muốn cấp cho tenant. Một ClusterRoleBinding với ClusterRole edit cho phép tenant ghi vào mọi namespace.

Quy tắc vàng cho multi-tenancy: Tenant chỉ nhận RoleBinding, không bao giờ ClusterRoleBinding. Dùng ClusterRole để định nghĩa role tái sử dụng, nhưng luôn bind qua RoleBinding scoped namespace.

Các built-in ClusterRole và bẫy của chúng

Kubernetes có sẵn cluster-admin, admin, edit, view. Trong multi-tenancy:

  • view: chỉ đọc, nhưng bao gồm cả đọc Secret trong namespace? Thực tế built-in view trong các phiên bản gần đây không cho đọc Secret (đã được sửa) — nhưng luôn kiểm tra. Đừng giả định.
  • edit: đọc/ghi hầu hết tài nguyên namespaced, bao gồm đọc Secret. Nếu bind trong namespace, tenant đọc được mọi Secret trong namespace của họ (chấp nhận được). Nhưng edit cũng cho phép tạo Pod — và tạo Pod là một con đường escalation (xem phần dưới).
  • admin: như edit cộng quản lý RBAC trong namespace (tạo Role/RoleBinding). Nguy hiểm: tenant admin có thể tự tạo RoleBinding gán thêm quyền cho chính mình — nhưng chỉ trong giới hạn quyền họ đã có (cơ chế escalate prevention).
  • cluster-admin: toàn quyền. Không bao giờ cấp cho tenant.

Con đường privilege escalation: nơi RBAC rò rỉ

Đây là phần quan trọng nhất. RBAC có thể bị vượt qua qua các con đường gián tiếp:

1. Tạo Pod → đánh cắp ServiceAccount token

Nếu tenant có quyền create pods trong một namespace, họ có thể tạo Pod gắn với bất kỳ ServiceAccount nào trong namespace đó. Nếu một SA trong namespace có quyền cao (ví dụ một SA của controller được bind ClusterRole mạnh), tenant tạo Pod dùng SA đó và đánh cắp token của nó.

Phòng tránh: Không đặt SA quyền cao trong namespace của tenant. Dùng automountServiceAccountToken: false mặc định. Kiểm soát SA nào Pod được dùng qua admission policy.

2. Quyền escalatebind

RBAC có cơ chế bảo vệ: bạn không thể tạo một Role/RoleBinding cấp quyền vượt quá quyền bạn đang có (escalation prevention). Nhưng động từ đặc biệt escalate (trên roles/clusterroles) và bind (trên rolebindings) vô hiệu hóa bảo vệ này. Một tenant có escalate có thể tự nâng quyền tùy ý.

Phòng tránh: Không bao giờ cấp escalate hay bind cho tenant. Audit các Role tùy chỉnh tìm hai động từ này.

3. Impersonation — con đường nguy hiểm nhất

Động từ impersonate cho phép một principal hành động với danh tính của user/group/SA khác. Nếu tenant có quyền impersonate, họ có thể giả danh system:masters group hoặc một admin SA và làm bất cứ điều gì.

yaml
# ClusterRole NGUY HIỂM — không bao giờ cấp cho tenant
rules:
- apiGroups: [""]
  resources: ["users", "groups", "serviceaccounts"]
  verbs: ["impersonate"]

Theo nguyên tắc least privilege, impersonate chỉ nên thuộc về một số rất ít hệ thống được audit (ví dụ một số tooling platform). Nó cho phép vượt qua mọi ranh giới RBAC.

Phòng tránh: Audit toàn cluster tìm impersonate. Nếu một SA/user có nó, coi đó là tương đương cluster-admin về mặt blast radius.

4. Wildcard trong Role

yaml
rules:
- apiGroups: ["*"]
  resources: ["*"]
  verbs: ["*"]   # tương đương admin trong namespace

Wildcard che giấu các quyền nguy hiểm (bao gồm cả các tài nguyên/động từ mới được thêm trong tương lai). Một Role wildcard hôm nay an toàn có thể trở nên nguy hiểm khi một CRD mới được cài.

Phòng tránh: Liệt kê tường minh resources và verbs. Tránh wildcard trong role của tenant.

Production architecture patterns

Pattern 1: ClusterRole định nghĩa + RoleBinding gán (chuẩn)

Định nghĩa các "persona" của tenant một lần bằng ClusterRole, bind per-namespace:

yaml
# Định nghĩa MỘT LẦN ở cấp cluster (chỉ là template, chưa cấp quyền)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: tenant-developer
rules:
- apiGroups: ["", "apps", "batch"]
  resources: ["pods", "deployments", "services", "configmaps", "jobs"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
---
# Bind TRONG namespace của từng tenant (quyền chỉ áp dụng namespace này)
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: acme-developers
  namespace: tenant-acme        # ← scope giới hạn ở đây
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole              # tham chiếu ClusterRole nhưng...
  name: tenant-developer
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: gke-security-groups:acme-developers@company.com   # Google Group

Lưu ý ClusterRole tenant-developer cố tình không chứa secrets ghi, pods/exec, hay rolebindings — tùy threat model bạn có thể loại bỏ thêm.

Pattern 2: Tích hợp Google Groups (Google Groups for RBAC)

Quản lý hàng trăm tenant với user riêng lẻ là ác mộng. GKE hỗ trợ Google Groups for RBAC: bind RBAC subject là một Google Group (gke-security-groups:team@company.com) thay vì user lẻ. Thêm/xóa thành viên qua Google Workspace, không cần đụng đến cluster. Đây là pattern bắt buộc cho multi-tenancy quy mô lớn — RBAC trỏ đến group, membership quản lý ở IAM/Workspace.

Pattern 3: Tách IAM (cấp GCP) và RBAC (cấp Kubernetes)

Hai lớp authorization tách biệt cần phối hợp:

  • IAM kiểm soát ai truy cập được GKE API (tạo/xóa cluster, lấy credentials). Một user có container.clusters.get lấy được kubeconfig.
  • RBAC kiểm soát ai làm gì bên trong cluster.

Bẫy: IAM role roles/container.admin cấp quyền cluster-admin Kubernetes ngầm (vì nó cho phép mọi thao tác container). Đừng cấp IAM container.admin hay container.developer rộng rãi cho tenant; cấp container.clusterViewer (chỉ lấy credentials) rồi để RBAC trong cluster quyết định quyền thực tế.

Real-world scenario

Internal platform với 40 đội: Platform team định nghĩa 3 ClusterRole (tenant-viewer, tenant-developer, tenant-admin). Mỗi đội có một Google Group tương ứng (team-x-developers@). Khi tạo namespace cho đội mới, một pipeline GitOps tự tạo RoleBinding bind group của đội vào namespace của họ với role phù hợp. Không user lẻ nào được bind trực tiếp. Khi một kỹ sư chuyển đội, chỉ cần cập nhật membership group — quyền cluster tự cập nhật. Không ai có ClusterRoleBinding ngoài platform admin (một group nhỏ, được audit, dùng cluster-admin).

Common mistakes / anti-patterns

Anti-pattern 1: ClusterRoleBinding cho tenant

Vì sao xảy ra: Tiện tay — bind một lần cho cả cluster thay vì từng namespace.

Hệ quả ở scale: Tenant có quyền trên mọi namespace, kể cả namespace của tenant khác và system namespace. Phá vỡ hoàn toàn isolation. Một ClusterRoleBinding với role edit = tenant đọc mọi Secret trong cluster.

Cách phòng tránh: Tenant chỉ nhận RoleBinding. Audit định kỳ: kubectl get clusterrolebindings và kiểm tra subject nào không phải system/platform.

Anti-pattern 2: Cấp pods/exec và pods/portforward bừa bãi

Vì sao xảy ra: Developer muốn debug, đội cấp pods/exec trong role developer.

Hệ quả ở scale: pods/exec cho phép vào shell của bất kỳ Pod nào trong namespace, kể cả Pod đang mount Secret nhạy cảm hoặc chạy với SA quyền cao. Trong namespace dùng chung, đây là cửa hậu đọc dữ liệu. pods/portforward cho phép bypass NetworkPolicy.

Cách phòng tránh: Tách pods/exec thành một role riêng chỉ cấp khi cần, audit việc dùng. Trong namespace nhạy cảm, không cấp exec.

Anti-pattern 3: Bỏ qua audit ServiceAccount token

Vì sao xảy ra: Mặc định mọi Pod auto-mount SA token; không ai để ý.

Hệ quả ở scale: Một Pod bị xâm phạm có ngay token của SA của nó. Nếu SA đó có quyền (qua RoleBinding), kẻ tấn công dùng token gọi API. Trong multi-tenant, SA default của namespace tenant không nên có quyền gì.

Cách phòng tránh: Đặt automountServiceAccountToken: false cho SA default. Mỗi workload có SA riêng với quyền tối thiểu. Dùng Workload Identity (Chương 13) cho truy cập GCP, không dùng SA token Kubernetes cho mục đích đó.

References