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
2
3
4
5
6
$ cat /usr/lib/systemd/system/kube-apiserver.service
或者是:
$ cat /etc/kubernetes/manifests/kube-apiserver.yaml
...
- --authorization-mode=Node,RBAC
...

如果是二进制的方式搭建的集群,添加这个参数过后,记得要重启 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的管理,除了上面的这些资源和操作以外,我们还需要另外的一些对象:

  1. Rule:规则,规则是一组属于不同API Group 资源上的一组操作的集合
  2. Role 和 ClusterRole:角色和集群角色,这两个对象都包含上面的Rules 元素,二者的区别在于,在Role 中,定义的规则只适用于单个命名空间,也就是和namespace 关联的,而ClusterRole 是集群范围内的,因此定义的规则不受命名空间的约束。另外Role和 ClusterRole在Kubernetes中都被定义为集群内部的API 资源,和Pod、ConfigMap 这些类似,都是集群的资源对象,所以同样的可以使用kubectl相关的命令来进行操作
  3. Subject:主题,对应在集群中尝试操作的对象,集群中定义了3种类型的主题资源:
  • User Account:用户,这是有外部独立服务进行管理的,管理员进行私钥的分配,用户可以使用KeyStone或者Goolge 帐号,甚至一个用户名和密码的文件列表也可以。对于用户的管理集群内部没有一个关联的资源对象,所以用户不能通过集群内部的API 来进行管理
  • Group:组,这是用来关联多个账户的,集群中有一些默认创建的组,比如cluster-admin
  • Service Account:服务帐号,通过Kubernetes API 来管理的一些用户帐号,和namespace 进行关联的,适用于集群内部运行的应用程序,需要通过API 来完成权限认证,所以在集群内部进行权限操作,都需要使用到 ServiceAccount
  1. 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
$ openssl genrsa -out martin.key 2048
  • 使用刚刚创建的私钥创建一个证书签名请求文件:martin.csr,要注意需要确保在-subj参数中指定用户名和组(CN表示用户名,O表示组):
1
$ openssl req -new -key martin.key -out martin.csr -subj "/CN=martin/O=op"
  • 然后找到Kubernetes集群的CA,我们使用的是kubeadm安装的集群,CA相关证书位于/etc/kubernetes/pki/目录下面,如果是二进制方式搭建的,应该在最开始搭建集群的时候就已经指定好了CA的目录(/data/kubernetes/ssl),然后利用该目录下面的ca.crt和ca.key两个文件来批准上面的证书请求

  • 生成最终的证书文件,这里设置证书的有效期为500天

1
2
3
4
$ openssl x509 -req -in martin.csr -CA /data/kubernetes/ssl/ca.crt -CAkey /data/kubernetes/ssl/ca.key -CAcreateserial -out martin.crt -days 500
现在查看当前文件夹下面是否生成了一个证书文件:
$ ls
martin.csr martin.key martin.crt
  • 现在可以使用刚刚创建的证书文件和私钥文件在集群中创建新的凭证和上下文(Context):
1
$ kubectl config set-credentials martin --client-certificate=martin.crt  --client-key=martin.key
  • 可以看到一个用户martin创建了,然后为这个用户设置新的 Context:
1
$ kubectl config set-context martin-context --cluster=kubernetes --namespace=kube-system --user=martin

到这里,用户martin就已经创建成功了,现在使用当前的这个配置文件来操作kubectl命令的时候,应该会出现错误,因为还没有为该用户定义任何操作的权限:

1
2
$ kubectl get pods --context=martin-context
Error from server (Forbidden): pods is forbidden: User "martin" cannot list pods in the namespace "default"
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操作说明下详细方法:

    1. CFSSL可以创建一个获取和操作证书的内部认证中心。运行认证中心需要一个CA证书和相应的CA私钥。任何知道私钥的人都可以充当CA颁发证书。因此,私钥的保护至关重要。

    2. 创建用来生成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提供的证书进行验证
    3. 创建用来生成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,州,省
    4. 生成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-adminGroup 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
martin kubectl config set-cluster kubernetes --certificate-authority=/data/kubernetes/ssl/ca.pem --embed-certs=true --server=https://192.168.0.14:6443 --kubeconfig=martin.kubeconfig
Cluster "kubernetes" set.
martin ls -lth
total 20K
-rw------- 1 root root 2.0K Jun 26 16:29 martin.kubeconfig
-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.kubeconfig
martin cat martin.kubeconfig
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: xxx
server: https://192.168.0.14:6443
name: kubernetes
contexts: []
current-context: ""
kind: Config
preferences: {}
users: []
martin

注意:–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
2
3
4
5
6
7
8
martin kubectl config set-credentials martin --client-certificate=./martin.pem --client-key=./martin-key.pem --embed-certs=true --kubeconfig=martin.kubeconfig                                      
User "martin" set.
martin
可以看到martin.kubeconfig新增了如下内容:
users:
- name: martin
user:
xxx
  • 设置上下文参数,指定命名空间为:kube-system

集群参数和用户参数可以同时设置多对,在上下文参数中将集群参数和用户参数关联起来。下面的上下文名称为martin-context,集群为kubenetes,用户为martin,表示使用martin的用户凭证来访问kubenetes集群的kube-system命名空间(增加–namspace来指定访问的命名空间)。

执行之前先看下:martin.kubeconfig文件内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
  martin cat martin.kubeconfig 
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: xxx
server: https://192.168.0.14:6443
name: kubernetes
contexts: []
current-context: ""
kind: Config
preferences: {}
users:
- name: martin
user:
client-certificate-data: xxx
client-key-data: xxx
martin

执行:

1
2
3
martin kubectl config set-context martin-context --cluster=kubernetes --namespace=kube-system --user=martin --kubeconfig=martin.kubeconfig   
Context "martin-context" created.
martin

再次查看martin.kubeconfig文件,发现内容做了如下改变:
之前:

1
contexts: [] 

现在:

1
2
3
4
5
6
contexts:
- context:
cluster: kubernetes
namespace: kube-system
user: martin
name: martin-context

增加了上下文的相关信息。

  • 设置默认上下文
1
2
➜  martin kubectl config use-context martin-context --kubeconfig=martin.kubeconfig
Switched to context "martin-context".

如果配置了多个环境项,可以通过切换不同的环境项名字或指定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
2
3
4
5
6
7
8
martin kubectl config get-contexts                               
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
* kubernetes kubernetes admin
martin
martin kubectl config get-contexts --kubeconfig martin.kubeconfig
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
* martin-context kubernetes martin kube-system
martin

现在使用当前的这个配置文件来操作kubectl命令的时候,应该会出现错误,因为还没有为该用户定义任何操作的权限呢:

1
2
3
➜  martin kubectl get pods --kubeconfig martin.kubeconfig
Error from server (Forbidden): pods is forbidden: User "martin" cannot list pods in the namespace "default"
➜ martin

如果提示:“kubectl error: You must be logged in to the server (Unauthorized)”
则是没有指定martin.kubeconfig文件或者默认的”~/.kube/config”里面没有martin用户的相关证书信息,因为前面设置客户端认证的时候没有指定password,而是用了证书。

1
2
3
4
kubectl config set-credentials kubeuser/foo.kubernetes.com --username=kubeuser --password=kubepassword (用户名密码认证方式)

kubectl config set-credentials martin --client-certificate=./martin.pem --client-key=./martin-key.pem --embed-certs=true --kubeconfig=martin.kubeconfig
(证书认证方式)

另外可以通过:”Cfssl-Certinfo“命令来查看martin证书信息

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
26
27
28
29
30
31
32
33
34
➜  martin cfssl-certinfo -cert martin.pem
{
"subject": {
"common_name": "martin",
"country": "CN",
"organization": "op",
"organizational_unit": "System",
"locality": "BeiJing",
"province": "BeiJing",
"names": [
"CN",
"BeiJing",
"BeiJing",
"op",
"System",
"martin"
]
},
"issuer": {
"common_name": "kubernetes",
"country": "CN",
"organization": "k8s",
"organizational_unit": "System",
"locality": "BeiJing",
"province": "BeiJing",
"names": [
"CN",
"BeiJing",
"BeiJing",
"k8s",
"System",
"kubernetes"
]
},
第二步:创建角色

用户创建完成后,接下来就需要给该用户添加操作权限,定义一个YAML文件,创建一个允许用户操作Deployment、Pod、ReplicaSets 的角色,如下定义:(martin-role.yaml)

1
2
3
4
5
6
7
8
9
10
11
➜  martin cat martin-role.yaml 
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: martin-role
namespace: kube-system
rules:
- apiGroups: ["", "extensions", "apps"]
resources: ["deployments", "replicasets", "pods"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] # 也可以使用['*']
➜ martin

其中Pod属于core这个API Group,在YAML中用空字符就可以,而Deployment属于apps 这个API Group,ReplicaSets属于extensions这个API Group(点这里查文档),所以 rules下面的apiGroups 就综合了这几个资源的 API Group:[“”, “extensions”, “apps”],其中verbs就是上面提到的可以对这些资源对象执行的操作,这里需要所有的操作方法,所以也可以使用[‘*’]来代替。

然后创建这个Role:

1
2
3
➜  martin kubectl create -f martin-role.yaml 
role.rbac.authorization.k8s.io "martin-role" created
➜ martin

注意这里没有使用上面的martin-context这个上下文,因为木有权限

第三步:创建角色权限绑定

Role创建完成了,但是很明显现在这个Role和我们的用户martin 还没有任何关系,这里就需要创建一个RoleBinding对象,在 kube-system这个命名空间下面将上面的martin-role角色和用户 martin进行绑定:(martin-rolebinding.yaml)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
➜  martin cat martin-rolebinding.yaml 
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: martin-rolebinding
namespace: kube-system
roleRef:
#apiGroup: rbac.authorization.k8s.io
#kind: ClusterRole
kind: Role
name: martin-role
apiGroup: ""
subjects:
- apiGroup: ""
kind: User
name: martin
#apiGroup: "" #会提示语法错误
➜ martin

上面的YAML文件中看到了subjects关键字,这里就是上面提到的用来尝试操作集群的对象,这里对应上面的 User帐号martin,使用kubectl创建上面的资源对象:

1
2
➜  martin kubectl create -f martin-rolebinding.yaml
rolebinding.rbac.authorization.k8s.io "martin-rolebinding" created

使用admin账号(martin账号无权限查看)查看role和rolebinding相关信息:

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
➜  martin kubectl get rolebinding --all-namespaces
NAMESPACE NAME AGE
kube-public system:controller:bootstrap-signer 19d
kube-system kubernetes-dashboard-minimal 11d
kube-system martin-rolebinding 18h
kube-system system::leader-locking-kube-controller-manager 19d
kube-system system::leader-locking-kube-scheduler 19d
kube-system system:controller:bootstrap-signer 19d
kube-system system:controller:cloud-provider 19d
kube-system system:controller:token-cleaner 19d
kube-system ui-admin-binding 11d
kube-system ui-read-binding 11d
➜ martin
➜ martin kubectl get role --all-namespaces
NAMESPACE NAME AGE
kube-public system:controller:bootstrap-signer 19d
kube-system extension-apiserver-authentication-reader 19d
kube-system kubernetes-dashboard-minimal 11d
kube-system martin-role 18h
kube-system system::leader-locking-kube-controller-manager 19d
kube-system system::leader-locking-kube-scheduler 19d
kube-system system:controller:bootstrap-signer 19d
kube-system system:controller:cloud-provider 19d
kube-system system:controller:token-cleaner 19d
➜ martin
第四步:测试

现在应该可以用上面的martin-context上下文来操作集群了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
➜  martin kubectl get pods                                          
NAME READY STATUS RESTARTS AGE
nexus3-68f55d9746-vfnf8 1/1 Running 0 5d
rbd-rest-api-registrykey-m262-1 1/1 Running 0 4d
➜ martin
➜ martin kubectl get pods -n default
NAME READY STATUS RESTARTS AGE
nexus3-68f55d9746-vfnf8 1/1 Running 0 5d
rbd-rest-api-registrykey-m262-1 1/1 Running 0 4d
➜ martin
➜ martin kubectl get pods --kubeconfig martin.kubeconfig
NAME READY STATUS RESTARTS AGE
coredns-77c989547b-lcbfw 1/1 Running 1 15d
coredns-77c989547b-xq4dr 1/1 Running 1 15d
heapster-77b9c5bd7b-l5ms6 1/1 Running 0 11d
kubernetes-dashboard-66c9d98865-g8l6l 1/1 Running 0 11d
monitoring-grafana-7c674cb7f6-nqvlw 1/1 Running 0 11d
monitoring-influxdb-644db5c5b6-llnp9 1/1 Running 0 11d

使用kubectl时并没有指定namespace,这是因为之前已经为该用户分配了权限,并且指定了kube-system命名空间写入到martin.kubeconfig文件中,如果使用default命名空间,在后面加上一个-n default,则会提示forbidden,如下:

1
2
3
➜  martin kubectl get pods -n default --kubeconfig martin.kubeconfig 
Error from server (Forbidden): pods is forbidden: User "martin" cannot list pods in the namespace "default"
➜ martin

这是因为该用户并没有default这个命名空间的操作权限。


创建一个只能访问某个namespace的ServiceAccount

上面创建了一个只能访问某个命名空间下面的普通用户,前面也提到过subjects,下面还有一种类型的主题资源:ServiceAccount。

第一步:创建一个集群内部的用户只能操作kube-system这个命名空间下面的pods和deployments

首先来创建一个ServiceAccount对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
➜  serviceaccount kubectl create sa martin-sa -n kube-system
serviceaccount "martin-sa" created
➜ serviceaccount
➜ serviceaccount kubectl get sa
NAME SECRETS AGE
default 1 20d
➜ serviceaccount kubectl get sa -n kube-system
NAME SECRETS AGE
admin-user 1 11d
coredns 1 15d
default 1 20d
heapster 1 11d
kubernetes-dashboard 1 11d
martin-sa 1 13s
➜ serviceaccount

当然也可以定义成YAML文件的形式来创建:

1
2
3
4
5
6
apiVersion: v1
kind: ServiceAccount
metadata:
name: martin-sa
namespace: kube-system

第二步:创建一个Role对象:(martin-sa-role.yaml)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
➜  serviceaccount cat martin-sa-role.yaml 
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: martin-sa-role
namespace: kube-system
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"] # 也可以使用['*']
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
➜ serviceaccount

可以看到这里定义的角色没有创建、删除、更新Pod的权限,等会可以重点测试一下。

创建该Role对象:

1
2
➜  serviceaccount kubectl create -f martin-sa-role.yaml 
role.rbac.authorization.k8s.io "martin-sa-role" created
第三步,创建一个RoleBinding对象,将上面的martin-sa和角色martin-sa-role进行绑定:(martin-sa-rolebinding.yaml)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  serviceaccount cat martin-sa-rolebinding.yaml 
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: martin-sa-rolebinding
namespace: kube-system
subjects:
- kind: ServiceAccount
name: martin-sa
namespace: kube-system
roleRef:
kind: Role
name: martin-sa-role
apiGroup: rbac.authorization.k8s.io

创建这个资源对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
➜  serviceaccount kubectl get rolebinding -n kube-system
NAME AGE
kubernetes-dashboard-minimal 11d
martin-rolebinding 22h
➜ serviceaccount

➜ serviceaccount kubectl create -f martin-sa-rolebinding.yaml
rolebinding.rbac.authorization.k8s.io "martin-sa-rolebinding" created
➜ serviceaccount
➜ serviceaccount kubectl get rolebinding
No resources found.
➜ serviceaccount kubectl get rolebinding -n kube-system
NAME AGE
kubernetes-dashboard-minimal 11d
martin-rolebinding 23h
martin-sa-rolebinding 26s
可以看到martin-sa-rolebinding已经添加

第四步,验证这个ServiceAccount

一个ServiceAccount会生成一个Secret对象和它进行映射,这个Secret里面包含一个token:

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
26
➜  serviceaccount kubectl get secret -n kube-system
NAME TYPE DATA AGE
admin-user-token-xszp7 kubernetes.io/service-account-token 3 11d
coredns-token-9ppnq kubernetes.io/service-account-token 3 15d
default-token-fs7zj kubernetes.io/service-account-token 3 20d
heapster-token-gn8g5 kubernetes.io/service-account-token 3 11d
kubernetes-dashboard-certs Opaque 0 11d
kubernetes-dashboard-key-holder Opaque 2 15d
kubernetes-dashboard-token-tg782 kubernetes.io/service-account-token 3 11d
martin-sa-token-78s5j kubernetes.io/service-account-token 3 41m #新增
➜ serviceaccount
➜ serviceaccount kubectl describe secret martin-sa-token-78s5j -n kube-system
Name: martin-sa-token-78s5j
Namespace: kube-system
Labels: <none>
Annotations: kubernetes.io/service-account.name=martin-sa
kubernetes.io/service-account.uid=576ef41d-79e2-11e8-bede-5254004f2222

Type: kubernetes.io/service-account-token

Data
====
ca.crt: 1359 bytes
namespace: 11 bytes
token: xxx
➜ serviceaccount

==注意:查看时需要-n指定kube-system命名空间!==

然后可以利用这个token去登录Dashboard,就可以在Dashboard中来验证功能是否符合预期:

1
2
3
➜  serviceaccount kubectl get secret martin-sa-token-78s5j -o jsonpath={.data.token} -n kube-system |base64 -d #会生成一串很长的base64后的字符串
xxxxxxxxxxxxxxxx
➜ serviceaccount

使用这里的xxx token去Dashboard页面进行登录:
会出现如下提示信息:

1
2
3
4
configmaps is forbidden: User "system:serviceaccount:kube-system:martin-sa" cannot list configmaps in the namespace "default"
close
warning
persistentvolumeclaims is forbidden: User "system:serviceaccount:kube-system:martin-sa" cannot list persistentvolumeclaims in the namespace "default"

这是因为登录进来后默认跳转到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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
➜  serviceaccount cat martin-sa2.yaml 
apiVersion: v1
kind: ServiceAccount
metadata:
name: martin-sa2
namespace: kube-system
➜ serviceaccount
➜ serviceaccount kubectl create -f martin-sa2.yaml
serviceaccount "martin-sa2" created
➜ serviceaccount kubectl get sa
NAME SECRETS AGE
default 1 20d
➜ serviceaccount kubectl get sa -n kube-system
NAME SECRETS AGE
admin-user 1 12d
coredns 1 15d
default 1 20d
heapster 1 12d
kubernetes-dashboard 1 12d
martin-sa 1 1h
martin-sa2 1 12s
➜ serviceaccount
第二步,创建一个ClusterRoleBinding 对象(martin-clusterolebinding.yaml):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  serviceaccount cat martin-clusterolebinding.yaml   
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: martin-sa2-clusterrolebinding
subjects:
- kind: ServiceAccount
name: martin-sa2
namespace: kube-system
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io
serviceaccount

对比下之前的”martin-sa-rolebinding.yaml”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  serviceaccount cat martin-sa-rolebinding.yaml 
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: martin-sa-rolebinding
namespace: kube-system
subjects:
- kind: ServiceAccount
name: martin-sa
namespace: kube-system
roleRef:
kind: Role
name: martin-sa-role
apiGroup: rbac.authorization.k8s.io
serviceaccount

从上面可以看到没有为这个资源对象声明namespace,因为这是一个ClusterRoleBinding 资源对象,是作用于整个集群的,也没有单独新建一个ClusterRole对象,而是使用的 cluster-admin这个对象,这是Kubernetes集群内置的ClusterRole对象,可以使用kubectl get clusterrole 和kubectl get clusterrolebinding查看系统内置的一些集群角色和集群角色绑定,这里使用的 cluster-admin这个集群角色是拥有最高权限的集群角色,所以一般需要谨慎使用该集群角色。

创建上面集群角色绑定资源对象:

1
2
3
➜  serviceaccount kubectl create -f martin-clusterolebinding.yaml 
clusterrolebinding.rbac.authorization.k8s.io "martin-sa2-clusterrolebinding" created
➜ serviceaccount

通过ubectl get clusterrolebinding可以看到”martin-sa2-clusterrolebinding”已经加入其中:

1
2
3
4
5
6
7
➜  serviceaccount kubectl get clusterrolebinding 
NAME AGE
admin-user 12d
cluster-admin 20d
heapster 12d
kubelet-bootstrap 19d
martin-sa2-clusterrolebinding 22s
第三步,使用 ServiceAccount对应的token去登录Dashboard验证:
1
2
3
4
5
6
7
➜  serviceaccount kubectl get secret -n kube-system|grep martin-sa2-token-q7bhr
martin-sa2-token-q7bhr kubernetes.io/service-account-token 3 34m
➜ serviceaccount
➜ serviceaccount kubectl get secret martin-sa2-token-q7bhr -o jsonpath={.data.token} -n kube-system |base64 -d
xxxxxxx
#会生成一串很长的base64后的字符串
➜ serviceaccount

在最开始接触到RBAC认证的时候,可能不太熟悉,特别是不知道应该怎么去编写rules规则,可以去分析系统自带的clusterrole、clusterrolebinding这些资源对象的编写方法,利用 kubectl的get、describe、-o yaml这些操作,所以kubectl最基本的操作一定要掌握好。

RBAC只是Kubernetes中安全认证的一种方式,当然也是现在最重要的一种方式。