RBAC

红米11个月前技术文章279

API 对象

在学习 RBAC 之前,我们还需要再去理解下 Kubernetes 集群中的对象,我们知道,在 Kubernetes 集群中,Kubernetes 对象是我们持久化的实体,就是最终存入 etcd 中的数据,集群中通过这些实体来表示整个集群的状态。前面我们都直接编写的 YAML 文件,通过 kubectl 来提交的资源清单文件,然后创建的对应的资源对象,那么它究竟是如何将我们的 YAML 文件转换成集群中的一个 API 对象的呢?

这个就需要去了解下声明式 API的设计,Kubernetes API 是一个以 JSON 为主要序列化方式的 HTTP 服务,除此之外也支持 Protocol Buffers 序列化方式,主要用于集群内部组件间的通信。为了可扩展性,Kubernetes 在不同的 API 路径(比如/api/v1 或者 /apis/batch)下面支持了多个 API 版本,不同的 API 版本意味着不同级别的稳定性和支持:

  • Alpha 级别,例如 v1alpha1 默认情况下是被禁用的,可以随时删除对功能的支持,所以要慎用

  • Beta 级别,例如 v2beta1 默认情况下是启用的,表示代码已经经过了很好的测试,但是对象的语义可能会在随后的版本中以不兼容的方式更改

  • 稳定级别,比如 v1 表示已经是稳定版本了,也会出现在后续的很多版本中。

在 Kubernetes 集群中,一个 API 对象在 Etcd 里的完整资源路径,是由:Group(API 组)、Version(API 版本)和 Resource(API 资源类型)三个部分组成的。通过这样的结构,整个 Kubernetes 里的所有 API 对象,实际上就可以用如下的树形结构表示出来:

006.png

从上图中我们也可以看出 Kubernetes 的 API 对象的组织方式,在顶层,我们可以看到有一个核心组(由于历史原因,是 /api/v1 下的所有内容而不是在 /apis/core/v1 下面)和命名组(路径 /apis/NAME/VERSION)和系统范围内的实体,比如 /metrics。我们也可以用下面的命令来查看集群中的 API 组织形式:

# kubectl get --raw /{
  "paths": [
    "/api",
    "/api/v1",
    "/apis",
    "/apis/",
    ......
    "/version"
  ]
}

比如我们来查看批处理这个操作,在我们当前这个版本中存在1个版本的操作:/apis/batch/v1,暴露了可以查询和操作的不同实体集合,同样我们还是可以通过 kubectl 来查询对应对象下面的数据:

➜  ~ kubectl get --raw /apis/batch/v1 | python -m json.tool
{
    "apiVersion": "v1",
    "groupVersion": "batch/v1",
    "kind": "APIResourceList",
    "resources": [
        {
            "categories": [
                "all"
            ],
            "kind": "CronJob",
            "name": "cronjobs",
            "namespaced": true,
            "shortNames": [
                "cj"
            ],
            "singularName": "",
            "storageVersionHash": "sd5LIXh4Fjs=",
            "verbs": [
                "create",
                "delete",
                "deletecollection",
                "get",
                "list",
                "patch",
                "update",
                "watch"
            ]
        },
        {
            "kind": "CronJob",
            "name": "cronjobs/status",
            "namespaced": true,
            "singularName": "",
            "verbs": [
                "get",
                "patch",
                "update"
            ]
        },
        {
            "categories": [
                "all"
            ],
            "kind": "Job",
            "name": "jobs",
            "namespaced": true,
            "singularName": "",
            "storageVersionHash": "mudhfqk/qZY=",
            "verbs": [
                "create",
                "delete",
                "deletecollection",
                "get",
                "list",
                "patch",
                "update",
                "watch"
            ]
        },
        {
            "kind": "Job",
            "name": "jobs/status",
            "namespaced": true,
            "singularName": "",
            "verbs": [
                "get",
                "patch",
                "update"
            ]
        }
    ]
}

但是这个操作和我们平时操作 HTTP 服务的方式不太一样,这里我们可以通过 kubectl proxy 命令来开启对 apiserver 的访问:

➜  ~ kubectl proxy
Starting to serve on 127.0.0.1:8001

然后重新开启一个新的终端,我们可以通过如下方式来访问批处理的 API 服务:

➜  ~ curl http://127.0.0.1:8001/apis/batch/v1
{
  "kind": "APIResourceList",
  "apiVersion": "v1",
  "groupVersion": "batch/v1",
  "resources": [
    {
      "name": "jobs",
      "singularName": "",
      "namespaced": true,
      "kind": "Job",
      "verbs": [
        "create",
        "delete",
        "deletecollection",
        "get",
        "list",
        "patch",
        "update",
        "watch"
      ],
      "categories": [
        "all"
      ],
      "storageVersionHash": "mudhfqk/qZY="
    },
    {
      "name": "jobs/status",
      "singularName": "",
      "namespaced": true,
      "kind": "Job",
      "verbs": [
        "get",
        "patch",
        "update"
      ]
    }
  ]
}

通常,Kubernetes API 支持通过标准 HTTP POST、PUT、DELETE 和 GET 在指定 PATH 路径上创建、更新、删除和检索操作,并使用 JSON 作为默认的数据交互格式。

比如现在我们要创建一个 Deployment 对象,那么我们的 YAML 文件的声明就需要怎么写:

apiVersion: apps/v1kind: Deployment

其中 Deployment 就是这个 API 对象的资源类型(Resource),apps 就是它的组(Group),v1 就是它的版本(Version)。API Group、Version 和 资源就唯一定义了一个 HTTP 路径,然后在 kube-apiserver 端对这个 url 进行了监听,然后把对应的请求传递给了对应的控制器进行处理而已,当然在 Kuberentes 中的实现过程是非常复杂的。

RBAC

首先我们需要知道各个证书文件意义

在使用 OpenSSL 创建证书时,通常会涉及到三个文件后缀:.key、.csr 和 .crt。这些后缀代表以下含义:

1. .key:该文件后缀表示私钥文件,通常是以 PEM(Privacy Enhanced Mail)格式存储的私钥。私钥是一对密钥中的一部分,用于生成证书请求和签署证书。

2. .csr:该文件后缀表示证书请求文件(Certificate Signing Request)。证书请求包含与证书相关的信息,例如公钥、组织信息等。当你想获得由证书颁发机构(CA)签署的证书时,需要向 CA 提供证书请求文件。

3. .crt:该文件后缀表示证书文件,通常是由证书颁发机构签署的证书。证书包含公钥、组织信息以及证书签名等。它用于证明某个实体(例如网站、组织等)的身份,并用于建立加密通信连接。

这些文件在使用 OpenSSL 生成证书时起到不同的作用:私钥用于生成证书请求,证书请求用于向证书颁发机构申请证书,而证书则是由证书颁发机构签署的用于验证实体身份的文件。

上面我们介绍了 Kubernetes 所有资源对象都是模型化的 API 对象,允许执行 CRUD(Create、Read、Update、Delete) 操作(也就是我们常说的增、删、改、查操作),比如下面的这些资源:

  • Pods

  • ConfigMaps

  • Deployments

  • Nodes

  • Secrets

  • Namespaces

  • ......
    对于上面这些资源对象的可能存在的操作有:

  • create

  • get

  • delete

  • list

  • update

  • edit

  • watch

  • exec

  • patch

在更上层,这些资源和 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、Deployment 这些对象类似,都是我们集群的资源对象,所以同样的可以使用 YAML 文件来描述,用 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。

接下来我们来通过几个简单的示例,来学习下在 Kubernetes 集群中如何使用 RBAC。

只能访问某个 namespace 的普通用户

我们想要创建一个 User Account,只能访问 kube-system 这个命名空间,对应的用户信息如下所示:

username: jixingxing
group: github

创建用户凭证

我们前面已经提到过,Kubernetes 没有 User Account 的 API 对象,不过要创建一个用户帐号的话也是挺简单的,利用管理员分配给你的一个私钥就可以创建了,这个我们可以参考官方文档中的方法,这里我们来使用 OpenSSL 证书来创建一个 User,当然我们也可以使用更简单的 cfssl工具来创建:
给用户 jixingxing 创建一个私钥,命名成 jixingxing.key:

[root@kube01 rbac]# openssl genrsa -out jixingxing.key 2048
Generating RSA private key, 2048 bit long modulus
...........................................+++
....................+++
e is 65537 (0x10001)

使用我们刚刚创建的私钥创建一个证书签名请求文件:jixingxing.csr,要注意需要确保在 -subj 参数中指定用户名和组(CN表示用户名,O表示组):

[root@kube01 rbac]# openssl req -new -key jixingxing.key -out jixingxing.csr -subj "/CN=jixingxing/O=github"

然后找到我们的 Kubernetes 集群的 CA 证书,我们使用的是 kubeadm 安装的集群,CA 相关证书位于 /etc/kubernetes/pki/ 目录下面,如果你是二进制方式搭建的,你应该在最开始搭建集群的时候就已经指定好了 CA 的目录,我们会利用该目录下面的 ca.crt 和 ca.key两个文件来批准上面的证书请求。生成最终的证书文件,我们这里设置证书的有效期为 500 天:

[root@kube01 rbac]# openssl x509 -req -in jixingxing.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out jixingxing.crt -days 500
Signature ok
subject=/CN=jixingxing/O=github
Getting CA Private Key

需要注意各个参数含义

以下是对使用 OpenSSL 命令进行证书签名的各个参数的解释:- `x509`:指定 OpenSSL 命令操作为生成自签名 X.509 证书。- `-req`:表示输入文件是证书请求文件。- `-in jixingxing.csr`:指定输入的证书请求文件的路径和文件名。- `-CA /etc/kubernetes/pki/ca.crt`:指定 CA(证书颁发机构)的证书文件路径和文件名。- `-CAkey /etc/kubernetes/pki/ca.key`:指定用于签署证书的 CA 的私钥文件路径和文件名。- `-CAcreateserial`:表示 OpenSSL 应该创建一个序列号文件,并将其与生成的证书关联起来。序列号文件用于跟踪每个生成的证书。- `-out jixingxing.crt`:指定生成的证书文件的路径和文件名。- `-days 500`:指定证书的有效期限,以天为单位。在此示例中,证书的有效期限为 500 天。

该命令的作用是使用指定的 CA 证书和私钥对给定的证书请求进行签名,生成一个有效期为 500 天的证书文件(jixingxing.crt)。该证书可以用于验证实体的身份,并建立加密通信连接。

现在查看我们当前文件夹下面是否生成了一个证书文件:

[root@kube01 rbac]# ll
总用量 12
-rw-r--r-- 1 root root 1001 6月  28 14:29 jixingxing.crt
-rw-r--r-- 1 root root  915 6月  28 14:26 jixingxing.csr
-rw-r--r-- 1 root root 1679 6月  28 14:25 jixingxing.key

现在我们可以使用刚刚创建的证书文件和私钥文件在集群中创建新的凭证和上下文(Context):

[root@kube01 rbac]# kubectl config set-credentials jixingxing --client-certificate=jixingxing.crt --client-key=jixingxing.key
User "jixingxing" set.

我们可以看到一个用户 jixingxing 创建了,然后为这个用户设置新的 Context,我们这里指定特定的一个 namespace:

[root@kube01 rbac]# kubectl config set-context jixingxing-context --cluster=kubernetes --namespace=kube-system --user=jixingxing
Context "jixingxing-context" created.

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

[root@kube01 rbac]# kubectl get pods --context=jixingxing-context
Error from server (Forbidden): pods is forbidden: User "jixingxing" cannot list resource "pods" in API group "" in the namespace "kube-system"

创建角色

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

# cat role.yamlapiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata:
  name: jixingxing-role
  namespace: kube-systemrules:- apiGroups: ["", "apps"]  resources: ["deployments", "replicasets", "pods"]  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] # 也可以使用['*']

注意这里我们没有使用上面的 jixingxing-context 这个上下文,因为暂时还木有权限。

创建角色权限绑定

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

# cat rolebind.yamlapiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: jixingxing-rolebinding
  namespace: kube-system
subjects:
- kind: User
  name: jixingxing
  apiGroup: ""
roleRef:
  kind: Role
  name: jixingxing-role
  apiGroup: rbac.authorization.k8s.io  # 留空字符串也可以,则使用当前的apiGrou

测试

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

[root@kube01 rbac]#  kubectl get pods --context=jixingxing-context
NAME                                        READY   STATUS      RESTARTS          AGE
coredns-65c54cc984-7fkp7                    1/1     Running     1 (100d ago)      111d
coredns-65c54cc984-cq95z                    1/1     Running     1 (100d ago)      111d
etcd-kube01                                 1/1     Running     14364 (20d ago)   111d
image-admission-webhook-b4475d665-fdc7l     1/1     Running     0                 12d
ingress-nginx-admission-create-hd47h        0/1     Completed   0                 111d
ingress-nginx-admission-patch-hsh6t         0/1     Completed   0                 111d
ingress-nginx-controller-7ffc675759-xx2xq   1/1     Running     1 (100d ago)      111d
kube-apiserver-kube01                       1/1     Running     16062 (20d ago)   111d
kube-controller-manager-kube01              1/1     Running     4 (20d ago)       111d
kube-flannel-ds-56hgr                       1/1     Running     1 (100d ago)      111d
kube-flannel-ds-9zzbj                       1/1     Running     1 (100d ago)      111d
kube-proxy-b56bm                            1/1     Running     1 (100d ago)      111d
kube-proxy-s29rd                            1/1     Running     1 (100d ago)      111d
kube-scheduler-kube01                       1/1     Running     4 (20d ago)       111d
metrics-server-v0.5.2-678db5756d-qjq6r      2/2     Running     2 (100d ago)      111d
[root@kube01 rbac]#  kubectl get svc --context=jixingxing-context
Error from server (Forbidden): services is forbidden: User "jixingxing" cannot list resource "services" in API group "" in the namespace "kube-system"

我们可以看到我们使用 kubectl 的使用并没有指定 namespace,这是因为我们我们上面创建这个 Context 的时候就绑定在了 kube-system 这个命名空间下面。
去获取其他的资源对象,我们可以看到没有权限获取,因为我们并没有为当前操作用户指定其他对象资源的访问权限,是符合我们的预期的。这样我们就创建了一个只有单个命名空间访问权限的普通 User 。

我们还可以把 context 的权限下载到本地使用

[root@kube01 ~]# kubectl config view --flatten --minify  --context=jixingxing-context  > my-config.config

[root@kube01 ~]# kubectl get pod  --kubeconfig my-config.config

只能访问某个 namespace 的 ServiceAccount

上面我们创建了一个只能访问某个命名空间下面的普通用户,我们前面也提到过 subjects 下面还有一种类型的主题资源:ServiceAccount,现在我们来创建一个集群内部的用户只能操作 kube-system 这个命名空间下面的 pods 和 deployments,首先来创建一个 ServiceAccount 对象:

apiVersion: v1kind: ServiceAccountmetadata:
  name: jixingxing-sa
  namespace: kube-system

然后新建一个 Role 对象:

apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata:
  name: jixingxing-sa-role
  namespace: kube-systemrules:- apiGroups: [""]  resources: ["pods"]  verbs: ["get", "watch", "list"]- apiGroups: ["apps"]  resources: ["deployments"]  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]

然后创建一个 RoleBinding 对象,将上面的 jixingxing-sa 和角色 jixingxing-sa-role 进行绑定:

kind: RoleBindingapiVersion: rbac.authorization.k8s.io/v1metadata:
  name: jixingxing-sa-rolebinding
  namespace: kube-systemsubjects:- kind: ServiceAccount
  name: jixingxing-sa
  namespace: kube-systemroleRef:
  kind: Role
  name: jixingxing-sa-role
  apiGroup: rbac.authorization.k8s.io

然后我们怎么去验证这个 ServiceAccount 呢?我们前面的课程中是不是提到过一个 ServiceAccount 会生成一个 Secret 对象和它进行映射,这个 Secret 里面包含一个 token,我们可以利用这个 token 去登录执行命令。

➜  ~ kubectl get secret -n kube-system |grep jixingxing-sa
jixingxing-sa-token-nxgqx                  kubernetes.io/service-account-token   3         47m
➜  ~ kubectl get secret jixingxing-sa-token-nxgqx -o jsonpath={.data.token} -n kube-system |base64 -d# 会生成一串很长的base64后的字符串

可以全局访问的 ServiceAccount

刚刚我们创建的 jixingxing-sa 这个 ServiceAccount 和一个 Role 角色进行绑定的,如果我们现在创建一个新的 ServiceAccount,需要他操作的权限作用于所有的 namespace,这个时候我们就需要使用到 ClusterRole 和 ClusterRoleBinding 这两种资源对象了。
同样,首先新建一个:

apiVersion: v1kind: ServiceAccountmetadata:
  name: jixingxing-sa2
  namespace: kube-system

然后创建一个 ClusterRoleBinding 对象:

kind: ClusterRoleBindingapiVersion: rbac.authorization.k8s.io/v1beta1metadata:
  name: jixingxing-sa2-clusterrolebindingsubjects:- kind: ServiceAccount
  name: jixingxing-sa2
  namespace: kube-systemroleRef:
  kind: ClusterRole
  name: cluster-admin
  apiGroup: rbac.authorization.k8s.io

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

相关文章

Logstash迁移ES集群

一、背景介绍   logstash 支持从一个 ES 集群中读取数据然后写入到另一个 ES 集群,因此可以使用 logstash 进行数据迁移,使用 logstash 进行迁移前,需要注意以下几点:需...

ES运维(八)添加IK分词器

ES运维(八)添加IK分词器

一、概述ES自带standard analyzer、simple analyzer、whitespace analyzer、stop analyzer、language analyzer、patter...

hive执行count和spark执行count结果不一致

hive执行count和spark执行count结果不一致

【组件版本】hive on mr、spark【问题现象】hive 执行count语句,结果条数为0,spark执行count语句能正常显示count数【详细描述】hive 执行count语句:显示co...

ubuntu20.04服务器安全策略设定

ubuntu20.04服务器安全策略设定

密码策略1、经核查,服务器用户身份标识唯一,口令存储在服务器中采用SHA512算法,服务器配置口令复杂度,口令要求8位以上,字母、数字、特殊字符组成,口令180天定期更换。# SHA512算法查看ca...

企业级大数据安全架构(四)

企业级大数据安全架构(四)

Ranger是支持审计功能的,安装时可以选择审计数据保存的位置,默认支持Solr和HDFS。HDFS的配置比较简单,这里就不赘述了,我们这里使用Ambari默认自带的Solr保存审计日志,下面部署So...

Hadoop3.2.4纠删码基础操作(二)

Hadoop3.2.4纠删码基础操作(二)

1、纠删码基础操作纠删码策略是与具体的路径(path)相关联的。也就是说,如果我们要使用纠删码,则要给一个具体的路径设置纠删码策略,后续,所有往此目录下存储的文件,都会执行此策略。1.首先我们在HDF...

发表评论    

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。