云原生技术Kubernetes之集群安全

举报
tea_year 发表于 2024/12/18 12:09:48 2024/12/18
【摘要】 集群安全认证Kubernetes API Server 组件是 Kubernetes 集群资源操作的唯一入口,它通过 HTTP RESTful 的形式暴露服务,允许不同的用户、外部组件等访问它。我们使用 curl 命令去模拟访问 apisever 请求过程中,发生了什么。Bash[root@node-01 ~]# curl https://172.17.0.93:6443/api/v1/na...

集群安全

认证

Kubernetes API Server 组件是 Kubernetes 集群资源操作的唯一入口,它通过 HTTP RESTful 的形式暴露服务,允许不同的用户、外部组件等访问它。我们使用 curl 命令去模拟访问 apisever 请求过程中,发生了什么。

Bash
[root@node-01 ~]# curl https://172.17.0.93:6443/api/v1/namespaces -k
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {},
  "status": "Failure",
  "message": "namespaces is forbidden: User \"system:anonymous\" cannot list resource \"namespaces\" in API group \"\" at the cluster scope",
  "reason": "Forbidden",
  "details": {
    "kind": "namespaces"
  },
  "code": 403
}

从上面返回的结果可以看出:

  • apiserver 识别这次请求的用户为 system:anonymous
  • apiserver 禁止了该用户 list namespaces,并返回 403

所以通过上面的测试大致了解 apiserver 的工作机制:

  • 首先,它会识别请求的用户是谁(AuthN
  • 然后,它会识别该用户具有什么样的权限(AuthZ

 

认证插件

 

鉴权

 

RBAC 权限控制

Kubernetes中 资源对象的操作都是通过 kube-apiserver 进行的,那么集群是怎样知道我们的请求就是合法的请求呢?这个就需要了解 Kubernetes 中另外一个非常重要的知识点了:RBAC(基于角色的权限控制)。

管理员可以通过 Kubernetes API 动态配置策略来启用RBAC,需要在 kube-apiserver 中添加参数--authorization-mode=RBAC,如果使用的kubeadm 安装的集群那么是默认开启了 RBAC 的,可以通过查看 Master 节点上 apiserver 的静态 Pod 定义文件:

Bash
cat /etc/kubernetes/manifests/kube-apiserver.yaml
...
    - --authorization-mode=Node,RBAC
...

 

API 对象

 

 

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

 

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

Bash
$ kubectl get --raw /apis/batch/v1 | python -m json.tool
{
    "apiVersion": "v1",
    "groupVersion": "batch/v1",
    "kind": "APIResourceList",
    "resources": [
        {
            "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 的访问:

Bash
$ kubectl proxy
Starting to serve on 127.0.0.1:8001

 

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

Bash
 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 POSTPUTDELETE  GET 在指定 PATH 路径上创建、更新、删除和检索操作,并使用 JSON 作为默认的数据交互格式。

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

YAML
apiVersion: apps/v1
kind: Deployment

 

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

 

RBAC  

上面我们介绍了 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 资源,和我们前面学习过的 PodDeployment 这些对象类似,都是我们集群的资源对象,所以同样的可以使用 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

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

Bash
username: dev-user
group: developer

 

创建用户凭证

Kubernetes 没有 User Account API 对象,不过要创建一个用户帐号的话也是挺简单的,可以使用 OpenSSL 命令创建 X509证书来创建一个 User

Bash
[root@node-01 dev-project]# openssl genrsa -out dev-user.key 2048
Generating RSA private key, 2048 bit long modulus
............+++
.....................+++
e is 65537 (0x10001)

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

Bash
openssl req -new -key dev-user.key -out dev-user.csr -subj "/CN=dev-user/O=developer"

然后找到我们 Kubernetes 集群的 CA 证书,如果使用的是 kubeadm 安装的集群,CA 相关证书位于 /etc/kubernetes/pki/ 目录下面,我们会利用该目录下面的 ca.crt  ca.key两个文件来批准上面的证书请求。生成最终的证书文件,我们这里设置证书的有效期为 3650 天:

Bash
[root@node-01 dev-project]# openssl x509 -req -in dev-user.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out dev-user.crt -days 3650
Signature ok
subject=/CN=dev-user/O=developer
Getting CA Private Key

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

Bash
[root@node-01 dev-project]# ls
dev-user.crt  dev-user.csr  dev-user.key

可以验证我们的证书是不是CA签发的

Bash
[root@node-01 dev-project]# openssl verify -CAfile  /etc/kubernetes/pki/ca.crt  dev-user.crt
dev-user.crt: OK

 

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

Bash
[root@node-01 dev-project]# kubectl config set-credentials dev-user --client-certificate=dev-user.crt --client-key=dev-user.key
User "dev-user" set.

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

Bash
[root@node-01 dev-project]# kubectl config set-context dev-user-context --cluster=kubernetes --namespace=dev-project --user=dev-user
Context "dev-user-context" created.

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

Bash
[root@node-01 dev-project]# kubectl  get pods --context=dev-user-context
Error from server (Forbidden): pods is forbidden: User "dev-user" cannot list resource "pods" in API group "" in the namespace "dev-project"

创建角色

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

YAML
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: dev-user-role
  namespace: dev-project
rules:
- apiGroups: ["", "apps"]
  resources: ["deployments", "replicasets", "pods"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] # 也可以使用['*']

其中 Pod 属于 core 这个 API Group,在 YAML 中用空字符就可以,而 Deployment ReplicaSet 现在都属于 apps 这个 API Group(如果不知道则可以用 kubectl explain 命令查看),所以 rules 下面的 apiGroups 就综合了这几个资源的 API Group["", "apps"],其中verbs 就是我们上面提到的可以对这些资源对象执行的操作,我们这里需要所有的操作方法,所以我们也可以使用['*']来代替。然后直接创建这个 Role

YAML
[root@node-01 useraccount]# kubectl apply -f dev-user-role.yaml
role.rbac.authorization.k8s.io/dev-user-role created

 

创建角色权限绑定

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

YAML
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: dev-user-rolebinding
  namespace: kube-system
subjects:
- kind: User
  name: dev-user
  apiGroup: ""
roleRef:
  kind: Role
  name: dev-user-role
  apiGroup: rbac.authorization.k8s.io

上面的 YAML 文件中我们看到了 subjects 字段,就是我们上面提到的用来操作集群的对象,对应上面的 User 帐号 dev-user,使用 kubectl 创建上面的资源对象:

Bash
[root@node-01 useraccount]# kubectl apply -f dev-user-rolebinding.yaml
rolebinding.rbac.authorization.k8s.io/dev-user-rolebinding created

 

验证

现在我们可以使用 dev-user-context 上下文来操作集群了:

Bash
[root@node-01 17-security]# kubectl  -n dev-project  apply -f nginx-dep.yaml  --context=dev-user-context
deployment.apps/nginx-dep created

[root@node-01 17-security]#  kubectl  -n dev-project  get pods --context=dev-user-context
NAME                         READY   STATUS    RESTARTS   AGE
nginx-dep-79f77dd7b7-gbbb9   1/1     Running   0          50s
nginx-dep-79f77dd7b7-p4bln   1/1     Running   0          50s

[root@node-01 17-security]# kubectl  -n dev-project  get deployment,rs --context=dev-user-context
NAME                        READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx-dep   2/2     2            2           2m32s

NAME                                   DESIRED   CURRENT   READY   AGE
replicaset.apps/nginx-dep-79f77dd7b7   2         2         2       2m32s

 

如果去获取其他的资源对象呢,可以看到并没有权限获取,因为我们没有为当前操作用户指定其他对象资源的访问权限,是符合我们的预期的。这样我们就创建了一个只有单个命名空间访问权限的普通 User

Bash
[root@node-01 17-security]#  kubectl  -n dev-project  get cm --context=dev-user-context
Error from server (Forbidden): configmaps is forbidden: User "dev-user" cannot list resource "configmaps" in API group "" in the namespace "dev-project"
[root@node-01 17-security]#
[root@node-01 17-security]#  kubectl  -n default  get cm --context=dev-user-context
Error from server (Forbidden): configmaps is forbidden: User "dev-user" cannot list resource "configmaps" in API group "" in the namespace "default"

 

 

只能访问某个 namespace ServiceAccount

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

Bash
kubectl -n default create sa namespace-user

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

YAML
apiVersion: v1
kind: ServiceAccount
metadata:
  name: namespace-user
  namespace: default

然后新建一个 Role 对象:(namespace-user-role.yaml)

YAML
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  namespace: default
  name: namespace-user-role
rules:
- apiGroups: [""]
  resources: ["pods", "pods/log"]
  verbs: ["get", "list"]
- apiGroups: ["", "apps"]
  resources: ["deployments", "replicasets", "pods"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]

 

YAML
kubectl apply -f namespace-user-role.yaml

然后创建一个 RoleBinding 对象,将上面的 namespace-user 和角色 namespace-user-role 进行绑定:(namespace-user-rolebinding.yaml)

YAML
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: namespace-user-rolebinding
  namespace: default
subjects:
- kind: ServiceAccount
  name: namespace-user
roleRef:
  kind: Role
  name: namespace-user-role
  apiGroup: rbac.authorization.k8s.io

 

验证

 

YAML
[root@node-01 .kube]# kubectl  --kubeconfig=namespace-user-conf  get pods
NAME                         READY   STATUS    RESTARTS   AGE
nginx-dep-587b6fd57c-dwn9g   1/1     Running   0          4d9h
redis-pv-sts-0               1/1     Running   0          4d6h
redis-pv-sts-1               1/1     Running   0          4d6h
[root@node-01 .kube]#
[root@node-01 .kube]#
[root@node-01 .kube]# kubectl  --kubeconfig=namespace-user-conf  get cm
Error from server (Forbidden): configmaps is forbidden: User "system:serviceaccount:default:namespace-user" cannot list resource "configmaps" in API group "" in the namespace "default"

 

可以全局访问的 ServiceAccount

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

YAML
apiVersion: v1
kind: ServiceAccount
metadata:
  name: cs-admin
  namespace: default

然后创建一个 ClusterRoleBinding 对象(cnych-clusterolebinding.yaml:

YAML
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: cs-admin-clusterrolebinding
subjects:
- kind: ServiceAccount
  name: cs-admin
  namespace: default
roleRef:
  kind: ClusterRole
  name: cluster-admin
  apiGroup: rbac.authorization.k8s.io

 

验证

YAML
[root@node-01 .kube]# kubectl  --kubeconfig=cs-admin-conf  -n kube-system get pods
NAME                                       READY   STATUS    RESTARTS      AGE
calico-kube-controllers-7b8458594b-nr8dz   1/1     Running   0             53d
calico-node-9w8tj                          1/1     Running   0             53d
coredns-6d8c4cb4d-phsh2                    1/1     Running   0             53d
coredns-6d8c4cb4d-zr8fx                    1/1     Running   0             53d
etcd-node                                  1/1     Running   0             53d
kube-apiserver-node                        1/1     Running   1 (24h ago)   6d10h
kube-controller-manager-node               1/1     Running   3 (24h ago)   53d
kube-proxy-pvsst                           1/1     Running   0             33d
kube-scheduler-node                        1/1     Running   3 (24h ago)   53d
nfs-client-provisioner-f95957677-jht7s     1/1     Running   3 (24h ago)   4d10h

 

 

What can i do

 

Bash

[root@master-01 17-security]# kubectl auth can-i --list --namespace=default --kubeconfig=namespace-user-conf
Resources                                       Non-Resource URLs                     Resource Names   Verbs
selfsubjectaccessreviews.authorization.k8s.io   []                                    []               [create]
selfsubjectrulesreviews.authorization.k8s.io    []                                    []               [create]
deployments                                     []                                    []               [get list watch create update patch delete]
pods                                            []                                    []               [get list watch create update patch delete]
replicasets                                     []                                    []               [get list watch create update patch delete]
deployments.apps                                []                                    []               [get list watch create update patch delete]
pods.apps                                       []                                    []               [get list watch create update patch delete]
replicasets.apps                                []                                    []               [get list watch create update patch delete]
pods/log                                        []                                    []               [get list]
                                                [/.well-known/openid-configuration]   []               [get]
                                                [/api/*]                              []               [get]
                                                [/api]                                []               [get]
                                                [/apis/*]                             []               [get]
                                                [/apis]                               []               [get]
                                                [/healthz]                            []               [get]
                                                [/healthz]                            []               [get]
                                                [/livez]                              []               [get]
                                                [/livez]                              []               [get]
                                                [/openapi/*]                          []               [get]
                                                [/openapi]                            []               [get]
                                                [/openid/v1/jwks]                     []               [get]
                                                [/readyz]                             []               [get]
                                                [/readyz]                             []               [get]
                                                [/version/]                           []               [get]
                                                [/version/]                           []               [get]
                                                [/version]                            []               [get]
                                                [/version]                            []               [get]

 

 

准入

Bash
apiVersion: v1
kind: ResourceQuota
metadata:
  name: object-counts
  namespace: default
spec:
  hard:
    configmaps: "1"

 

Bash
[root@node-01 admission]# kubectl  apply -f quota-cm.yaml
resourcequota/object-counts created

[root@node-01 admission]# kubectl  create cm cm-demo --from-literal=a=b
error: failed to create configmap: configmaps "cm-demo" is forbidden: exceeded quota: object-counts, requested: configmaps=1, used: configmaps=8, limited: configmaps=1

 

附录

Kubeconf

列出当前配置的 context

Bash
[root@node-01 .kube]# kubectl config get-contexts
CURRENT   NAME                          CLUSTER      AUTHINFO           NAMESPACE
          dev-user-context              kubernetes   dev-user           dev-project
*         kubernetes-admin@kubernetes   kubernetes   kubernetes-admin   

切换上下文

Bash
[root@node-01 .kube]#  kubectl config use-context dev-user-context
Switched to context "dev-user-context".
[root@node-01 .kube]#
[root@node-01 .kube]# kubectl config get-contexts
CURRENT   NAME                          CLUSTER      AUTHINFO           NAMESPACE
*         dev-user-context              kubernetes   dev-user           dev-project
          kubernetes-admin@kubernetes   kubernetes   kubernetes-admin   
[root@node-01 .kube]#
[root@node-01 .kube]# kubectl  -n kube-system get pods
Error from server (Forbidden): pods is forbidden: User "dev-user" cannot list resource "pods" in API group "" in the namespace "kube-system"

 

使用 --kubeconfig

Bash
[root@node-01 .kube]# kubectl  --kubeconfig=/root/.kube/namespace-user-conf  get pods
NAME                         READY   STATUS    RESTARTS   AGE
nginx-dep-587b6fd57c-dwn9g   1/1     Running   0          4d10h
redis-pv-sts-0               1/1     Running   0          4d6h
redis-pv-sts-1               1/1     Running   0          4d6h

 

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。