Mr.可豆

可豆先生的生活,技术和学习


Quality-focused Systems Engineer, support in different OS based system and Virtualization.

K8s系列之RBAC

前几天突发奇想,把以前自己写的ansible部署k8集群的脚本改成saltstack,说干就干,结果一不小心就把下载到本地的kube的几个二进制可执行文件给删了,因为众所周知的原因,下载这几个东西还挺麻烦。打开下载链接发现现在最新都是 1.14.1 了, 在这里 下载链接 ,就直接下载了1.14,结果脚本改好部署完却起不来.

kube-apiserver 报错,说这个--enable-admission-plugins参数不正确,才发现 1.12 版本适用的几个在 1.14 都没有了,改完正常起来后部署了几个deployment都很正常。直到要进到容器里看文件的时候出现了问题:

nick@x79:~$ kubectl exec busybox cat /etc/resolv.conf
error: unable to upgrade connection: Forbidden (user=kubernetes, verb=create, resource=nodes, subresource=proxy)

看到这个报错就想是不是权限错误,因为 1.12 的时候我并没有弄过,对RBAC也没太多了解,只是大概知道 role, rolebinding, clusterrole, clusterrolebinding 这四个东西,但是更让我摸不着头脑的是为什么报错信息里的user是 kubernetes,因为我的用户并不叫这个名字,那会不会是kube的运行进程的用户呢,也不对阿,部署过程中我并没有创建这个用户,而且看了一下我的 .kube/config 里面的 user 也是 admin,那这个 kubernetes哪里来的呢?

其实 k8 里的 RBAC 主要有两个概念,分别是 rolerolebinding , role可能有些书籍会翻译成角色,主要定义的是可以做什么,具体看下面这个yaml文档:

kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: pod-reader
  namespace: default
rules:
- apiGroups: [""]
  resources: ["pod"]
  resourceNames: ["busybox"]
  verbs: ["get", "update","patch"]

从metadata开始,name 指的是这个 role 的名字,也就是在运行 kubectl get roles 可以看到的名字。接下来的namespace指的是这个 role 所适用的范围,这个其实也类似于组的概念,我们这里写了namespace:default也就是说kubectl get roles -n default可以看到这个定义。说到这里,RBAC里面还有另外一个概念,但是跟这个 role 很类似的,就是clusterrole,它定义的是全局的一个 role ,适用于所有的namespace,因此它不需要定义namespace 对于一个 role 来说,接下来的rules相对更重要一些,因为这里定义了这个role真正可以做的事情,像我们这里 apiGroups ,就是接下来的resources所属的 apiGroup,而resources就是这个 role 的执行对象了,像我们这个例子就是 pod 也就是说这个role 定义了对于 pod 的允许操作, 不难发现resourceNames就是 pod 的名字,也就是说上面一行说的是可以对 pod 做什么事情,加上这一行就是可以对名字为resourceNames的 pod 做什么事情了。那现在我们已经规定了作用对象,那能执行什么操作呢,就看接下来这个verbs,英语意思是动词,像我们这里就是可以对 pod 去 get, update, patch 。注意如果设置了 resourceNames ,那 verb 这里就不能是 list, watch, create, 或者 delete了。 这样这一整个yaml文件就会被kubernetes理解成“创建一个名字叫 pod-reader 仅适用于 default 这个 namespace 的role,它仅仅可以对名字为 busybox 的 pod 作出 get,update, patch 的操作”。 好了,现在我们有个自定义的 role 了,那这个 role 怎么应用呢,接下来就是第二个概念了rolebinding, binding 的意思是绑定,但是别理解错了,这里并不是说把系统用户跟k8的 role 绑定,那是什么用户呢,且看看下面的yaml先:

kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: admin
  namespace: default
subjects:
- kind: User
  name: test # Name is case sensitive
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

这个 yaml 文件告诉 kubernete 把刚才创建的 pod-reader 的 role 赋予给 test 这个用户,我们先解释这个文件再说这个用户什么。还是从 metadata 开始,这里的 name 就是 kubectl get rolebindings 的时候看到的名字,下面的 namespace 表示这个 rolebind 作用在哪个 namespace ,跟 role 一样, rolebinding 也区分 namespace ,当然不同 namespace 的 rolebinding 和 role 是无法绑定的。subjects 里面定义了绑定的用户或者组,也就是 kind 这里,我们这里是 User ,所以这个 test 就是用户的名字. 接下来的 roleRef 就定义了绑定什么 role 了, 像我们这里就是绑定到 pod-reader 了。因此 kubernetes 就会这样理解这个文件: 允许用户 test 获得 pod-reader 这个 role 的权限,也就是可以对名字为 busybox 的 pod 作出 get,update, patch 的操作 到了这里,相信你已经对 role, rolebinding 有个基本认知了吧,相对应 role, rolebinding 还有 clusterrole , clusterrolebinding, 他们的差别就是 cluster 的是针对整个集群,因此定义过程中不需要声明 namespace 。

接下来,我们就要聊聊用户了,为什么我上面那个报错会报 user=kubernetes 呢? rolebinding 的 yaml 文件里面的 user 是哪里来的呢?

先说说我是如何解决那个报错的,只要把这个 kubernetes 用户绑定到 system:kubelet-api-admin 的这个 clusterrole, 其实也就是创建一个 clusterrolebinding 就可以了。 可能很多新用户都不知道apiserver是如何验证用户的,那是因为像使用kubeadm等工具自动部署完集群后会生成一个 admin.kubeconfig, 好像是叫这个名字,然后会要求你把这个文件拷贝到自己的 HOME 目录的 .kube/config ,这样每次执行 kubectl 命令就会自动读取这个文件去验证,看看这个文件就知道里面包含了客户端的证书和 key 。所以呢,我们执行 kubectl 的时候,就是使用这个证书去跟 apiserver 验证的,这个就是 x509 验证,而 apiserver 会把证书的 CN 当作用户名,刚好我们这个证书的 CN 是 kubernetes ,所以这个错误才会说是 user=kubernetes,也就是因此我们绑定了这个用户和对应的权限,就可以通过 apiserver 的验证了。 我自己也做了个实验验证了一把,首先我创建一个新的证书,并指定CN=usernotexist , 然后再生成 kubeconfig 以供认证,结果果然报错如下:

nick@x79:/tmp/testcert$ kubectl get pods --kubeconfig=kubeconfig.bak
Error from server (Forbidden): pods is forbidden: User "usernotexist" cannot list resource "pods" in API group "" in the namespace "default": 

nick@x79:/tmp/testcert$ kubectl create clusterrolebinding master-test --clusterrole admin --user usernotexist
clusterrolebinding.rbac.authorization.k8s.io/master-test created
nick@x79:/tmp/testcert$ kubectl get pods --kubeconfig=kubeconfig.bak
NAME                                  READY   STATUS    RESTARTS   AGE
busybox-deployment-57b645c6cd-s8n6f   1/1     Running   6          12h
busybox-deployment-57b645c6cd-sx5nw   1/1     Running   6          12h
nginx-deployment-6dd86d77d-dm46v      1/1     Running   1          12h
nginx-deployment-6dd86d77d-g7szj      1/1     Running   1          12h

参考文档: 官档

最近的文章

Docker系列之MTU debug

昨天花了一整天时间解决了一个由MTU引起的问题,事情的起因是前天收到一个request, 开发人员要求部署个自定制的jenkins,依赖docker和anisble, 二话不说,架起sublimetext就写了个salt脚本,一键部署,各种顺畅,没想到事情来了。Docker 起来很正常,没有任何错误征兆,但是 jenkins 却一直停留在“jenkins is starting” 的界面,这就很怪异了。起初以为是读写什么东西没权限,因为在 jenkins community里有人说要把 J...…

继续阅读
更早的文章

记一次flannel vxlan的debug过程

自从部署好k8之后,用了一段时间都是自己配置路由,简单的使用 cni 的网络,简直是累得不行,每次加减新节点都得配置路由,因此就部署了 flannel , 结果今天晚上发现在 node 上居然访问不到服务,仔细看才知道节点之间的 pod 竟无法相互 ping 通,因此才有了今晚的这个过程。首先介绍一下我的这个环境是由一个master和四个node组成的一个集群,工作节点分别是node1,node2,node3和node4,对应的ip地址分别是192.168.1.12{1,2,3,4} , ...…

继续阅读