本頁面提供規劃角色型存取權控管 (RBAC) 政策的最佳做法。如要瞭解如何在 Google Kubernetes Engine (GKE) 中導入 RBAC,請參閱「設定角色型存取權控管」。RBAC 是 Kubernetes 的核心安全功能,可讓您建立精細的權限,管理使用者和工作負載可對叢集中的資源執行的動作。您會建立 RBAC 角色,並將這些角色繫結至主體,也就是經過驗證的使用者,例如服務帳戶或 Google 群組。
本頁內容適用於安全專家和作業人員,他們負責為機構規劃及實作 RBAC 政策。如要進一步瞭解 Google Cloud 內容中提及的常見角色和範例工作,請參閱常見的 GKE Enterprise 使用者角色和工作。
閱讀本頁面之前,請先熟悉下列概念:
如需這項指引的檢查清單,請參閱「檢查清單摘要」。
RBAC 的運作方式
RBAC 支援下列類型的角色和繫結:
- ClusterRole:一組可套用至任何命名空間或整個叢集的權限。
- 角色:一組權限,僅限於單一命名空間。
- ClusterRoleBinding:將
ClusterRole
繫結至使用者或群組,適用於叢集中的所有命名空間。 - RoleBinding:將
Role
或ClusterRole
繫結至特定命名空間中的使用者或群組。
您可以在 Role
或 ClusterRole
中將權限定義為 rules
。角色中的每個 rules
欄位都包含 API 群組、該 API 群組中的 API 資源,以及允許對這些資源執行的動詞 (動作)。或者,您可以使用 resourceNames
欄位,將動詞範圍限定為 API 資源的具名執行個體。如需範例,請參閱「限制只能存取特定資源執行個體」。
定義角色後,您可以使用 RoleBinding
或 ClusterRoleBinding
將角色繫結至主體。根據您要在單一命名空間或多個命名空間中授予權限,選擇繫結類型。
RBAC 角色設計
遵循最低權限原則
指派 RBAC 角色所含權限時,請採用最低權限原則,並授予執行工作所需的最低權限。採用最低權限原則可以降低叢集遭駭時權限提升的可能性,以及因過度存取而引發安全事件的機率。
設計角色時,請仔細考量常見的權限提升風險,例如 escalate
或 bind
動詞、PersistentVolume 的 create
存取權,或是 Certificate Signing Requests 的 create
存取權。如需風險清單,請參閱 Kubernetes RBAC - 權限提升風險。
避免使用預設角色和群組
Kubernetes 會建立一組預設的 ClusterRole 和 ClusterRoleBinding,可用於 API 探索及啟用受管理元件功能。視角色而定,這些預設角色授予的權限可能相當廣泛。Kubernetes 也有一組預設使用者和使用者群組,以 system:
前置字元識別。根據預設,Kubernetes 和 GKE 會自動將這些角色繫結至預設群組和各種主體。如需 Kubernetes 建立的預設角色和繫結完整清單,請參閱「預設角色和角色繫結」。
下表說明部分預設角色、使用者和群組。建議您避免與這些角色、使用者和群組互動,除非您已仔細評估過,因為與這些資源互動可能會對叢集的安全性狀態造成意料之外的後果。
名稱 | 類型 | 說明 |
---|---|---|
cluster-admin |
ClusterRole | 授予主體在叢集內對任何資源執行任何操作的權限。 |
system:anonymous |
使用者 | Kubernetes 會將這個使用者指派給未提供驗證資訊的 API 伺服器要求。 將角色繫結至這個使用者後,任何未通過驗證的使用者都會獲得該角色授予的權限。 |
system:unauthenticated |
群組 | Kubernetes 會將這個群組指派給未提供驗證資訊的 API 伺服器要求。 將角色繫結至這個群組後,任何未通過驗證的使用者都會獲得該角色授予的權限。 |
system:authenticated |
群組 | 如果使用者登入 Google 帳戶 (包括所有 Gmail 帳戶),GKE 會將這個群組指派給 API 伺服器要求。實際上,這與 將角色繫結至這個群組後,任何擁有 Google 帳戶的使用者 (包括所有 Gmail 帳戶) 都會獲得該角色授予的權限。 |
system:masters |
群組 | Kubernetes 預設會將 如果將自有主體新增至這個群組,這類主體就能對叢集內的各項資源執行任何操作。 |
請盡可能避免建立涉及預設使用者、角色和群組的繫結。這可能會對叢集的安全性狀態造成意想不到的後果。例如:
- 將預設
cluster-admin
ClusterRole 繫結至system:unauthenticated
群組,會授予任何未經驗證的使用者存取叢集中所有資源 (包括密鑰) 的權限。這類高權限繫結是攻擊 (例如大規模惡意軟體活動) 的目標。 - 將自訂角色繫結至
system:unauthenticated
群組,即可授予未經驗證的使用者該角色授予的權限。
請盡可能遵守下列規定:
- 請勿將自己的主體新增至
system:masters
群組。 - 請勿將
system:unauthenticated
群組繫結至任何 RBAC 角色。 - 請勿將
system:authenticated
群組繫結至任何 RBAC 角色。 - 請勿將
system:anonymous
使用者繫結至任何 RBAC 角色。 - 請勿將
cluster-admin
ClusterRole 繫結至您自己的主體,或任何預設使用者和群組。如果應用程式需要多項權限,請判斷確切需要的權限,並為此建立特定角色。 - 繫結主體前,請先評估其他預設角色授予的權限。
- 修改預設群組的成員之前,請先評估繫結至這些群組的角色。
禁止使用預設群組
您可以使用 gcloud CLI,在參照 system:unauthenticated
和 system:authenticated
群組或 system:anonymous
使用者的叢集中,停用非預設 RBAC 繫結。建立新的 GKE 叢集或更新現有叢集時,請使用下列一或兩個標記。使用這些標記不會停用參照這些群組的預設 Kubernetes 繫結。這些標記需要 GKE 1.30.1-gke.1283000 以上版本。
--no-enable-insecure-binding-system-authenticated
:停用參照system:authenticated
的非預設繫結。--no-enable-insecure-binding-system-unauthenticated
:停用參照system:unauthenticated
和system:anonymous
的非預設繫結。
偵測及移除預設角色和群組的使用情形
如要檢查叢集是否在 RBAC 繫結中參照這些使用者和群組,請為叢集或車隊啟用 Kubernetes 安全防護機制掃描標準層級,這樣 GKE 就能在 Google Cloud 控制台的安全防護機制資訊主頁中顯示結果。如需操作說明,請參閱「啟用工作負載設定稽核」。
以下各節說明如何找出參照預設使用者和群組的特定 RoleBinding 或 ClusterRoleBinding,以及如何刪除這些資源。
ClusterRoleBindings
列出主體為
system:anonymous
、system:unauthenticated
或system:authenticated
的所有 ClusterRoleBinding 名稱:kubectl get clusterrolebindings -o json \ | jq -r '["Name"], ["-----"], (.items[] | select((.subjects | length) > 0) | select(any(.subjects[]; .name == "system:anonymous" or .name == "system:unauthenticated" or .name == "system:authenticated")) | [.metadata.namespace, .metadata.name]) | @tsv'
輸出內容應只列出下列 ClusterRoleBinding:
Name ---- "system:basic-user" "system:discovery" "system:public-info-viewer"
如果輸出內容包含其他非預設繫結,請為每個額外繫結執行下列步驟。如果輸出內容不含非預設繫結,請略過下列步驟。
列出與繫結相關聯的角色權限:
kubectl get clusterrolebinding CLUSTER_ROLE_BINDING_NAME -o json \ | jq ' .roleRef.name +" " + .roleRef.kind' \ | sed -e 's/"//g' \ | xargs -l bash -c 'kubectl get $1 $0 -o yaml'
將
CLUSTER_ROLE_BINDING_NAME
替換為非預設 ClusterRoleBinding 的名稱。輸出結果會與下列內容相似:
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: ... rules: - apiGroups: - "" resources: - secrets verbs: - get - watch - list
如果您判斷輸出內容中的權限可安全授予預設使用者或群組,則無須採取進一步行動。如果您判斷繫結授予的權限不安全,請繼續下一個步驟。
從叢集中刪除不安全的繫結:
kubectl delete clusterrolebinding CLUSTER_ROLE_BINDING_NAME
將
CLUSTER_ROLE_BINDING_NAME
替換為要刪除的 ClusterRoleBinding 名稱。
RoleBindings
列出主體為
system:anonymous
、system:unauthenticated
或system:authenticated
的任何 RoleBinding 的命名空間和名稱:kubectl get rolebindings -A -o json \ | jq -r '["Namespace", "Name"], ["---------", "-----"], (.items[] | select((.subjects | length) > 0) | select(any(.subjects[]; .name == "system:anonymous" or .name == "system:unauthenticated" or .name == "system:authenticated")) | [.metadata.namespace, .metadata.name]) | @tsv'
如果叢集設定正確,輸出內容應為空白。 如果輸出內容包含任何非預設繫結,請針對每個額外繫結執行下列步驟。如果輸出結果空白,請略過後續步驟。
如果您只知道 RoleBinding 的名稱,可以使用下列指令在所有命名空間中尋找相符的 RoleBinding:
kubectl get rolebindings -A -o json \ | jq -r '["Namespace", "Name"], ["---------", "-----"], (.items[] | select((.subjects | length) > 0) | select(.metadata.name == "ROLE_BINDING_NAME") | [.metadata.namespace, .metadata.name]) | @tsv'
請將
ROLE_BINDING_NAME
替換為非預設 RoleBinding 的名稱。列出與繫結相關聯的角色權限:
kubectl get rolebinding ROLE_BINDING_NAME --namespace ROLE_BINDING_NAMESPACE -o json \ | jq ' .roleRef.name +" " + .roleRef.kind' \ | sed -e 's/"//g' \ | xargs -l bash -c 'kubectl get $1 $0 -o yaml --namespace ROLE_BINDING_NAMESPACE'
更改下列內容:
ROLE_BINDING_NAME
:非預設 RoleBinding 的名稱。ROLE_BINDING_NAMESPACE
:非預設 RoleBinding 的命名空間。
輸出結果會與下列內容相似:
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: ... rules: - apiGroups: - "" resources: - secrets verbs: - get - watch - list
如果您判斷輸出內容中的權限可安全授予預設使用者或群組,則無須採取進一步行動。如果您判斷繫結授予的權限不安全,請繼續下一個步驟。
從叢集中刪除不安全的繫結:
kubectl delete rolebinding ROLE_BINDING_NAME --namespace ROLE_BINDING_NAMESPACE
更改下列內容:
ROLE_BINDING_NAME
:要刪除的 RoleBinding 名稱。ROLE_BINDING_NAMESPACE
:要刪除的 RoleBinding 命名空間。
將權限範圍設為命名空間層級
請根據工作負載或使用者的需求,使用繫結和角色:
- 如要授予一個命名空間中資源的存取權,請使用
Role
和RoleBinding
。 - 如要授予多個命名空間中資源的存取權,請為每個命名空間使用
ClusterRole
和RoleBinding
。 - 如要授予每個命名空間中資源的存取權,請使用
ClusterRole
,並搭配ClusterRoleBinding
。
盡可能在最少的命名空間中授予權限。
請勿使用萬用字元
*
字元是適用於所有內容的萬用字元。請勿在規則中使用萬用字元。在 RBAC 規則中明確指定 API 群組、資源和動詞。舉例來說,在 verbs
欄位中指定 *
,會授予資源 get
、list
、watch
、patch
、update
、deletecollection
和 delete
權限。下表列出在規則中避免使用萬用字元的範例:
建議 | 不建議使用 |
---|---|
- rules: apiGroups: ["apps","extensions"] resources: ["deployments"] verbs: ["get","list","watch"] 專門授予 |
- rules: apiGroups: ["*"] resources: ["deployments"] verbs: ["get","list","watch"] 將動詞授予任何 API 群組中的 |
- rules: apiGroups: ["apps", "extensions"] resources: ["deployments"] verbs: ["get", "list", "watch"] 僅將 |
- rules: apiGroups: ["apps", "extensions"] resources: ["deployments"] verbs: ["*"] 授予所有動詞,包括 |
使用個別規則授予特定資源的最低權限存取權
規劃規則時,請嘗試下列高階步驟,為每個角色設計更有效率的最低權限規則:
- 針對主體需要存取的每個資源,為每個動詞草擬個別的 RBAC 規則。
- 草擬規則後,請分析規則,檢查是否有相同
verbs
清單的多項規則。將這些規則合併為單一規則。 - 將所有剩餘規則彼此分開。
這種做法可讓規則設計更有條理,將授予多個資源相同動詞的規則合併,並將授予資源不同動詞的規則分開。
舉例來說,如果工作負載需要 deployments
資源的 get 權限,但需要 list
和 watch
資源的 daemonsets
權限,則建立角色時應使用個別規則。將 RBAC 角色繫結至工作負載後,工作負載就無法在 deployments
上使用 watch
。
再舉一例,如果工作負載在 pods
資源和 daemonsets
資源上都需要 get
和 watch
,您可以將這些項目合併為單一規則,因為工作負載在兩個資源上都需要相同的動詞。
下表顯示這兩種規則設計都有效,但分割規則可根據您的需求,更精細地限制資源存取權:
建議 | 不建議使用 |
---|---|
- rules: apiGroups: ["apps"] resources: ["deployments"] verbs: ["get"] - rules: apiGroups: ["apps"] resources: ["daemonsets"] verbs: ["list", "watch"] 授予 Deployment 的 |
- rules: apiGroups: ["apps"] resources: ["deployments", "daemonsets"] verbs: ["get","list","watch"] 將動詞授予 Deployment 和 DaemonSet。即使主體可能不需要存取 |
- rules: apiGroups: ["apps"] resources: ["daemonsets", "deployments"] verbs: ["list", "watch"] 合併兩項規則,因為主體需要 |
- rules: apiGroups: ["apps"] resources: ["daemonsets"] verbs: ["list", "watch"] - rules: apiGroups: ["apps"] resources: ["deployments"] verbs: ["list", "watch"] 這些分割規則與合併規則的結果相同,但會在角色資訊清單中產生不必要的雜亂內容 |
限制特定資源執行個體的存取權
RBAC 可讓您在規則中使用 resourceNames
欄位,限制對特定具名資源執行個體的存取權。舉例來說,如果您要編寫 RBAC 角色,該角色只需要 update
seccomp-high
ConfigMap,您可以使用 resourceNames
僅指定該 ConfigMap。請盡可能使用 resourceNames
。
建議 | 不建議使用 |
---|---|
- rules: apiGroups: [""] resources: ["configmaps"] resourceNames: ["seccomp-high"] verbs: ["update"] 將主體限制為只能更新 |
- rules: apiGroups: [""] resources: ["configmaps"] verbs: ["update"] 主體可以更新 |
- rules: apiGroups: [""] resources: ["configmaps"] verbs: ["list"] - rules: apiGroups: [""] resources: ["configmaps"] resourceNames: ["seccomp-high"] verbs: ["update"] 具備命名空間中所有 ConfigMap 的 |
- rules: apiGroups: [""] resources: ["configmaps"] verbs: ["update", "list"] 授予所有 ConfigMap 的 |
禁止服務帳戶修改 RBAC 資源
請勿將具有 bind
、escalate
、create
、update
或 patch
權限的 Role
或 ClusterRole
資源,繫結至任何命名空間中的服務帳戶。rbac.authorization.k8s.io
escalate
和 bind
特別容易讓攻擊者規避 RBAC 內建的權限提升防範機制。
Kubernetes 服務帳戶
為各項工作負載建立 Kubernetes 服務帳戶
為每個工作負載建立個別的 Kubernetes 服務帳戶。將最低權限 Role
或 ClusterRole
繫結至該服務帳戶。
請勿使用預設服務帳戶
Kubernetes 會在每個命名空間中建立名為 default
的服務帳戶。如果 Pod 未在資訊清單中明確指定服務帳戶,系統會自動將 default
服務帳戶指派給這些 Pod。請勿將 Role
或 ClusterRole
繫結至 default
服務帳戶。Kubernetes 可能會將 default
服務帳戶指派給不需要這些角色授予存取權的 Pod。
不要自動掛接服務帳戶權杖
Pod 規格中的 automountServiceAccountToken
欄位會告知 Kubernetes 將 Kubernetes 服務帳戶的憑證權杖插入 Pod。Pod 可以使用這個權杖,向 Kubernetes API 伺服器發出已驗證的要求。這個欄位的預設值為 true
。
在所有 GKE 版本中,如果 Pod 不需要與 API 伺服器通訊,請在 Pod 規格中設定 automountServiceAccountToken=false
。
偏好使用臨時權杖,而非以 Secret 為基礎的權杖
根據預設,節點上的 kubelet 程序會為每個 Pod 擷取短期有效的服務帳戶權杖,並自動輪替。除非您在 Pod 規格中將 automountServiceAccountToken
欄位設為 false
,否則 kubelet 會將這個權杖掛接至 Pod,做為投射磁碟區。Pod 對 Kubernetes API 的任何呼叫都會使用這個權杖,向 API 伺服器進行驗證。
如果您是手動擷取服務帳戶權杖,請避免使用 Kubernetes Secrets 儲存權杖。以密碼為基礎的服務帳戶權杖是舊版憑證,不會過期,也不會自動輪替。如需服務帳戶的憑證,請使用 TokenRequest
API 取得會自動輪替的短期權杖。
持續檢查 RBAC 權限
定期檢查 RBAC 角色和存取權,找出潛在的權限提升路徑和多餘規則。舉例來說,假設您未刪除將具有特殊權限的 Role
繫結至已刪除使用者的 RoleBinding
,如果攻擊者在該命名空間中建立與已刪除使用者同名的使用者帳戶,該帳戶就會繫結至 Role
,並沿用相同的存取權。定期審查可將這類風險降至最低。