使用Golang实现对K8s ServiceAccount权限的一键审计

1次阅读

最快查 serviceaccount rbac 权限用 kubectl auth can-i –list -n ns –serviceaccount=sa,它调用 subjectaccessreview api 真实无缓存;go 中需正确设置 resourceattributes.Namespace(集群资源留空,命名空间资源填对),并检查高危动词和 nonresourceurls。

使用Golang实现对K8s ServiceAccount权限的一键审计

怎么快速查清一个 ServiceAccount 有哪些 RBAC 权限

直接用 kubectl auth can-i --list 最快,但必须指定 --serviceaccount 和命名空间,否则查的是当前用户权限。它背后走的是 K8s 的 subjectaccessreview API,结果真实、无缓存。

常见错误是漏掉 --namespace,比如在 default 里查 monitoring/sa-foo 却不加 -n monitoring,结果返回空——不是没权限,而是查错了地方。

  • 必须显式指定命名空间:kubectl auth can-i --list -n monitoring --serviceaccount=sa-foo
  • 如果 SA 绑定了多个 RoleBindingClusterRoleBindingcan-i 会自动聚合所有权限,不用手动拼
  • 注意:它只反映当前生效的 RBAC 规则,不包含 PodSecurityPolicy(已废弃)、OPA/Gatekeeper 等外部策略控制

Go 里调用 SubjectAccessReview API 获取精确权限判定

想在 Go 程序里复现 kubectl auth can-i 的逻辑,核心就是构造并提交一个 SubjectAccessReview 对象。K8s 官方 client-go 提供了开箱即用的 builder,但容易忽略 ResourceAttributesNamespace 字段的语义:对集群级资源(如 nodes)必须留空,对命名空间级资源(如 pods)必须填对——填错就永远返回 allowed: false

示例中常把 Namespace 写死成 "default",结果审计 kube-system 下的 SA 时全报“拒绝”,其实是字段填错导致请求被当成跨命名空间越权检查。

立即学习go语言免费学习笔记(深入)”;

  • authorizationv1.SubjectAccessReview 类型,别用旧版 v1beta1(1.19+ 已弃用)
  • ResourceAttributes.Namespace:查 secrets 就填对应命名空间;查 clusterroles 就设为空字符串
  • client-go 的 AuthorizationV1().SubjectAccessReviews().Create() 返回对象里,只看 .Status.Allowed,别依赖 .Status.Reason——它可能为空或不准确

一键审计脚本该覆盖哪些典型风险操作

真正在生产环境出事的,往往不是“能不能读 pod”,而是“能不能删 secret”“能不能 bind clusterrole”“能不能 patch node”。所以审计不能只扫 get/list/watch,得重点标出高危动词。

RBAC 中最危险的不是 admin ClusterRole,而是看似普通却带 bindescalateimpersonate 的细粒度权限。比如一个 SA 能 create rolebindings 在任意命名空间,等于能给自己提权。

  • 必须检查的动词:deletedeletecollectionbindescalateimpersonatepatch(尤其对 serviceaccountsroles
  • 必须检查的资源组:rbac.authorization.k8s.io(rolebinding/clusterrolebinding)、certificates.k8s.io(certificatesigningrequests)、authentication.k8s.ioTokenreviews)
  • 别漏掉 nonResourceURLs,比如 /api/healthz —— 这些权限在 can-i --list 里不显示,但 Go 调用 SAR 时要单独构造 NonResourceAttributes

权限聚合后为什么还缺一些实际能力

RBAC 只管“能否发起请求”,不管“请求是否成功”。比如 SA 有 patch nodes 权限,但 K8s 默认禁止 patch node.spec,真正起作用的是 kubelet 的 admission 控制或 NodeRestriction 准入插件。Go 审计脚本如果只查 SAR,就会误判为“可 patch node”,其实发出去必 403。

另一个盲区是 mutating webhook:SA 有 create pods 权限,但若 webhook 拒绝注入 sidecar,Pod 创建仍失败——这和 RBAC 无关,但运维排查时容易归因错误。

  • 审计结果里要明确区分“RBAC 允许”和“K8s 实际允许”,后者需结合集群启用的准入控制器(如 NodeRestrictionPodSecurity)判断
  • customresourcedefinitions 的权限,SAR 能查,但 CRD 自身定义的 scope(Namespaced/Cluster)会影响 Namespace 字段是否必需,容易漏判
  • ServiceAccount 的 automountServiceAccountToken: false 会导致 token 不挂载,此时哪怕 RBAC 全开,容器内也拿不到 token——Go 脚本查 API 是 OK 的,但实际 Pod 用不了
text=ZqhQzanResources