Appearance
Kubernetes RBAC 帳號管理
在 AKS 叢集建立兩種用途的受限 ServiceAccount kubeconfig:Power User(全域維運但禁刪 Node/Namespace)與 Restricted(特定 Namespace 僅讀 Pod Log),並附完整憑證輪替 SOP。
概述
直接使用叢集管理員 kubeconfig 進行日常維運,一旦操作失誤(誤刪 Namespace、誤刪 Node)後果可能是災難性且難以復原的。透過 Kubernetes RBAC(Role-Based Access Control)建立最小權限帳號,可以在不影響維運效率的前提下有效隔離風險。
本文涵蓋兩種典型受限帳號模式:
- Power User:具備全域維運能力(可刪除工作負載),但明確禁止刪除
nodes和namespaces,適合日常部署、Debug、事故處理 - Restricted:僅限特定 Namespace 的 Pod 讀取與 Log 串流,適合提供給外部支援人員或監控工具
兩種帳號採用相同設計模式:ServiceAccount + ClusterRole/RoleBinding + 靜態 Token Secret + 自動化 kubeconfig 生成腳本,以及完整的憑證輪替 SOP。
核心內容
設計原則
最小權限(Least Privilege):Power User 能刪除工作負載(Pods、Deployments)但不能刪除底層基礎設施(Node、Namespace);Restricted 帳號只能讀取,完全不能修改任何資源。
K8s 1.24+ 靜態 Token:Kubernetes 1.24 後,ServiceAccount 不再自動產生永久 Token。需要明確建立 type: kubernetes.io/service-account-token 的 Secret,Kubernetes 才會注入長期 Token。長期 Token 沒有過期日,須搭配憑證輪替 SOP 管理。
憑證輪替策略:刪除並重建 ServiceAccount(而非只刪除 Secret),因為 K8s Token 的唯一性綁定於 ServiceAccount 的 UID。重建 SA 後 UID 改變,所有舊 Token 立即全部失效,是最可靠的停權方式。
Power User 帳號設計
使用 ClusterRoleBinding(全域作用),結合兩條 RBAC rule:
- 對所有資源開放 get/list/watch/create/update/patch
- 對工作負載資源(Pods、Deployments 等,明確列舉)開放 delete
- 對
nodes和namespaces僅開放 get/list/watch/create/update/patch(明確排除 delete)
Restricted 帳號設計
使用 ClusterRole(作為可重用模板)+ RoleBinding(限定特定 Namespace):
pods的 get/list/watchpods/log的 get
RoleBinding 將 ClusterRole 效力限制在指定 Namespace,帳號無法存取其他 Namespace 的任何資源。
驗證方式
使用 kubectl auth can-i 對新 kubeconfig 做權限模擬,直接向 API Server 確認是否允許特定操作。在交付前就能確認權限範圍符合預期,比實際執行操作更安全。
關鍵要點
- K8s 1.24+ 必須手動建立
kubernetes.io/service-account-tokenSecret 才能取得長期 Token - 憑證輪替應刪除並重建 ServiceAccount(不只刪 Secret),確保舊 UID 完全失效
- Power User 使用 ClusterRoleBinding(全域);Restricted 使用 RoleBinding(限 Namespace)
kubectl auth can-i是交付前的必要驗證步驟- 產出的 kubeconfig 為長期憑證,需妥善保管並在人員異動時立即輪替
實際應用
Power User kubeconfig 提供給需要全域維運能力的維運人員,用於日常部署、Debug、事故處理。人員離職時立即執行憑證輪替 SOP 停權。
Restricted kubeconfig 提供給外部協作廠商、監控整合(如 Grafana 讀取 Pod Log)、或新進人員的受限存取。範例中帳號範圍鎖定在 ingress-basic namespace,無法對其他 namespace 進行任何操作。
部署設定參考
Power User RBAC YAML
yaml
# power-user-rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: power-user-sa
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: power-user-role
rules:
# 1. 允許對所有資源進行讀取、建立與更新
- apiGroups: ["*"]
resources: ["*"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
# 2. 允許刪除工作負載(明確列舉,不含 nodes 和 namespaces)
- apiGroups: ["*"]
resources:
- "pods"
- "deployments"
- "statefulsets"
- "daemonsets"
- "jobs"
- "cronjobs"
- "services"
- "ingresses"
- "configmaps"
- "secrets"
- "persistentvolumeclaims"
- "events"
verbs: ["delete"]
# 3. nodes 和 namespaces 僅給基礎管理權限,明確排除 delete
- apiGroups: [""]
resources: ["nodes", "namespaces"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: power-user-global-binding
subjects:
- kind: ServiceAccount
name: power-user-sa
namespace: kube-system
roleRef:
kind: ClusterRole
name: power-user-role
apiGroup: rbac.authorization.k8s.ioPower User Token Secret
yaml
# power-user-token-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: power-user-token
namespace: kube-system
annotations:
kubernetes.io/service-account.name: power-user-sa
type: kubernetes.io/service-account-tokenPower User Kubeconfig 生成腳本
bash
#!/bin/bash
# generate_power_user_config.sh
SA_SECRET_NAME="power-user-token"
NAMESPACE="kube-system"
USER_NAME="power-user-sa"
CLUSTER_NAME="k8s-power-cluster"
KUBECONFIG_FILE="power-user.kubeconfig"
CONTEXT_NAME="power-user-context"
APISERVER=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')
CA_CRT=$(kubectl get secret $SA_SECRET_NAME -n $NAMESPACE -o jsonpath='{.data.ca\.crt}')
TOKEN=$(kubectl get secret $SA_SECRET_NAME -n $NAMESPACE -o jsonpath='{.data.token}' | base64 --decode)
kubectl config set-cluster $CLUSTER_NAME \
--server=$APISERVER \
--embed-certs=true \
--certificate-authority=<(echo "$CA_CRT" | base64 --decode) \
--kubeconfig=$KUBECONFIG_FILE
kubectl config set-credentials $USER_NAME \
--token=$TOKEN \
--kubeconfig=$KUBECONFIG_FILE
kubectl config set-context $CONTEXT_NAME \
--cluster=$CLUSTER_NAME \
--user=$USER_NAME \
--kubeconfig=$KUBECONFIG_FILE
kubectl config use-context $CONTEXT_NAME --kubeconfig=$KUBECONFIG_FILE
echo "Success: $KUBECONFIG_FILE 已產生(全域維運,禁刪 Node/Namespace)"Power User 驗證
bash
CHECK_KUBECONFIG="power-user.kubeconfig"
printf "全域讀取 Pods: [%s]\n" "$(kubectl --kubeconfig=$CHECK_KUBECONFIG auth can-i list pods --all-namespaces)"
printf "刪除 Pods: [%s]\n" "$(kubectl --kubeconfig=$CHECK_KUBECONFIG auth can-i delete pods)"
printf "建立 Namespace: [%s]\n" "$(kubectl --kubeconfig=$CHECK_KUBECONFIG auth can-i create namespaces)"
printf "刪除 Namespace: [%s] (預期 no)\n" "$(kubectl --kubeconfig=$CHECK_KUBECONFIG auth can-i delete namespaces)"
printf "刪除 Node: [%s] (預期 no)\n" "$(kubectl --kubeconfig=$CHECK_KUBECONFIG auth can-i delete nodes)"| 測試項目 | 預期 |
|---|---|
| 全域讀取 Pods | ✅ YES |
| 刪除 Pods | ✅ YES |
| 建立 Namespace | ✅ YES |
| 刪除 Namespace | ❌ NO |
| 刪除 Node | ❌ NO |
完全管理員("*" "*") | ❌ NO |
Restricted(Pod Log Viewer)RBAC YAML
yaml
# read-logs-rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: pod-log-viewer-sa
namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: pod-log-reader # ClusterRole 作為可重用模板
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["pods/log"]
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: pod-log-viewer-binding
namespace: ingress-basic # 權限僅在此 Namespace 生效
subjects:
- kind: ServiceAccount
name: pod-log-viewer-sa
namespace: ingress-basic
roleRef:
kind: ClusterRole
name: pod-log-reader
apiGroup: rbac.authorization.k8s.ioRestricted Token Secret
yaml
# sa-token-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: pod-log-viewer-token
namespace: ingress-basic # 務必與 SA 同一個 Namespace
annotations:
kubernetes.io/service-account.name: pod-log-viewer-sa
type: kubernetes.io/service-account-tokenRestricted Kubeconfig 生成腳本
bash
#!/bin/bash
# generate_kubeconfig.sh
SA_SECRET_NAME="pod-log-viewer-token"
NAMESPACE="ingress-basic"
USER_NAME="pod-log-viewer-sa"
CLUSTER_NAME="k8s-restricted-cluster"
KUBECONFIG_FILE="restricted.kubeconfig"
APISERVER=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')
CA_CRT=$(kubectl get secret $SA_SECRET_NAME -n $NAMESPACE -o jsonpath='{.data.ca\.crt}')
TOKEN=$(kubectl get secret $SA_SECRET_NAME -n $NAMESPACE -o jsonpath='{.data.token}' | base64 --decode)
if [ -z "$TOKEN" ]; then
echo "錯誤:抓不到 Token,請確認 Secret 是否存在於 $NAMESPACE"
exit 1
fi
kubectl config set-cluster $CLUSTER_NAME \
--server=$APISERVER \
--embed-certs=true \
--certificate-authority=<(echo "$CA_CRT" | base64 --decode) \
--kubeconfig=$KUBECONFIG_FILE
kubectl config set-credentials $USER_NAME \
--token=$TOKEN \
--kubeconfig=$KUBECONFIG_FILE
kubectl config set-context $NAMESPACE-context \
--cluster=$CLUSTER_NAME \
--user=$USER_NAME \
--namespace=$NAMESPACE \
--kubeconfig=$KUBECONFIG_FILE
kubectl config use-context $NAMESPACE-context --kubeconfig=$KUBECONFIG_FILE
echo "Success: $KUBECONFIG_FILE 已產生(僅 $NAMESPACE namespace 的 Pod 讀取/Log 權限)"Restricted 驗證
| 測試項目 | 預期 | 指令 |
|---|---|---|
| 讀取 Pod 列表 | ✅ YES | kubectl --kubeconfig=restricted.kubeconfig auth can-i get pods |
| 讀取 Pod Log | ✅ YES | kubectl --kubeconfig=restricted.kubeconfig auth can-i get pods/log |
| 跨 Namespace 讀取 | ❌ NO | kubectl --kubeconfig=restricted.kubeconfig auth can-i get pods -n kube-system |
| 刪除 Pod | ❌ NO | kubectl --kubeconfig=restricted.kubeconfig auth can-i delete pods |
憑證輪替 SOP(兩種帳號通用)
bash
# 步驟 A:立即停權(刪除舊 ServiceAccount)
kubectl delete -f <rbac-yaml>
kubectl delete -f <token-secret-yaml> # 若未隨 SA 一起刪除
# 步驟 B:重新核發新身分
kubectl apply -f <rbac-yaml>
kubectl apply -f <token-secret-yaml>
# 步驟 C:重新產生 kubeconfig
./generate_<type>_config.sh為什麼刪除並重建 ServiceAccount? Token 的唯一性綁定於 SA 的 UID,重建後 UID 改變,所有基於舊 SA 的 Token 立即全部失效。只刪 Secret 不能確保舊 Token 完全無效。
相關概念
- Squid Proxy(Kubernetes 部署) — 同樣部署在 AKS
ingress-basicnamespace 的基礎設施,可搭配 Restricted 帳號進行監控 - SMTP 郵件伺服器(Kubernetes 部署) — 部署於
ingress-basicnamespace,Restricted 帳號預設涵蓋此 namespace 的 Log 存取 - Prometheus Exporter 部署模式 — 監控場景下可搭配 Restricted 帳號進行 read-only 整合
- SFTP on Kubernetes 部署 — 檔案傳輸服務的 K8s 部署,受 RBAC 管理 namespace 存取
- RabbitMQ on Kubernetes 部署 — Message Broker 的 K8s 部署,維運操作受 RBAC 帳號管控
- KEDA(Kubernetes Event-Driven Autoscaling) — 事件驅動擴縮器,需要 RBAC 權限存取目標 Deployment
- Azure DevOps Pipeline CI/CD 設定指南 — AKS 部署 Pipeline 需要 RBAC kubeconfig 存取權限
- 驗證 Base64 Kubeconfig 有效性 — 交付 kubeconfig 前的連線測試 SOP
- Azure Pipeline — Kubeconfig 隔離最佳實踐 — CI/CD 中安全使用 kubeconfig 的隔離方案