Argo CD 的权限不是集群权限
一句话总结
在 Argo CD 中,谁能做什么不是由 Kubernetes RBAC 决定,而是由 argocd-rbac-cm 中的一份 policy.csv 决定;
这一判定无需服务器,用 argocd admin settings rbac 就能当场确认。
为什么需要这种区分
启用 GitOps 的那一刻,真正修改集群的不再是人,而是 Argo CD。Argo CD 的控制器对其管理的命名空间拥有非常大的权限—— 只有这样,清单里写的任何内容它才都能创建。陷阱就出在这里:在集群里没有任何权限的人,只要拿到一个 Argo CD 账号, 他点击的同步就会以 Argo CD 的权限执行。Kubernetes RBAC 从头到尾都看不到这个人。
因此 Argo CD 另设了一张自己的权限表,就是 argocd-rbac-cm ConfigMap 中的 policy.csv 键,
格式是 casbin 的策略 CSV。行只有两种。
p, <주체>, <자원>, <동작>, <객체>, allow|deny
g, <사용자 또는 그룹>, <역할>
p 是一行权限,g 是一行归属。资源是 applications、applicationsets、projects、clusters、
repositories、certificates、gpgkeys、logs 这样的名称,操作则因资源而异——对应用来说,
支持 get、create、update、delete、sync、override,还支持指向资源操作的
action/<그룹>/<종류>/<이름>(占位符依次为组、类型、名称)形式。对象在应用的情况下是 <프로젝트>/<앱이름>(占位符依次为项目、应用名称)两个字段。
dev/* 是 dev 项目的所有应用,*/* 则是全部。这里最常见的错误,就是在对象中只写应用名称——web 与任何应用都匹配不上。
工作原理
判定有两个特性。第一,deny 与行的顺序无关,总是获胜。 把 allow 写在上面、deny 写在下面,
或者反过来,结果都一样。这与采用首条匹配规则的防火墙语法不同,用那种习惯去读就会读错。
第二,没有命中任何一行的请求,会按 policy.default 指定的角色重新判定。 在这里写上
role:readonly,只有账号的人就只能读取;值为空,则什么都做不了。
除了全局策略,还有只在项目内部生效的角色。在 AppProject 的 spec.roles 中写下名称、策略行,
以及要绑定的组。主体名称是 proj:<프로젝트>:<역할>(占位符依次为项目、角色),语法与全局策略完全相同。
区别在于它存放的位置——全局策略是平台团队持有的 ConfigMap,而项目角色可以由拥有该项目的团队在自己的清单里修改。
团队越多,这条边界就越能实实在在地减少工作量。
最后是让这次实验得以进行的工具。argocd admin settings rbac validate 和
argocd admin settings rbac can 不会连接 Argo CD 服务器。 它们只读取通过 --policy-file 给出的文件来判定。
因此可以把策略当作代码来处理,并在每次修改时运行期望表做回归检查。can 默认是严格模式,
如果给出不存在的资源名称或操作名称,会在判定之前停下并告知——
这就彻底省去了把写错的策略部署出去之后再去琢磨“为什么不行”的时间。
在现场相遇的样子
事故通常是这样发生的。值班人员在夜里同步了支付应用,而这次同步把一个错误的提交推了上去。事后查看,
策略里只有 p, role:oncall, applications, sync, prod/*, allow 这一行。支付是另一个团队的应用,
但 prod/* 这一个字段把它也覆盖了。修复方法很简单——再加一行针对 prod/payments 的
deny,无论顺序如何都会被拦住。困难的是修复之后,要知道别的东西有没有被弄坏,
而这只能通过运行期望表来确认。
反方向的事故同样常见。收紧权限之后,部署自动化悄悄停了。比如 CI 账号用的操作
不是 sync,而是 action/apps/Deployment/restart;或者对象只写了 * 一个字段,
必须改成 */*。越是收紧的变更,越需要这张表。没有这张表,就没有人会去收紧,
权限只会越来越宽。
本实验环境的局限
实验 Pod 中既没有 Argo CD 控制器,也没有 API 服务器。 因此无法做到“以这个人登录并点击按钮试试”。 不过,真正解释策略的那段代码就原样包含在 CLI 里,所以判定结果与服务器给出的结果走的是同一条路径。 启动真正的控制器、观察同步运转的实验,另外放在认证课程部分。
下一项实验要做什么
写一份策略,检查语法,再用 rbac can 询问八个问题。故意漏掉一个字段来查看错误,
把 deny 写在上面试一试,并用 policy.default 决定给陌生人什么权限。把项目角色放进 AppProject,
部署到 kwok 集群,最后制作期望表和检查脚本,一次全部运行。