從 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 在建立時都會有一個預設的 default ServiceAccount,並帶有 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.crtca.keypod-viewer.csrpod-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.io
kubectl 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