從 Context 接續到授權#
上一篇用 Context 處理了「我是誰、要連到哪個叢集」的問題,但只談到了使用者的識別,並沒有真正討論「這個身份能做什麼」。這一篇就把另一半補齊:用 RBAC(Role-Based Access Control)為使用者綁定權限。
API Server 的兩道關卡#
任何想取得 Kubernetes 資源的請求,都會被 kube-apiserver 連續檢查兩件事:
- Authentication(身份認證):確認是誰在發請求。
- Authorization(授權):確認這個身份能不能做這件事。
身份認證#
Kubernetes 把使用者粗分成兩種:
- 普通使用者(User):能透過 kubectl 或直接打 RESTful API 與叢集溝通的對象,也就是寫在 Context 裡的那個
user。User 是全域概念,沒有對應的 Kubernetes 物件。 - 服務帳號(ServiceAccount):本身是一種 Kubernetes 資源,作用域以 namespace 為單位。每個 namespace 在建立時都會有一個預設的
defaultServiceAccount,並帶有 token 供同 namespace 中的 Pod 使用。Pod 內的程式向 API Server 認證時,靠的就是這個 token。
Kubernetes 支援的驗證方式包含:
- Certificate
- Token
- OpenID
- Webhook
對 User 來說最常見的是 Certificate:用戶端要持有一張由叢集 CA 簽署的 X.509 憑證,API Server 會驗證這張憑證來確認身份。概念上和 HTTPS 憑證一樣,差別只在簽署方變成 Kubernetes。
授權#
通過身份認證只代表「這位使用者可以開口」,能不能拿到資源還要看授權。API Server 在這一步會檢查請求的多個屬性:user、Verb(HTTP 動作或 API request verb)、Resource、Namespace、API Group 等。
Kubernetes 的授權模式有:
- Node
- ABAC
- RBAC
- Webhook
本篇示範的是 RBAC。另外有個小技巧可以快速檢查當前身份是否被允許做某件事:
# 檢查當前 user 是否能在 default namespace 對 deployments 執行 create
kubectl auth can-i create deployments --namespace default
# yes實戰:用 RBAC 綁定權限#
RBAC 是 Kubernetes v1.8 正式引入的授權機制,主要由四個資源組成:
- Role
- ClusterRole
- RoleBinding
- ClusterRoleBinding
整個流程的目標是:取得身份 → 為身份建立 Role → 用 RoleBinding 把 Role 綁到身份上。
建立 Context 並用 X.509 憑證驗證 User#
直接用 docker-desktop 叢集本身的 CA 來簽 client 憑證。
第一步,產生使用者私鑰:
openssl genrsa -out pod-viewer.key 2048第二步,用私鑰產生 CSR(Certificate Signing Request)。Kubernetes 會把 Subject 中的 Common Name(CN)當成使用者名稱:
openssl req -new -key pod-viewer.key -out pod-viewer.csr -subj "/CN=pod-viewer/O=app"第三步,到 docker-desktop 節點裡取出 CA 根憑證。可以借助 kubectl-node-shell ↗:
curl -LO https://github.com/kvaps/kubectl-node-shell/raw/master/kubectl-node_shell
chmod +x ./kubectl-node_shell
sudo mv ./kubectl-node_shell /usr/local/bin/kubectl-node_shell
kubectl node-shell docker-desktop
# 進入節點後分別取得 ca.crt 與 ca.key 內容到本機檔案
cat /etc/kubernetes/pki/ca.crt
cat /etc/kubernetes/pki/ca.key到這一步本機目錄會有四個檔案:ca.crt、ca.key、pod-viewer.csr、pod-viewer.key。
第四步,用叢集 CA 簽署 CSR:
openssl x509 -req -in pod-viewer.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out pod-viewer.crt -days 365可以用以下指令確認憑證內容(包含 Issuer、Subject、有效期等):
openssl x509 -noout -text -in pod-viewer.crt有了憑證之後,註冊一個 User 並建立對應 Context:
kubectl config set-credentials pod-viewer \
--client-certificate=ca.crt \
--client-key=ca.key \
--embed-certs=true
kubectl config set-context only-view --cluster=docker-desktop --user=pod-viewer切到這個 Context 試打一個簡單請求:
kubectl config use-context only-view
kubectl get pod
# Error from server (Forbidden): pods is forbidden:
# User "pod-viewer" cannot list resource "pods" in API group "" in the namespace "default"身份認證有過,但什麼都做不了——這就是還沒授權的狀態。
Role 與 ClusterRole#
Role 與 ClusterRole 都代表「一組允許的操作」,差別在於作用範圍:
- Role:限定在某個 namespace 內。
- ClusterRole:作用於整個叢集。
如果無法使用 ClusterRole,碰到需要跨 namespace 的權限時,就要逐一 namespace 綁定,會變成維運上的惡夢。
一個 Role 範例:
# role.yaml
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: default
name: pod-viewer
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "watch", "list"]一條 rule 由三個欄位組成:
apiGroups:資源所屬的 API Group。""代表 core 組(v1),其他常見的還有 extensions、apps、batch 等。resources:資源型別,例如 pods、deployments、services、secrets。verbs:動作,例如 get、list、watch、create、delete、update。
ClusterRole 結構幾乎相同,只是不需要 namespace:
# cluster-role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cluster-pod-viewer
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]要建立 Role,記得先切回管理員 Context:
kubectl config use-context docker-desktop
kubectl apply -f role.yaml到這裡 Role 存在了,但沒有綁定到任何使用者,所以還是沒人有權限。
RoleBinding 與 ClusterRoleBinding#
RoleBinding(或 ClusterRoleBinding)的工作是把 Role 綁到一個或多個 Subject 上。Subject 可以是:
- User:對應到外部的單一帳號
subjects:
- kind: User
name: "alice@example.com"
apiGroup: rbac.authorization.k8s.io- ServiceAccount:對應到 namespace 內的服務帳號
subjects:
- kind: ServiceAccount
name: default
namespace: kube-system- Group:可以用前綴匹配把一群 ServiceAccount 或 User 視為同一組
# qa namespace 內所有 ServiceAccount
subjects:
- kind: Group
name: system:serviceaccounts:qa
apiGroup: rbac.authorization.k8s.io
# 所有已認證/未認證使用者
subjects:
- kind: Group
name: system:authenticated
apiGroup: rbac.authorization.k8s.io
- kind: Group
name: system:unauthenticated
apiGroup: rbac.authorization.k8s.io
system:這個前綴是 Kubernetes 系統保留的,自訂使用者或群組名稱不要踩到。除此之外 RBAC 對命名沒有額外限制。
把 pod-viewer 綁到剛剛的 Role:
# role-binding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: pod-viewer-rolebinding
namespace: default
subjects:
- kind: User
name: pod-viewer
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-viewer
apiGroup: rbac.authorization.k8s.iokubectl apply -f role-binding.yaml切回 only-view Context 驗證權限:
kubectl config use-context only-view
# default namespace 可以列 pod
kubectl get pod -n default
# No resources found in default namespace.
# kube-system namespace 仍然被擋
kubectl get pod -n kube-system
# Error from server (Forbidden): pods is forbidden:
# User "pod-viewer" cannot list resource "pods" in API group "" in the namespace "kube-system"行為符合 Role 的限制:只能在 default namespace 看 Pod,其他 namespace 都會被擋下來。
小結#
走完這個流程之後,整條鏈路就成形了:用憑證建立身份、用 Role 描述允許的動作、用 RoleBinding 把兩者綁起來。Kubernetes 圍繞認證與授權的設計面向非常多,這裡只走了最常見的 X.509 + RBAC 路線,後續若需要 ServiceAccount 跨服務呼叫、Webhook 整合 SSO 或多叢集 RBAC 治理,都還有大量官方文件可以慢慢啃。
原文出處#
- GitHub:https://github.com/MikeHsu0618/2022-ithelp/tree/main/Day29
- iThome:https://ithelp.ithome.com.tw/articles/10300806