Kubernetes—RBAC

295 阅读5分钟

RBAC

在互联网级别的大规模集群中,Kubernetes内置的编排对象,很难做到满足所有需求。所以,很多实际的容器化工作,都会要求你设计一个自己的编排对象,事先自己的控制器模式。在Kubernetes中,我们可以通过CRD机制来完成这种工作,但是,在Kubernetes里新增和操作API对象,就必须了解RBAC。Kubernetes中所有的API对象都是保存在etcd中,对这些API对象的操作都是通过访问api-server实现的。你需要API Server来帮助你做授权操作。

在Kubernetes中,负责完成授权工作的机制,就是RBAC:基于角色的访问控制。

RBAC中的三个基本概念:

  • Role:角色,就是一组规则,定义了一组对Kubernetes API对象的操作权限
  • Subject:被作用者,既可以是人,也可以是机器,也可以是你在Kubernetes中定义的用户
  • RoleBinding:定义了“被作用者”和“角色”的绑定关系

Role

Role本身就是一个Kubernetes的API对象,定义如下:

kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
 namespace: mynamespace
 name: example-role
rules:
- apiGroups: [""]
  resources: ["pod"]
  verbs: ["get","watch","list"]

这个Role对象指定了它能产生作用的Namespace是mynamespace

rules字段就是它所定义的规则,上面的例子中,就是允许被作用者对mynamespace下的Pod对象,进行Get、WATCH和LIST操作

RoleBinding

RoleBinding本身也是一个Kubernetes的API对象,定义如下:

kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
 name: example-rolebinding
 namespace: mynamespace
subjects:
- kind: User
  name: example-user
  apiGroup: rbac.authorization.k8s.io
roleRef:
 kind: Role
 name: example-role
 apiGroup: rbac.authorization.k8s.io

RoleBinding对象可以直接通过名字,来引用前面定义的Role对象,从而定义了被作用者和角色之间的关系

RoleBinding和Role对象都是Namespaced对象,它们对权限的限制规则仅在它们自己的Namespace内有效,roleRef也只能引用当前Namespace里的Role对象

ClusterRole和ClusterRoleBinding

对于非Namespaced对象(比如Node)或者一个Role想作用于所有的Namespace的时候,我们可以使用ClusterRole和ClusterRoleBinding做授权。这两个API对象的用法跟Role和RoleBinding完全相同,只不过没有了namespace字段

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
 name: example-cluster
rule:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get","watch","list"]
  
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
 name: cluster-rolebinding
subjects:
- kind: User
  name: example-user
  apiGroup: rbac.authorization.k8s.io
roleRef:
 kind: ClusterRole
 name: example-clusterrole
 apiGroup: rbac.authorization.k8s.io

上面的例子中ClusterRole和ClusterRoleBinding的组合,意味着名叫example-role的用户,拥有对所有Namespace的Pod进行get、watch、list操作的权限。

如果在Role或者ClusterRole中,如果要赋予用户example-user所有权限,那可以给它指定一个verbs字段的全集

verbs: ["get","watch","list","update","create","patch","delete"]

ServiceAccount

在大多数时候,我们都不太使用“用户”这个功能,而是使用Kubernetes里的“内置用户”

首先,我们定义一个ServiceAccount。

apiVersion: v1
kind: ServiceAccount
metadata:
 namespace: mynamespace
 name: example-sa

然后我们通过Role和RoleBinding的YAML文件,为这个ServiceAccount分配权限:

kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
 name: example-role
 namespace: mynamespace
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get","watch","list"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
 name: example-rolebinding
 namespace: mynamespace
subjects:
- kind: ServiceAccount
  name: example-sa
  namespace: mynamespace
roleRef:
 kind: Role
 name: example-role
 apiGroup: rbac.authorization.k8s.io

操作演示

我们先创建上面三个对象

[root@host1 rbac]# kubectl create namespace mynamespace
namespace/mynamespace created
[root@host1 rbac]# kubectl get namespace
NAME          STATUS   AGE
default       Active   2d19h
kube-public   Active   2d19h
kube-system   Active   2d19h
mynamespace   Active   5s
rook-ceph     Active   42h
[root@host1 rbac]# kubectl create -f svc-account.yaml 
serviceaccount/example-sa created
[root@host1 rbac]# kubectl create -f role-binding.yaml 
role.rbac.authorization.k8s.io/example-role created
rolebinding.rbac.authorization.k8s.io/example-rolebinding created

然后,我们查看一下这个ServiceAccount的详细信息

[root@host1 rbac]# kubectl get sa -n mynamespace -o yaml
apiVersion: v1
items:
- apiVersion: v1
  kind: ServiceAccount
  metadata:
    creationTimestamp: "2019-10-25T02:15:24Z"
    name: default
    namespace: mynamespace
    resourceVersion: "474819"
    selfLink: /api/v1/namespaces/mynamespace/serviceaccounts/default
    uid: 4da5a943-f6cd-11e9-928f-000c296c79f3
  secrets:
  - name: default-token-ncwxl
- apiVersion: v1
  kind: ServiceAccount
  metadata:
    creationTimestamp: "2019-10-25T02:15:46Z"
    name: example-sa
    namespace: mynamespace
    resourceVersion: "474876"
    selfLink: /api/v1/namespaces/mynamespace/serviceaccounts/example-sa
    uid: 5aeef1d2-f6cd-11e9-928f-000c296c79f3
  secrets:
  - name: example-sa-token-vg2sk
kind: List
metadata:
  resourceVersion: ""
  selfLink: ""

Kubernetes会为每一个ServiceAccount自动创建并分配一个Secret对象,即Secrets字段

这个Secret,就是这个ServiceAccount用来和API Server进行交互的授权文件,我们称它为:ServiceAccount Token.

这时候,用户的Pod,就可以声明使用这个ServiceAccount了,比如下面这个例子:

apiVersion: v1
kind: Pod
metadata:
 namespace: mynamespace
 name: sa-token-test
spec:
 containers:
 - name: nginx
   image: nginx:1.7.9
serviceAcccountName: example-sa

在Pod中,我们指定了ServiceAccountName为example-sa

这个Pod运行起来之后,该ServiceAccount的token就会被自动挂载到容器的/var/run/secrets/kubernetes.io/serviceaccount目录下。

如果一个Pod没有声明ServiceAccountName,Kubernetes会自动在它的Namespace下创建一个名叫default的默认ServiceAccount,然后分配给这个Pod。这个默认的ServiceAccount没有关联任何Role。也就是说,此时它有访问API Server的绝大多数权限。

Kubernetes中的用户组

除了前面使用的“用户”,Kubernetes还有用户组的概念,也就是一组用户的意思。如果你为Kubernetes配置了外部认证服务的话,这个用户组的概念由外部认证服务提供。

但是对于Kubernetes内置用户ServiceAccount来说,上述用户组也同样使用。

实际上,一个ServiceAccount,在Kubernetes里对应的“用户”名字是:

system:serviceaccount:<ServiceAccount Name>

而它对应的内置“用户组”的名字就是:

system:serviceaccounts:<Namespace Name>

比如,我们可以在RoleBinding定义如下的subjects:

subjects:
- kind: Group
  name: system:serviceaccounts:mynamespace
  apiGroup: rbac.authorization.k8s.io

这就意味着这个Role的权限规则,作用于mynamespace里的所有ServiceAccount。

再比如下面的例子

subjects:
- kind: Group
  name: system:serviceaccounts
  apiGroup: rbac.authorization.k8s.io

这意味着这个Role的权限规则,作用于整个系统里的所有ServiceAccount

Kubernetes提供了四个预先定义好的ClusterRole来供用户直接使用:

  1. cluster-admin(Kubernetes中的最高权限)
  2. admin
  3. edit
  4. view