Kubernetes 学习笔记(十六):ServiceAccount 和 RBAC 如何控制权限
Han Lv5

前言

前面整理到这里,应用已经能运行、能暴露、能读取配置,也能做基本排障。接下来我想把视角切到安全上。

第一个问题是:Pod 里的程序能不能访问 Kubernetes API?

比如一个应用想读取同 namespace 下的 ConfigMap,或者一个控制器想监听 Pod 变化。它访问 API Server 时,总要有一个身份。

在 Kubernetes 里,给程序用的身份通常是 ServiceAccount。

1
2
3
4
5
人访问集群
-> User / Group

Pod 里的程序访问集群
-> ServiceAccount

有了身份之后,还要决定这个身份能做什么。这部分通常由 RBAC 控制。

1
2
3
4
5
6
7
8
ServiceAccount
表示“谁”

Role / ClusterRole
表示“能做什么”

RoleBinding / ClusterRoleBinding
表示“把权限给谁”

我目前把它理解成:ServiceAccount 是工牌,Role 是权限清单,RoleBinding 是把权限清单发给某张工牌。

default ServiceAccount

每个 Namespace 默认都会有一个叫 default 的 ServiceAccount。

1
kubectl get serviceaccount -n demo-dev

如果创建 Pod 时不指定 ServiceAccount,Pod 通常会使用当前 Namespace 的 default

1
2
3
4
Pod 没写 serviceAccountName
|
v
使用 default ServiceAccount

这点容易被忽略。不是没写身份就没有身份,而是用了默认身份。

一个明确指定 ServiceAccount 的 Pod:

1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: Pod
metadata:
name: demo-api
namespace: demo-dev
spec:
serviceAccountName: demo-api
containers:
- name: demo-api
image: example/demo-api:1.0.0

我更倾向给重要应用单独创建 ServiceAccount,而不是所有应用都混用 default

1
2
3
demo-api -> demo-api ServiceAccount
worker -> worker ServiceAccount
operator -> operator ServiceAccount

这样后面分配权限时才清楚。

ServiceAccount token

Pod 使用 ServiceAccount 后,Kubernetes 会让 Pod 拥有对应身份的凭据。应用如果访问 API Server,就可以拿这个身份认证。

可以粗略理解成:

1
2
3
4
5
6
7
ServiceAccount
|
v
token
|
v
Pod 内程序拿 token 访问 API Server

现代 Kubernetes 更推荐使用有边界、可过期的 TokenRequest 机制,而不是长期静态 token。作为入门理解,我先记住两个边界:

  • ServiceAccount 是身份;
  • token 是这个身份访问 API Server 时用到的凭据。

如果某个 Pod 根本不需要访问 Kubernetes API,可以关闭自动挂载:

1
2
3
4
5
6
7
8
9
apiVersion: v1
kind: Pod
metadata:
name: demo-api
spec:
automountServiceAccountToken: false
containers:
- name: demo-api
image: example/demo-api:1.0.0

这就像一个普通同学只来上自习,不需要进资料室,就没必要给他额外门禁。

RBAC 的四个对象

RBAC 常见四个对象:

1
2
3
4
5
6
7
8
9
10
11
Role
namespace 内权限清单

ClusterRole
集群级权限清单,也可以被绑定到 namespace

RoleBinding
在 namespace 内把 Role 或 ClusterRole 绑定给主体

ClusterRoleBinding
在集群范围把 ClusterRole 绑定给主体

先看最常见的 namespace 内授权:

1
2
3
4
5
6
7
8
9
10
ServiceAccount: demo-api
|
v
RoleBinding
|
v
Role: read-configmap
|
v
允许读取 ConfigMap

这条线是:

1
2
3

通过绑定
获得什么权限

Role:描述能做什么

假设只允许读取 ConfigMap:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: read-configmap
namespace: demo-dev
rules:
- apiGroups:
- ""
resources:
- configmaps
verbs:
- get
- list
- watch

这份 Role 可以翻译成:

1
2
在 demo-dev namespace 内
允许对 configmaps 执行 get/list/watch

几个字段这样读:

1
2
3
4
5
6
7
8
apiGroups
资源属于哪个 API 组,核心资源通常是空字符串

resources
哪类资源,例如 pods、configmaps、secrets

verbs
能做什么动作,例如 get、list、watch、create、update、delete

它只是权限清单,还没有发给任何人。

RoleBinding:把权限给某个身份

RoleBinding 负责把 Role 绑定给 ServiceAccount。

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: demo-api-read-configmap
namespace: demo-dev
subjects:
- kind: ServiceAccount
name: demo-api
namespace: demo-dev
roleRef:
kind: Role
name: read-configmap
apiGroup: rbac.authorization.k8s.io

可以翻译成:

1
2
把 read-configmap 这个 Role
绑定给 demo-dev namespace 下的 demo-api ServiceAccount

关系图:

1
2
3
RoleBinding
├─ subject: demo-api ServiceAccount
└─ roleRef: read-configmap Role

到这里,Pod 如果使用 demo-api 这个 ServiceAccount,就能读取 demo-dev 里的 ConfigMap。

ClusterRole 和 ClusterRoleBinding

Role 是 namespace 内的权限。ClusterRole 是集群级权限清单。

例如读取节点:

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: read-nodes
rules:
- apiGroups:
- ""
resources:
- nodes
verbs:
- get
- list
- watch

Node 是集群级资源,不属于某个 namespace,所以通常要用 ClusterRole。

ClusterRoleBinding 会把权限授予整个集群范围:

1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: demo-api-read-nodes
subjects:
- kind: ServiceAccount
name: demo-api
namespace: demo-dev
roleRef:
kind: ClusterRole
name: read-nodes
apiGroup: rbac.authorization.k8s.io

这个权限范围更大,要更谨慎。

1
2
3
4
5
RoleBinding
通常限制在一个 namespace

ClusterRoleBinding
直接影响集群范围

ClusterRole 也可以绑定到 namespace

ClusterRole 不一定只能通过 ClusterRoleBinding 使用。它也可以被 RoleBinding 绑定到某个 Namespace。

1
2
3
4
5
ClusterRole
描述一份可复用权限清单

RoleBinding
只在 demo-dev namespace 中使用这份权限

例如 Kubernetes 内置有一些常见 ClusterRole。通过 RoleBinding 绑定时,权限仍然限定在 RoleBinding 所在的 Namespace。

这个设计一开始有点绕,但好处是可以复用权限模板:

1
2
3
4
同一个 ClusterRole: view
|
├─ RoleBinding 到 dev
└─ RoleBinding 到 test

最小权限原则

RBAC 最重要的习惯是最小权限。

比如应用只需要读 ConfigMap,就不要给它:

1
2
3
4
secrets get/list
pods delete
deployments update
cluster-admin

权限应该像借工具:

1
2
3
4
5
需要螺丝刀
就借螺丝刀

不要因为省事
把整间工具房钥匙给出去

我会尽量避免这些写法:

1
2
3
4
verbs:
- "*"
resources:
- "*"

除非非常明确知道边界,否则通配符会让权限变得很难审计。

怎么检查权限

可以用 kubectl auth can-i 检查某个身份是否有权限。

检查当前用户:

1
kubectl auth can-i get pods -n demo-dev

检查某个 ServiceAccount:

1
2
3
kubectl auth can-i get configmaps \
--as=system:serviceaccount:demo-dev:demo-api \
-n demo-dev

ServiceAccount 的完整用户名形式大致是:

1
system:serviceaccount:<namespace>:<name>

这个命令很适合排查:

1
2
3
4
应用访问 API 失败
|
v
先确认 ServiceAccount 有没有对应权限

不要只看 YAML 觉得“我应该配了”,用 can-i 查一下更直接。

小结

这一篇整理下来,我对 ServiceAccount 和 RBAC 的理解可以概括成几句话:

  • ServiceAccount 是给 Pod 或自动化程序使用的集群身份;
  • 每个 Namespace 都有默认的 default ServiceAccount;
  • Pod 可以通过 serviceAccountName 指定身份;
  • 不需要访问 API Server 的 Pod 可以关闭 token 自动挂载;
  • Role 描述 namespace 内权限;
  • ClusterRole 描述集群级或可复用权限;
  • RoleBinding / ClusterRoleBinding 负责把权限绑定给主体;
  • RBAC 要遵循最小权限原则;
  • kubectl auth can-i 适合检查权限是否真的生效。

下一篇准备继续看 SecurityContext。RBAC 管的是“能不能访问 Kubernetes API”,SecurityContext 更关注“容器进程在节点上以什么权限运行”。

参考资料

 评论