Kubernetes RBAC Detailed
RBAC - 基于角色的访问控制
RBAC使用:rbac.authorization.k8s.io API Group 来实现授权决策,允许管理员通过 Kubernetes API 动态配置策略,要启用RBAC,需要在 apiserver 中添加参数–authorization-mode=RBAC,如果使用的kubeadm安装的集群,1.6 版本以上的都默认开启了RBAC,可以通过查看 Master 节点上 apiserver 的静态Pod定义文件:
1 |
|
如果是二进制的方式搭建的集群,添加这个参数过后,记得要重启 apiserver 服务。
RBAC API 对象
Kubernetes有一个很基本的特性就是它的所有资源对象都是模型化的 API 对象,允许执行 CRUD(Create、Read、Update、Delete)操作(也就是我们常说的增、删、改、查操作),比如下面的这下资源:
- Pods
- ConfigMaps
- Deployments
- Nodes
- Secrets
- Namespaces
上面这些资源对象的可能存在的操作有:
- create
- get
- delete
- list
- update
- edit
- watch
- exec
在更上层,这些资源和API Group 进行关联,比如Pods属于Core API Group,而Deployements属于 apps API Group,要在Kubernetes中进行RBAC的管理,除了上面的这些资源和操作以外,我们还需要另外的一些对象:
- Rule:规则,规则是一组属于不同API Group 资源上的一组操作的集合
- Role 和 ClusterRole:角色和集群角色,这两个对象都包含上面的Rules 元素,二者的区别在于,在Role 中,定义的规则只适用于单个命名空间,也就是和namespace 关联的,而ClusterRole 是集群范围内的,因此定义的规则不受命名空间的约束。另外Role和 ClusterRole在Kubernetes中都被定义为集群内部的API 资源,和Pod、ConfigMap 这些类似,都是集群的资源对象,所以同样的可以使用kubectl相关的命令来进行操作
- Subject:主题,对应在集群中尝试操作的对象,集群中定义了3种类型的主题资源:
- User Account:用户,这是有外部独立服务进行管理的,管理员进行私钥的分配,用户可以使用KeyStone或者Goolge 帐号,甚至一个用户名和密码的文件列表也可以。对于用户的管理集群内部没有一个关联的资源对象,所以用户不能通过集群内部的API 来进行管理
- Group:组,这是用来关联多个账户的,集群中有一些默认创建的组,比如cluster-admin
- Service Account:服务帐号,通过Kubernetes API 来管理的一些用户帐号,和namespace 进行关联的,适用于集群内部运行的应用程序,需要通过API 来完成权限认证,所以在集群内部进行权限操作,都需要使用到 ServiceAccount
- RoleBinding和 ClusterRoleBinding:角色绑定和集群角色绑定,简单来说就是把声明的Subject和Role 进行绑定的过程(给某个用户绑定上操作的权限),二者的区别也是作用范围的区别:RoleBinding只会影响到当前namespace 下面的资源操作权限,而ClusterRoleBinding会影响到所有的 namespace。
示例
通过如下示例来演示RBAC的配置方法:
创建一个只能访问某个 namespace 的用户
创建一个 User Account,只能访问 kube-system这个命名空间:
- username: martin
- group: op
第一步:创建用户凭证
Kubernetes没有User Account的API 对象,不过要创建一个用户帐号的话也是挺简单的,利用管理员分配的一个私钥就可以创建了。
创建方法有两种:
1. 使用OpenSSL证书来创建User;
- 给用户martin创建一个私钥,命名成:martin.key:
1 |
|
- 使用刚刚创建的私钥创建一个证书签名请求文件:martin.csr,要注意需要确保在-subj参数中指定用户名和组(CN表示用户名,O表示组):
1 |
|
然后找到Kubernetes集群的CA,我们使用的是kubeadm安装的集群,CA相关证书位于/etc/kubernetes/pki/目录下面,如果是二进制方式搭建的,应该在最开始搭建集群的时候就已经指定好了CA的目录(/data/kubernetes/ssl),然后利用该目录下面的ca.crt和ca.key两个文件来批准上面的证书请求
生成最终的证书文件,这里设置证书的有效期为500天
1 |
|
- 现在可以使用刚刚创建的证书文件和私钥文件在集群中创建新的凭证和上下文(Context):
1 |
|
- 可以看到一个用户martin创建了,然后为这个用户设置新的 Context:
1 |
|
到这里,用户martin就已经创建成功了,现在使用当前的这个配置文件来操作kubectl命令的时候,应该会出现错误,因为还没有为该用户定义任何操作的权限:
1 |
|
2. 使用cfssl工具来创建,也是参考官方文档中的方法。
CFSSL是CloudFlare开源的一款PKI/TLS工具。 CFSSL 包含一个命令行工具和一个用于签名,验证并且捆绑TLS证书的HTTP API服务。使用Go语言编写。
CFSSL包括:
- 一组用于生成自定义TLS PKI的工具
- cfssl程序,是CFSSL的命令行工具
- multirootca程序是可以使用多个签名密钥的证书颁发机构服务器
- mkbundle程序用于构建证书池
- cfssljson程序,从cfssl和multirootca程序获取JSON输出,并将证书,密钥,CSR和bundle写入磁盘
PKI借助数字证书和公钥加密技术提供可信任的网络身份。通常,证书就是一个包含如下身份信息的文件:
- 证书所有组织的信息
- 公钥
- 证书颁发组织的信息
- 证书颁发组织授予的权限,如证书有效期、适用的主机名、用途等
- 使用证书颁发组织私钥创建的数字签名
cfssl工具,子命令介绍:
- bundle: 创建包含客户端证书的证书包
- genkey: 生成一个key(私钥)和CSR(证书签名请求)
- scan: 扫描主机问题
- revoke: 吊销证书
- certinfo: 输出给定证书的证书信息,跟cfssl-certinfo 工具作用一样
- gencrl: 生成新的证书吊销列表
- selfsign: 生成一个新的自签名密钥和签名证书
- print-defaults: 打印默认配置,这个默认配置可以用作模板
- serve: 启动一个HTTP API服务
- gencert: 生成新的key(密钥)和签名证书
- -ca:指明ca的证书
- -ca-key:指明ca的私钥文件
- -config:指明请求证书的json文件
- -profile:与-config中的profile对应,是指根据config中的prof ile段来生成证书的相关信息
- ocspdump
- ocspsign
- info: 获取有关远程签名者的信息
- sign: 签名一个客户端证书,通过给定的CA和CA密钥,和主机名
- ocsprefresh
- ocspserve
创建认证中心(CA),也就是Kubernetes集群的CA,上面用openssl时已经将其省略了,现cfssl操作说明下详细方法:
CFSSL可以创建一个获取和操作证书的内部认证中心。运行认证中心需要一个CA证书和相应的CA私钥。任何知道私钥的人都可以充当CA颁发证书。因此,私钥的保护至关重要。
创建用来生成CA文件的JSON配置文件,配置证书生成策略,让CA软件知道颁发什么样的证书。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25[root@linux-node1 ssl]# vim ca-config.json
{
"signing": {
"default": {
"expiry": "8760h"
},
"profiles": {
"kubernetes": {
"usages": [
"signing",
"key encipherment",
"server auth",
"client auth"
],
"expiry": "8760h"
}
}
}
}
这个策略,有一个默认的配置,和一个profile,可以设置多个profile,这里的profile 是kubernetes。
默认策略,指定了证书的有效期是一年(8760h)
kubernetes策略,指定了证书的用途
signing, 表示该证书可用于签名其它证书;生成的ca.pem 证书中 CA=TRUE
server auth:表示client可以用该CA对server提供的证书进行验证
client auth:表示server可以用该CA对client提供的证书进行验证创建用来生成CA证书签名请求(CSR)的JSON配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24[root@linux-node1 ssl]# vim ca-csr.json
{
"CN": "kubernetes",
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "BeiJing",
"L": "BeiJing",
"O": "k8s",
"OU": "System"
}
]
}
术语介绍:
CN: Common Name,浏览器使用该字段验证网站是否合法,一般写的是域名。非常重要。浏览器使用该字段验证网站是否合法
C: Country, 国家
L: Locality,地区,城市
O: Organization Name,组织名称,公司名称
OU: Organization Unit Name,组织单位名称,公司部门
ST: State,州,省生成CA证书(ca.pem)和私钥(ca-key.pem)
1
2
3
4
5
6
7
8
9[root@ linux-node1 ssl]# cfssl gencert -initca ca-csr.json | cfssljson -bare ca
#初始化ca
[root@ linux-node1 ssl]# ls -l ca*
-rw-r--r-- 1 root root 290 Mar 4 13:45 ca-config.json
-rw-r--r-- 1 root root 1001 Mar 4 14:09 ca.csr
-rw-r--r-- 1 root root 208 Mar 4 13:51 ca-csr.json
-rw------- 1 root root 1679 Mar 4 14:09 ca-key.pem
-rw-r--r-- 1 root root 1359 Mar 4 14:09 ca.pem
该命令会生成运行CA所必需的文件ca-key.pem(私钥)和ca.pem(证书),还会生成ca.csr(证书签名请求),用于交叉签名或重新签名。小提示:
- 使用现有的CA私钥,重新生成:
1
cfssl gencert -initca -ca-key key.pem ca-csr.json | cfssljson -bare ca
- 使用现有的CA私钥和CA证书,重新生成:
1
cfssl gencert -renewca -ca cert.pem -ca-key key.pem
- 查看cert(证书信息):cfssl certinfo -cert ca.pem
- 查看CSR(证书签名请求)信息:cfssl certinfo -csr ca.csr
创建martin证书签名请求(Kubernetes集群的CA创建好了,再根据该CA证书来创建一个只能访问某个namespace的用户)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22➜ martin cat martin-csr.json
{
"CN": "martin",
"hosts": [],
"key": {
"algo": "rsa",
"size": 2048
},
"names": [
{
"C": "CN",
"ST": "BeiJing",
"L": "BeiJing",
"O": "op", #system:masters
"OU": "System"
}
]
}
➜ martin
后续kube-apiserver使用RBAC对客户端(如kubelet、kube-proxy、Pod)请求进行授权;
kube-apiserver预定义了一些RBAC使用的RoleBindings,如cluster-admin将Group op(system:masters)与 Role cluster-admin 绑定,该Role授予了调用kube-apiserver的所有API的权限;
OU指定该证书的Group为op(system:masters),kubelet使用该证书访问 kube-apiserver时,由于证书被CA签名,所以认证通过,同时由于证书用户组为经过预授权的op(system:masters),所以被授予访问所有 API 的权限;生成martin证书和私钥
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20➜ martin ls -lth
total 4.0K
-rw-r--r-- 1 root root 218 Jun 26 11:59 martin-csr.json
➜ martin cfssl gencert -ca=/data/kubernetes/ssl/ca.pem -ca-key=/data/kubernetes/ssl/ca-key.pem -config=/data/kubernetes/ssl/ca-config.json -profile=kubernetes martin-csr.json|cfssljson -bare martin
2018/06/26 16:20:37 [INFO] generate received request
2018/06/26 16:20:37 [INFO] received CSR
2018/06/26 16:20:37 [INFO] generating key: rsa-2048
2018/06/26 16:20:38 [INFO] encoded CSR
2018/06/26 16:20:38 [INFO] signed certificate with serial number 451530418945753741698899402739082416074910829402
2018/06/26 16:20:38 [WARNING] This certificate lacks a "hosts" field. This makes it unsuitable for
websites. For more information see the Baseline Requirements for the Issuance and Management
of Publicly-Trusted Certificates, v.1.1.6, from the CA/Browser Forum (https://cabforum.org);
specifically, section 10.2.3 ("Information Requirements").
➜ martin ls -lth
total 16K
-rw-r--r-- 1 root root 993 Jun 26 16:20 martin.csr
-rw------- 1 root root 1.7K Jun 26 16:20 martin-key.pem
-rw-r--r-- 1 root root 1.4K Jun 26 16:20 martin.pem
-rw-r--r-- 1 root root 218 Jun 26 11:59 martin-csr.json
➜ martin设置集群参数
本段主要设置了需要访问的集群的信息。使用set-cluster设置了需要访问的集群,如下为kubernetes,这只是个名称,实际为–server指向的apiserver;–certificate-authority设置了该集群的公钥;–embed-certs为true表示将–certificate-authority证书写入到kubeconfig中;–server则表示该集群的kube-apiserver地址
1 |
|
注意:–kubeconfig=martin.kubeconfig是将生成的相关信息全部写入martin.kubeconfig文件,如果不指定的话,默认是写入到“~/.kube/config ”
- 设置客户端认证参数
本段主要设置用户的相关信息,主要是用户证书。如下用户名为martin,证书为:/martin.pem,私钥为:./martin-key.pem。客户端的证书首先要经过集群CA的签署,否则不会被集群认可。此处使用的是ca认证方式,也可以使用token认证,如kubelet的TLS Boostrap机制下的bootstrapping使用的就是token认证方式。如下kubectl使用的是ca认证,不需要token字段
1 |
|
- 设置上下文参数,指定命名空间为:kube-system
集群参数和用户参数可以同时设置多对,在上下文参数中将集群参数和用户参数关联起来。下面的上下文名称为martin-context,集群为kubenetes,用户为martin,表示使用martin的用户凭证来访问kubenetes集群的kube-system命名空间(增加–namspace来指定访问的命名空间)。
执行之前先看下:martin.kubeconfig文件内容:
1 |
|
执行:
1 |
|
再次查看martin.kubeconfig文件,发现内容做了如下改变:
之前:
1 |
|
现在:
1 |
|
增加了上下文的相关信息。
- 设置默认上下文
1 |
|
如果配置了多个环境项,可以通过切换不同的环境项名字或指定kubeconfig文件来访问到不同的集群环境。
- 现在martin用户通过cfssl创建成功,可以看到所有关于martin用户的信息都写入了配置文件:martin.kubeconfig,不指定”-kubeconfig=”的话,默认是写入到”~/.kube/config”,如果之前有admin的相关信息,会追加到后面。martin.kubeconfig配置文件描述了集群、用户和上下文
kubectl只是个go编写的可执行程序,只要为kubectl配置合适的kubeconfig,就可以在集群中的任意节点使用。kubectl默认会从$HOME/.kube目录下查找文件名为config的文件,也可以通过设置环境变量KUBECONFIG或者通过设置–kubeconfig去指定其它kubeconfig文件,总之kubeconfig就是为访问集群所作的配置。
如果之前”~/.kube/config”下配置的是admin账号信息,要用martin账号,则指定kubeconfig文件即可:
1 |
|
现在使用当前的这个配置文件来操作kubectl命令的时候,应该会出现错误,因为还没有为该用户定义任何操作的权限呢:
1 |
|
如果提示:“kubectl error: You must be logged in to the server (Unauthorized)”
则是没有指定martin.kubeconfig文件或者默认的”~/.kube/config”里面没有martin用户的相关证书信息,因为前面设置客户端认证的时候没有指定password,而是用了证书。
1 |
|
另外可以通过:”Cfssl-Certinfo“命令来查看martin证书信息
1 |
|
第二步:创建角色
用户创建完成后,接下来就需要给该用户添加操作权限,定义一个YAML文件,创建一个允许用户操作Deployment、Pod、ReplicaSets 的角色,如下定义:(martin-role.yaml)
1 |
|
其中Pod属于core这个API Group,在YAML中用空字符就可以,而Deployment属于apps 这个API Group,ReplicaSets属于extensions这个API Group(点这里查文档),所以 rules下面的apiGroups 就综合了这几个资源的 API Group:[“”, “extensions”, “apps”],其中verbs就是上面提到的可以对这些资源对象执行的操作,这里需要所有的操作方法,所以也可以使用[‘*’]来代替。
然后创建这个Role:
1 |
|
注意这里没有使用上面的martin-context这个上下文,因为木有权限
第三步:创建角色权限绑定
Role创建完成了,但是很明显现在这个Role和我们的用户martin 还没有任何关系,这里就需要创建一个RoleBinding对象,在 kube-system这个命名空间下面将上面的martin-role角色和用户 martin进行绑定:(martin-rolebinding.yaml)
1 |
|
上面的YAML文件中看到了subjects关键字,这里就是上面提到的用来尝试操作集群的对象,这里对应上面的 User帐号martin,使用kubectl创建上面的资源对象:
1 |
|
使用admin账号(martin账号无权限查看)查看role和rolebinding相关信息:
1 |
|
第四步:测试
现在应该可以用上面的martin-context上下文来操作集群了:
1 |
|
使用kubectl时并没有指定namespace,这是因为之前已经为该用户分配了权限,并且指定了kube-system命名空间写入到martin.kubeconfig文件中,如果使用default命名空间,在后面加上一个-n default,则会提示forbidden,如下:
1 |
|
这是因为该用户并没有default这个命名空间的操作权限。
创建一个只能访问某个namespace的ServiceAccount
上面创建了一个只能访问某个命名空间下面的普通用户,前面也提到过subjects,下面还有一种类型的主题资源:ServiceAccount。
第一步:创建一个集群内部的用户只能操作kube-system这个命名空间下面的pods和deployments
首先来创建一个ServiceAccount对象:
1 |
|
当然也可以定义成YAML文件的形式来创建:
1 |
|
第二步:创建一个Role对象:(martin-sa-role.yaml)
1 |
|
可以看到这里定义的角色没有创建、删除、更新Pod的权限,等会可以重点测试一下。
创建该Role对象:
1 |
|
第三步,创建一个RoleBinding对象,将上面的martin-sa和角色martin-sa-role进行绑定:(martin-sa-rolebinding.yaml)
1 |
|
创建这个资源对象:
1 |
|
第四步,验证这个ServiceAccount
一个ServiceAccount会生成一个Secret对象和它进行映射,这个Secret里面包含一个token:
1 |
|
==注意:查看时需要-n指定kube-system命名空间!==
然后可以利用这个token去登录Dashboard,就可以在Dashboard中来验证功能是否符合预期:
1 |
|
使用这里的xxx token去Dashboard页面进行登录:
会出现如下提示信息:
1 |
|
这是因为登录进来后默认跳转到default命名空间,但是却没有改空间的权限,因此需要切换到kube-system命名空间下面:
原来url:
https://xxx/#!/deployment?namespace=default
修改为新url:
https://xxx/#!/deployment?namespace=kube-system
可以看到能访问pod列表了,但是也会有一些其他额外的提示:events is forbidden: User “system:serviceaccount:kube-system:martin-sa” cannot list events in the namespace “kube-system”,这是因为当前登录用只被授权了访问pod和deployment的权限,同样的,访问下deployment看看可以了吗?
同样的,可以根据自己的需求来对访问用户的权限进行限制,可以自己通过Role定义更加细粒度的权限,也可以使用系统内置的一些权限……
创建一个可以访问所有 namespace 的ServiceAccount
刚刚创建的martin-sa这个ServiceAccount和一个Role角色进行绑定的,如果现在创建一个新的ServiceAccount,需要他操作的权限作用于所有的namespace,这个时候就需要使用到ClusterRole 和 ClusterRoleBinding 这两种资源对象了。
第一步,同样,首先新建一个ServiceAcount对象:(martin-sa2.yaml)
1 |
|
第二步,创建一个ClusterRoleBinding 对象(martin-clusterolebinding.yaml):
1 |
|
对比下之前的”martin-sa-rolebinding.yaml”
1 |
|
从上面可以看到没有为这个资源对象声明namespace,因为这是一个ClusterRoleBinding 资源对象,是作用于整个集群的,也没有单独新建一个ClusterRole对象,而是使用的 cluster-admin这个对象,这是Kubernetes集群内置的ClusterRole对象,可以使用kubectl get clusterrole 和kubectl get clusterrolebinding查看系统内置的一些集群角色和集群角色绑定,这里使用的 cluster-admin这个集群角色是拥有最高权限的集群角色,所以一般需要谨慎使用该集群角色。
创建上面集群角色绑定资源对象:
1 |
|
通过ubectl get clusterrolebinding可以看到”martin-sa2-clusterrolebinding”已经加入其中:
1 |
|
第三步,使用 ServiceAccount对应的token去登录Dashboard验证:
1 |
|
在最开始接触到RBAC认证的时候,可能不太熟悉,特别是不知道应该怎么去编写rules规则,可以去分析系统自带的clusterrole、clusterrolebinding这些资源对象的编写方法,利用 kubectl的get、describe、-o yaml这些操作,所以kubectl最基本的操作一定要掌握好。
RBAC只是Kubernetes中安全认证的一种方式,当然也是现在最重要的一种方式。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!