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 机构是第三方证书权威机构,认证步骤如下图:
- 服务器端向 CA 机构申请证书,CA 机构下发根证书、服务端证书、私钥给申请者
- 客户端向 CA 机构申请证书,CA 机构下发根证书、客户端证书、私钥给申请者
- 客户端向服务端发起请求,服务端下发服务端证书给客户端,客户端通过私钥进行解密,并利用服务端证书中的公钥认证证书信息比较证书中的信息,如果一致,则客户端认可服务端身份
- 客户端向服务端发送证书,服务端使用私钥进行解密,获得客户端证书公钥,并用公钥认证证书信息,确认客户端是否合法
- 两端协商好加密方案后,客户端产生一个随机密钥,通过协商好的方案加密该密钥,并发送该密钥给服务端,服务端收到密钥后,双方使用这个随机密钥进行信息传输。
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等一系列知识点的讲解,并且最后总