mirrord 用户授权的 GitOps 化:按用户维护 RBAC 清单
目录
💡 这篇我整理一下按用户维护 RBAC 清单的方式。按组(group-based)或其他高级授权模式将在后续文章讨论。
背景:mirrord 的 RBAC 现状#
mirrord 是开发者本地调试工具,让开发者的本地进程可以通过集群中的 agent pod 访问 in-cluster 的网络资源(mirrord 是什么、完整 demo 见 《用 mirrord 把本地进程接入 K8s 集群》)。作为集群管理员,如果你想要:
- ✅ 开发者能在指定 namespace 运行 mirrord
- ✅ 开发者不能访问其他 namespace
- ✅ 开发者不能删除应用或改动配置
- ✅ 权限变化可以审计和回滚
那么你需要给每个开发者颁发 identity(身份凭证) 和 授权清单(RoleBinding)。
mirrord 的两层 RBAC 模型#
mirrord 的权限拆分挺有意思,分成两部分:
| 层级 | 资源 | 作用域 | 目的 |
|---|---|---|---|
| 命名空间权限 | RoleBinding → ClusterRole/mirrord-developer |
某个 namespace | 赋予开发者在该 namespace 内 create/delete jobs、port-forward 等能力 |
| cluster-scoped 权限 | ClusterRoleBinding → ClusterRole/mirrord-impersonator |
集群范围 | 赋予开发者 impersonate ServiceAccount 的能力(WebSocket 隧道认证必需) |
这么分的原因是:impersonate serviceaccounts 是 cluster-scoped 虚拟资源,namespace-scoped 的 RoleBinding 无法满足(这条 cluster-scoped 约束的完整原委,见 《VS Code 跑 mirrord 撞上 WebSocket 403》)。
当前的脚本式操作流程#
现有的实现中(这套脚本和按 namespace 授权的 demo 来自前一篇 《给 mirrord 开发者按 namespace 签发 kubeconfig》),管理员手工运行:
# 第 1 步:为开发者生成身份凭证
bash rbac/admin/scripts/issue-developer-kubeconfig.sh alice
# 第 2 步:授予 namespace 权限
bash rbac/admin/scripts/grant-namespace-access.sh alice team-a-dev
# 第 3 步:收回权限时
bash rbac/admin/scripts/revoke-namespace-access.sh alice team-a-dev
这些脚本做的本质上就是:
- 生成证书 + kubeconfig
- 创建
RoleBinding和ClusterRoleBinding - 删除这些 binding
问题:
- 授权变更不经过代码审查
- 无法追踪谁在什么时间做了什么
- 无法轻松回滚
- 分布在多个脚本中,容易遗漏
方案:按用户维护 RBAC 清单#
如果你想让 mirrord 授权管理变成 GitOps,比较直接的做法是 把授权关系写成声明式的 YAML,存进 Git,让 Argo CD 或 Flux 去 reconcile。
核心思路#
每个开发者的授权 = 一对 binding YAML 文件 + git 里的声明
# rbac/live/alice.yaml
---
# RoleBinding: alice 在 team-a-dev 里能做什么
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: mirrord-developer-alice
namespace: team-a-dev
labels:
mirrord-rbac-demo/user: alice
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: mirrord-developer
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: alice # ← 身份来自 kubeconfig
---
# ClusterRoleBinding: alice 能否 impersonate serviceaccounts
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: mirrord-impersonator-alice
labels:
mirrord-rbac-demo/user: alice
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: mirrord-impersonator
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: alice
目录结构建议#
mirrord-demo/
├── rbac/
│ ├── base/
│ │ ├── 01-mirrord-developer-clusterrole.yaml # 基础定义(不变)
│ │ ├── 01b-mirrord-impersonator-clusterrole.yaml # 基础定义(不变)
│ │ └── kustomization.yaml
│ ├── live/
│ │ ├── alice.yaml # alice 的权限绑定
│ │ ├── bob.yaml # bob 的权限绑定
│ │ ├── charlie.yaml # charlie 的权限绑定
│ │ └── kustomization.yaml # 声明所有 user YAML
│ ├── admin/
│ │ ├── scripts/
│ │ │ ├── issue-developer-kubeconfig.sh # 保留:生成 kubeconfig(一次性)
│ │ │ └── lib.sh
│ │ └── manifests/
│ │ ├── 02-namespaces.yaml
│ │ ├── 03-test-workloads.yaml
│ │ └── ...
│ ├── developer/
│ │ └── scripts/
│ │ ├── whoami.sh
│ │ └── run-mirrord.sh
│ └── validate-rbac.sh
├── .argocd/ # ← 新增:Argo CD 配置
│ └── mirrord-rbac-app.yaml
└── ...
工作流变化#
原来的流程(脚本驱动):
管理员 → 手工运行脚本 → 直接创建 binding → 无审计
新的流程(GitOps 驱动):
开发者或管理员 → PR 修改 rbac/live/alice.yaml →
评审通过 → merge →
Argo CD 自动检测变化 →
应用到集群 →
✅ alice 获得权限
撤销权限也是同样的方式:
管理员 → PR 删除或修改 rbac/live/alice.yaml →
评审 → merge →
Argo CD 自动 sync →
删除 binding →
✅ alice 权限回收
按用户维护 RBAC 的具体步骤#
第 1 步:生成 kubeconfig(一次性,脚本不变)#
开发者身份来自客户端证书。这部分仍然使用现有脚本(CSR + API 签名):
# 管理员执行
bash rbac/admin/scripts/issue-developer-kubeconfig.sh alice
# 输出:rbac/.credentials/alice.kubeconfig
# 这个文件包含:
# - alice.crt(签好的证书)
# - alice.key(私钥)
# - 集群 CA 和 server 地址
# 把 kubeconfig 安全地发给 alice(不走 Git)
安全建议:
- 不要把 kubeconfig 提交进 Git
- 不要把
alice.key放进 Git - 通过 1password、vault 或 secure email 分发
- 或者更推荐:让开发者自己生成 CSR,管理员只签名
.crt(下文详述)
第 2 步:维护 Git 中的授权清单#
根据需要修改 rbac/live/alice.yaml 中的 RoleBinding 和 ClusterRoleBinding。
示例 1:授予 alice 在 team-a-dev 的权限
编辑或创建 rbac/live/alice.yaml:
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: mirrord-developer-alice
namespace: team-a-dev
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: mirrord-developer
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: alice
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: mirrord-impersonator-alice
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: mirrord-impersonator
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: alice
提交 PR:
git add rbac/live/alice.yaml
git commit -m "feat(rbac): grant alice access to team-a-dev namespace"
git push origin feat/alice-team-a-dev
示例 2:授予 alice 在多个 namespace 的权限
如果 alice 需要访问 team-a-dev 和 team-b-dev,把多个 RoleBinding 写在同一个文件里:
---
# team-a-dev
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: mirrord-developer-alice
namespace: team-a-dev
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: mirrord-developer
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: alice
---
# team-b-dev
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: mirrord-developer-alice
namespace: team-b-dev
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: mirrord-developer
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: alice
---
# Cluster-scoped impersonation(只需一个)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: mirrord-impersonator-alice
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: mirrord-impersonator
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: alice
第 3 步:用 Kustomize 或 Helm 管理多个用户#
如果用户很多,手工维护每个文件会很繁琐。可以用 Kustomize 或 Helm 生成。
方案 A:Kustomize(简单)#
创建 rbac/live/kustomization.yaml:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
bases:
- ../base
resources:
- alice.yaml
- bob.yaml
- charlie.yaml
commonLabels:
app.kubernetes.io/part-of: mirrord-rbac
方案 B:Helm(更灵活)#
创建一个 Helm Chart,从 values.yaml 中读取用户列表:
# helm/values.yaml
users:
- name: alice
namespaces:
- team-a-dev
- name: bob
namespaces:
- team-b-dev
- name: charlie
namespaces:
- team-a-dev
- team-b-dev
模板 helm/templates/rolebinding.yaml:
{{- range .Values.users }}
---
{{- range .namespaces }}
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: mirrord-developer-{{ .name }}
namespace: {{ . }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: mirrord-developer
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: {{ .name }}
{{- end }}
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: mirrord-impersonator-{{ .name }}
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: mirrord-impersonator
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: {{ .name }}
{{- end }}
第 4 步:配置 Argo CD 自动同步#
创建 .argocd/mirrord-rbac-app.yaml:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: mirrord-rbac
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/meirongdev/mirrord-demo
targetRevision: main
path: rbac/live
destination:
server: https://kubernetes.default.svc
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
或如果用 Helm 方案:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: mirrord-rbac
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/meirongdev/mirrord-demo
targetRevision: main
path: helm
helm:
releaseName: mirrord-rbac
values: |
users:
- name: alice
namespaces:
- team-a-dev
- name: bob
namespaces:
- team-b-dev
destination:
server: https://kubernetes.default.svc
syncPolicy:
automated:
prune: true
selfHeal: true
用户身份的管理#
按用户维护 RBAC 的前提是,你有一个稳定的用户身份方案。有两种选择:
选项 1:管理员生成 kubeconfig(当前方案)#
bash rbac/admin/scripts/issue-developer-kubeconfig.sh alice
# → rbac/.credentials/alice.kubeconfig
优点:
- 简单,一命令搞定
缺点:
- 私钥在管理员手中(安全风险)
- 开发者无法更新或轮换密钥
- kubeconfig 泄露时,管理员要手工重新签发
选项 2:开发者自己生成,管理员只签名(推荐)#
流程:
-
开发者本地生成私钥 + CSR
openssl genrsa -out alice.key 2048 openssl req -new -key alice.key -out alice.csr -subj "/CN=alice" # 把 alice.csr 提交给管理员(或放进 issue/PR) -
管理员验证 + 签名
# 验证身份(是真的 alice 吗?) # 然后: kubectl apply -f - <<EOF apiVersion: certificates.k8s.io/v1 kind: CertificateSigningRequest metadata: name: mirrord-rbac-demo-alice spec: request: $(base64 < alice.csr | tr -d '\n') signerName: kubernetes.io/kube-apiserver-client usages: - client auth EOF kubectl certificate approve mirrord-rbac-demo-alice kubectl get csr mirrord-rbac-demo-alice -o jsonpath='{.status.certificate}' | base64 --decode > alice.crt⚠️ 关于
spec.usages:在certificates.k8s.io/v1(GA 之后)里这是必填字段,漏写会被 API server 直接拒掉(spec.usages: Required value)。对kubernetes.io/kube-apiserver-client这个 signer,客户端证书认证只需要client auth;写server auth之类会被签发控制器拒签。另外提醒两个 heredoc 的坑:分隔符要用不带引号的
<<EOF(写成<< 'EOF'会关闭$(...)替换,request字段会塞进字面字符串导致illegal base64 data);base64后面的| tr -d '\n'不能省(Linux 的base64会按 76 列折行,证书数据必须是单行)。 -
开发者收到证书,自己拼 kubeconfig
cat > alice.kubeconfig <<EOF apiVersion: v1 kind: Config clusters: - name: mirrord-rbac-demo cluster: server: https://127.0.0.1:6443 certificate-authority-data: <CA 证书> users: - name: alice user: client-certificate-data: $(base64 < alice.crt | tr -d '\n') client-key-data: $(base64 < alice.key | tr -d '\n') contexts: - name: alice@mirrord-rbac-demo context: cluster: mirrord-rbac-demo user: alice current-context: alice@mirrord-rbac-demo EOF chmod 600 alice.kubeconfig
优点:
- 私钥永不离开开发者机器
- 开发者可以自己轮换密钥(重新生成 CSR)
- 符合 PKI 最佳实践
缺点:
- 多一步操作
权限防护和最小权限原则#
mirrord-developer ClusterRole 包含什么#
现有的 mirrord-developer ClusterRole 授予的权限尽量做到最小化,只包括 mirrord 必需的操作:
- apiGroups: [""]
resources: ["pods", "services", "configmaps", "secrets", "endpoints"]
verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
resources: ["deployments", "statefulsets", "daemonsets", "replicasets"]
verbs: ["get", "list", "watch"]
- apiGroups: ["batch"]
resources: ["jobs"]
verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["create", "delete"]
- apiGroups: [""]
resources: ["pods/ephemeralcontainers", "pods/log", "pods/portforward"]
verbs: ["update", "patch", "get", "create"]
用户不能做什么#
有了这个权限集,开发者 alice 在 team-a-dev namespace 中:
❌ 不能删除 Deployment
→ 没有 deployments: delete 权限
❌ 不能删除 MySQL Pod(由 Deployment 管理)
→ 如果尝试 kubectl delete pod mysql-xxxxx -n team-a-dev,API 服务器返回 403
❌ 不能修改 ConfigMap
→ 只有 configmaps: get, list, watch,没有 create, update, patch, delete
❌ 不能访问 team-b-dev namespace
→ RoleBinding 只在 team-a-dev 有效
❌ 不能 impersonate 其他用户
→ impersonate users 需要 cluster-scoped 权限,而且也没有
验证权限#
开发者可以检查自己有什么权限:
export KUBECONFIG=rbac/.credentials/alice.kubeconfig
# 看自己的身份
kubectl auth whoami
# Output: alice
# 看自己在 team-a-dev 能做什么
kubectl auth can-i get pods -n team-a-dev # yes
kubectl auth can-i delete deployments -n team-a-dev # no
kubectl auth can-i list secrets -n team-a-dev # yes
kubectl auth can-i impersonate users -n team-a-dev # no
# 看自己在 team-b-dev 能做什么
kubectl auth can-i get pods -n team-b-dev # no (no binding)
一个完整的使用示例#
初始化(管理员)#
-
部署基础设施
kubectl apply -f rbac/base/ # 创建 mirrord-developer 和 mirrord-impersonator ClusterRole -
为 alice 生成 kubeconfig
bash rbac/admin/scripts/issue-developer-kubeconfig.sh alice # 输出:rbac/.credentials/alice.kubeconfig -
创建初始 RBAC 清单
cat > rbac/live/alice.yaml <<EOF apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: mirrord-developer-alice namespace: team-a-dev roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: mirrord-developer subjects: - apiGroup: rbac.authorization.k8s.io kind: User name: alice --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: mirrord-impersonator-alice roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: mirrord-impersonator subjects: - apiGroup: rbac.authorization.k8s.io kind: User name: alice EOF -
配置 Argo CD
kubectl apply -f .argocd/mirrord-rbac-app.yaml
使用(开发者)#
export KUBECONFIG=rbac/.credentials/alice.kubeconfig
# 查看自己能访问什么
bash rbac/developer/scripts/whoami.sh
# 运行 mirrord
bash rbac/developer/scripts/run-mirrord.sh
权限变更(管理员 + GitOps)#
场景:alice 需要访问 team-b-dev
-
编辑
rbac/live/alice.yaml,添加 team-b-dev 的 RoleBinding -
提交 PR
git add rbac/live/alice.yaml git commit -m "feat(rbac): grant alice access to team-b-dev" git push -
评审 + merge
-
Argo CD 自动同步
# Argo CD 检测到 rbac/live/alice.yaml 变化 # 自动 kubectl apply # alice 很快获得 team-b-dev 访问权(取决于 Argo CD sync 周期)
验证
kubectl auth can-i get pods -n team-b-dev --as alice
# yes
权限回收(管理员 + GitOps)#
场景:alice 离职,收回所有权限
- 删除
rbac/live/alice.yaml - 提交 PR + merge
- Argo CD 自动删除所有 alice 的 binding
- 完成
注意:签发后无法「吊销证书」,要回收的是授权#
这里有个容易踩的认知误区。当你已经 kubectl certificate approve 之后,想「反悔」时会发现:
- approve 不可逆:CSR 的
Approved/Denied是一次性、互斥的终态。一旦 approve,签发控制器会立刻把证书写进.status.certificate,没法再改成 Denied,也没有「反 approve」。 - 删掉 CSR 对象 ≠ 吊销证书:
kubectl delete csr <name>只删集群里那个资源对象,已经签发出去的.crt仍然有效到过期。Kubernetes 的客户端证书认证没有 CRL / 吊销机制——这是它一个有名的限制。
所以真正要「取消」的不是身份(证书决定 CN=alice 是谁),而是授权(RBAC binding 决定 alice 能干什么)。删掉 binding 后,这份证书还能登录、kubectl auth whoami 还认得出 alice,但任何操作都会 403——这才是有效的回收,也正是上面 GitOps 流程在做的事。
| 想做的事 | 是否有效 | 说明 |
|---|---|---|
| 把 approve 改成 deny | ❌ | condition 终态,不可逆 |
kubectl delete csr |
⚠️ 没用 | 只删对象,已签发证书照样有效到过期 |
删 RoleBinding / ClusterRoleBinding |
✅ | 身份还在但没权限,实际等于回收(推荐) |
| 轮换集群 CA | ✅ 但是核弹 | 让所有用户证书一起失效,影响整个集群 |
| 等证书过期 | ✅ | 取决于签发时设的有效期 |
这也反过来说明了「证书有效期别设太长」和「用短期凭证 + 可自助轮换」的价值——既然没法吊销,过期就是唯一的兜底。
对比:脚本式 vs GitOps 式#
| 维度 | 脚本式 | GitOps 式 |
|---|---|---|
| 审计 | ❌ 无法追踪谁改了什么 | ✅ Git log + PR review |
| 回滚 | ❌ 手工脚本,容易出错 | ✅ git revert 一行搞定 |
| 一致性 | ❌ 人工易出错 | ✅ 自动 reconcile |
| 团队协作 | ❌ 需要专人操作 | ✅ 任何有权限的人都能提 PR |
| 可观测性 | ❌ 脚本执行无记录 | ✅ Argo CD UI 可视化 |
| 灾备 | ❌ 无 | ✅ Git 是完整的恢复源 |
未来:其他授权方式#
按用户维护 RBAC 是比较直接的做法,适合:
- 用户数少(< 50)
- 权限关系简单(基本是"是或否")
- 团队规模不大
但当团队或环境增长时,还有其他方向:
-
按 Group 授权(下一篇重点)
- 用户加入/移出组,由 IdP(OIDC/LDAP)管理
- Git 里只维护"group → namespace"关系
- 优点:权限清单爆炸性增长被避免
-
按租户或团队授权
- 每个团队一套权限模板
- 新人只需分配到团队,权限自动从团队继承
- 需要 Helm 或 Kustomize 的二阶模板
-
OIDC + RBAC 绑定
- 集群配置 OIDC provider
- 用户身份直接来自 OIDC,不需要 CSR
- 权限仍然用 RBAC,但
subjects.name来自 OIDC 的subclaim
-
外部授权系统(如 Keycloak + Gatekeeper)
- 在 API gateway 层集中管理权限
- RBAC 作为二层防护
- 适合大规模多集群场景
本文只讨论按用户的方式,是比较 straightforward 的 GitOps 起点。
总结#
通过把 mirrord RBAC 管理转换成 声明式的 Git YAML,你获得:
- ✅ 可审计:每个权限变更都有 PR 和 commit 记录
- ✅ 可回滚:发现问题直接
git revert - ✅ 自动化:Argo CD 负责 reconcile,无需手工脚本
- ✅ 透明:团队能看到谁有什么权限
- ✅ 安全:最小权限原则,默认拒绝
启动步骤:
- 保留现有的 kubeconfig 生成脚本(或改成开发者自生 CSR)
- 把
RoleBinding+ClusterRoleBinding写成 YAML - 放进
rbac/live/目录 - 配置 Argo CD 或 Flux 自动同步
- 后续权限变更走 PR 流程
这就是一个小而完整的、生产级的 mirrord 用户管理系统。
相关阅读(本系列)#
- 《用 mirrord 把本地进程接入 K8s 集群》 — mirrord 是什么、完整 demo
- 《给 mirrord 开发者按 namespace 签发 kubeconfig》 — 本篇 GitOps 化的脚本前传
- 《VS Code 跑 mirrord 撞上 WebSocket 403》 — 为什么需要 cluster-scoped impersonator binding