39. Kubernetes 集群安全机制

本章讲解知识点

  • API Server 认证管理
  • API Server 授权管理
  • Admission Control

<br>

1. API Server 认证管理

我们知道,Kubernetes 集群中所有资源的访问和变更都是通过 Kubernetes API Server 的 REST API 实现的,所以集群安全的关键点就在于如何识别并认证客户端身份,以及随后访问权限的授权这两个关键问题。

Kubernetes 集群有两种用户账号:第 1 种是集群内部的 Service Account;第 2 种是外部用的账号,可能是某个运维人员或外部应用的账号。Kubernetes 集群并不支持常规的个人账号,但拥有被 Kubernetes 集群的 CA 证书签名的有效证书,个人用户就可被授权访问 Kubernetes 集群了。

Kubernetes 集群提供了以下用户身份认证方式。

  • HTTPS 证书认证:基于 CA 根证书签名的双向数字证书认证方式。
  • HTTP Bearer Token 认证:通过一个 Token 识别合法用户。
  • 第三方认证。
  • Webhook Token 认证:通过外部 Webhook 服务进行认证。
  • Authenticating Proxy 认证:通过认证代理程序进行认证。

我们只讲最重要的 HTTPS 证书认证。

这里需要有一个 CA 证书,CA 是 KPI 系统中通信双方都信任的实体,被称为可信第三方。CA 作为可信第三方的重要条件之一,就是其行为具有非否认性。作为第三方而不是简单的上级,就必须让信任者有追究自己责任的能力。CA 通过证书证实他人的公钥信息,在证书上有 CA 的签名。如果用户因为信任证书而有了损失,证书就可以作为有效的证据用于追究 CA 的法律责任。CA 正是因为对责任的承诺,也被称为可信第三方。在证书中绑定了公钥数据和相应私钥拥有者的身份信息,并带有 CA 的数字签名;在证书中也包含了 CA 的名称,以便于依赖方找到 CA 的公钥,验证证书上的数字签名。

基于 CA 根证书签名的的双向数字认证方式,CA 机构是第三方证书权威机构,认证步骤如下图:

  1. 服务器端向 CA 机构申请证书,CA 机构下发根证书、服务端证书、私钥给申请者
  2. 客户端向 CA 机构申请证书,CA 机构下发根证书、客户端证书、私钥给申请者
  3. 客户端向服务端发起请求,服务端下发服务端证书给客户端,客户端通过私钥进行解密,并利用服务端证书中的公钥认证证书信息比较证书中的信息,如果一致,则客户端认可服务端身份
  4. 客户端向服务端发送证书,服务端使用私钥进行解密,获得客户端证书公钥,并用公钥认证证书信息,确认客户端是否合法
  5. 两端协商好加密方案后,客户端产生一个随机密钥,通过协商好的方案加密该密钥,并发送该密钥给服务端,服务端收到密钥后,双方使用这个随机密钥进行信息传输。

HTTPS 证书认证原理涉及计算机网络知识,需要补充相关知识。

<br>

2. API Server 授权管理

当客户端发起 API Server 调用时,API Server 内部要先进行用户认证,然后执行用户授权流程,即通过授权策略决定一个 API 调用是否合法。对合法用户进行授权并随后在用户访问时进行鉴权,是权限与安全系统中重要的一环。简单地说,就是授权就是授予不同用户不同的访问权限。API Server 目前支持以下授权策略:

  • AlwaysDeny:表示拒绝所有请求,一般用于测试。
  • AlwaysAllow:允许接收所有请求,如果集群不需要授权流程,则可以采用该策略,这也是Kubernetes的默认配置。
  • ABAC(Attribute-Based Access Control):基于属性的访问控制,表示使用用户配置的授权规则对用户请求进行匹配和控制。
  • Webhook:通过调用外部REST服务对用户进行授权。
  • RBAC:Role-Based Access Control,基于角色的访问控制。
  • Node:是一种专用模式,用于对kubelet发出的请求进行访问控制。

AlwaysDeny 和 AlwaysAllow 不会被用于实际生产中。我们只讲目前最实用也在用的授权模式:RBAC

在 Kubernetes 中,所有的 API 对象都存储在 Etcd 中。然而,对于这些对象的操作,必须通过访问 kube-apiserver 来实现。其中一个重要的原因是需要 APIServer 来进行授权工作。在 Kubernetes 项目中,RBAC(基于角色的访问控制)机制负责完成授权工作。

在这里,需要明确三个最基本的概念。

  • Role:角色,它其实是一组规则,定义了一组对 Kubernetes API 对象的操作权限。
  • Subject:被作用者,既可以是“人”,也可以是“机器”,也可以是在 Kubernetes 里定义的“用户”。
  • RoleBinding:定义了“被作用者”和“角色”的绑定关系。

而这三个概念,其实就是整个 RBAC 体系的核心所在。

先来讲解一下 Role。实际上,Role 本身就是一个 Kubernetes 的 API 对象,定义如下所示:

kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  namespace: mynamespace
  name: example-role
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "watch", "list"]

首先,这个 Role 对象指定了它能产生作用的 Namepace 是:mynamespace。这个 Role 对象的 rules 字段,就是它所定义的权限规则。在上面的例子里,这条规则的含义就是:允许“被作用者”,对 mynamespace 下面的 Pod 对象,进行 GET、WATCH 和 LIST 操作。

当然,RoleBinding 本身也是一个 Kubernetes 的 API 对象。它的定义如下所示:

kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: example-rolebinding
  namespace: mynamespace
subjects:
- kind: User
  name: example-user
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: example-role
  apiGroup: rbac.authorization.k8s.io

可以看到,这个 RoleBinding 对象里定义了一个 subjects 字段,即“被作用者”。它的类型是 User,即 Kubernetes 里的用户。这个用户的名字是 example-user。可是,在 Kubernetes 中,其实并没有一个叫作“User”的 API 对象。而且,我们在前面和部署使用 Kubernetes 的流程里,既不需要 User,也没有创建过 User。这个 User 到底是从哪里来的呢?实际上,Kubernetes 里的“User”,也就是“用户”,只是一个授权系统里的逻辑概念。它需要通过外部认证服务,比如 Keystone,来提供。或者,你也可以直接给 APIServer 指定一个用户名、密码文件。那么 Kubernetes 的授权系统,就能够从这个文件里找到对应的“用户”了。当然,在大多数私有的使用环境中,我们只要使用 Kubernetes 提供的内置“用户”,就足够了。

接下来,我们会看到一个 roleRef 字段。正是通过这个字段,RoleBinding 对象就可以直接通过名字,来引用我们前面定义的 Role 对象(example-role),从而定义了“被作用者(Subject)”和“角色(Role)”之间的绑定关系

Role 和 RoleBinding 对象都是 Namespaced 对象(Namespaced Object),它们对权限的限制规则仅在它们自己的 Namespace 内有效,roleRef 也只能引用当前 Namespace 里的 Role 对象。那么,对于非 Namespaced(Non-namespaced)对象(比如:Node),或者,某一个 Role 想要作用于所有的 Namespace 的时候,我们又该如何去做授权呢?这时候,我们就必须要使用 ClusterRole 和 ClusterRoleBinding 这两个组合了。这两个 API 对象的用法跟 Role 和 RoleBinding 完全一样。只不过,它们的定义里,没有了 Namespace 字段,如下所示:

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: example-clusterrole
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "watch", "list"]

kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: example-clusterrolebinding
subjects:
- kind: User
  name: example-user
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: example-clusterrole
  apiGroup: rbac.authorization.k8s.io

上面的例子里的 ClusterRole 和 ClusterRoleBinding 的组合,意味着名叫 example-user 的用户,拥有对所有 Namespace 里的 Pod 进行 GET、WATCH 和 LIST 操作的权限。Role 和 RoleBinding 的逻辑关系如下图:

更进一步地,在 Role 或者 ClusterRole 里面,如果要赋予用户 example-user 所有权限,那你就可以给它指定一个 verbs 字段的全集,如下所示:

verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]

这些就是当前 Kubernetes(v1.11)里能够对 API 对象进行的所有操作了。类似地,Role 对象的 rules 字段也可以进一步细化。比如,你可以只针对某一个具体的对象进行权限设置,如下所示:

rules:
- apiGroups: [""]
  resources: ["configmaps"]
  resourceNames: ["my-config"]
  verbs: ["get"]

这个例子就表示,这条规则的“被作用者”,只对名叫“my-config”的 ConfigMap 对象,有进行 GET 操作的权限。而正如我前面介绍过的,在大多数时候,我们其实都不太使用“用户”这个功能,而是直接使用 Kubernetes 里的“内置用户”。这个由 Kubernetes 负责管理的“内置用户”,正是我们前面曾经提到过的:ServiceAccount。接下来,通过一个具体的实例来讲解一下为 ServiceAccount 分配权限的过程。首先,我们要定义一个 ServiceAccount。它的 API 对象非常简单,如下所示:

apiVersion: v1
kind: ServiceAccount
metadata:
  namespace: mynamespace
  name: example-sa

可以看到,一个最简单的 ServiceAccount 对象只需要 Name 和 Namespace 这两个最基本的字段。然后,我们通过编写 RoleBinding 的 YAML 文件,来为这个 ServiceAccount 分配权限:

kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: example-rolebinding
  namespace: mynamespace
subjects:
- kind: ServiceAccount
  name: example-sa
  namespace: mynamespace
roleRef:
  kind: Role
  name: example-role
  apiGroup: rbac.authorization.k8s.io

可以看到,在这个 RoleBinding 对象里,subjects 字段的类型(kind),不再是一个 User,而是一个名叫 example-sa 的 ServiceAccount。而 roleRef 引用的 Role 对象,依然名叫 example-role,也就是一开始定义的 Role 对象。接着,我们用 kubectl 命令创建这三个对象:

$ kubectl create -f svc-account.yaml
$ kubectl create -f role-binding.yaml
$ kubectl create -f role.yaml

然后,我们来查看一下这个 ServiceAccount 的详细信息:

$ kubectl get sa -n mynamespace -o yaml
- apiVersion: v1
  kind: ServiceAccount
  metadata:
    creationTimestamp: 2018-09-08T12:59:17Z
    name: example-sa
    namespace: mynamespace
    resourceVersion: "409327"
    ...
  secrets:
  - name: example-sa-token-vmfg6

可以看到,Kubernetes 会为一个 ServiceAccount 自动创建并分配一个 Secret 对象,即:上述 ServiceAcount 定义里最下面的 secrets 字段。这个 Secret,就是这个 ServiceAccount 对应的、用来跟 APIServer 进行交互的授权文件,我们一般称它为:Token。Token 文件的内容一般是证书或者密码,它以一个 Secret 对象的方式保存在 Etcd 当中。这时候,用户的 Pod,就可以声明使用这个 ServiceAccount 了,比如下面这个例子:

apiVersion: v1
kind: Pod
metadata:
  namespace: mynamespace
  name: sa-token-test
spec:
  containers:
  - name: nginx
    image: nginx:1.7.9
  serviceAccount

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

云计算面试题全解析 文章被收录于专栏

本专刊适合于立志转行云计算的小白,有一定的编程、操作系统、计算机网络、数据结构、算法基础。 本专刊同时也适合于面向云计算(Docker + Kubernetes)求职的从业者。 本专刊囊括了云计算、VMWare、Docker、Kubernetes、Containerd等一系列知识点的讲解,并且最后总

全部评论

相关推荐

10-24 11:10
山西大学 Java
若梦难了:哥们,面试挂是很正常的。我大中厂终面挂,加起来快10次了,继续努力吧。
点赞 评论 收藏
分享
评论
1
3
分享
牛客网
牛客企业服务