十、API Server的访问控制

Kubernetes作为一个分布式集群的管理工具,保证集群的安全性是其一个重要的任务。API Server是集群内部各个组件通信的中介,也是外部控制的入口。所以Kubernetes的安全机制基本就是围绕保护API Server来设计的。

在第八章中,提到API服务器的安全防护流程,当请求到达 API 时,它会经历多个阶段,如下图所示:

image-20220109133214166

Kubernetes使用了认证(Authentication)、鉴权(Authorization)、准入控制(Admission Control)三步来保证API Server的安全。

1 认证

API服务器接收到的请求会经过一个认证插件的列表,列表中的每个插件都可以检查这个请求和尝试确定谁在发送这个请求。列表中的第一个插件可以提取请求中客户端的用户名、用户ID和组信息,并返回给API服务器。API服务器会停止调用剩余的认证插件井继续进入授权阶段。

认证分为两种:

  • 客户端认证:控制k8s组件对API服务器的访问,比如kubectl、Controller Manager、Scheduler、kubelet、kube-proxy
  • pod(更准确地说是运行在pod中的应用):控制pod的访问

客户端认证的常用方式如下:

  • HTTP Token 认证:通过一个 Token 来识别合法用户
  • HTTP 基本认证:通过 用户名+密码 的方式认证
  • HTTPS 证书认证:基于 CA 根证书签名的客户端身份认证方式

通过用户名密码认证方式麻烦且不是很安全,所以很少使用。而且密码和token都是单向认证,最安全的是证书签名的认证方式。

HTTPS证书的双向认证机制如下图所示:

image-20220110064506328

pod认证使用一种称为service accounts的机制,该机制被创建和存储在集群中作为ServiceAccount资源(曾经在第六章Secret中介绍过)。

1.1 客户端认证

有很多组件需要访问API服务器,如果都使用HTTPS双向认证会消耗很多资源,因此

  • 对于Controller Manager、Scheduler组件,它们与API Server在同一台机器,所以直接使用API Server的非安全端口访问--insecure-bind-address=127.0.0.1即可
  • kubectl、kubelet、kube-proxy 访问API Server都需要证书进行HTTPS双向认证

CA证书的颁发有以下两种:

  • 手动签发:你可以通过easyrsaopensslcfssl等工具以手工方式生成证书。
  • 自动签发:kubelet首次访问 API Server 时,使用 token 做认证,通过后,Controller Manager 会为kubelet生成一个证书,以后的访问都是用证书做认证了

1.2 pod认证

Pod中的容器访问API Server。因为Pod的创建、销毁是动态的,所以为它手动生成证书不可行。Kubenetes使用了Service Account解决Pod访问API Server的认证问题。

Kubernetes 设计了一种资源对象叫做 Secret,分为两类,一种是用于 ServiceAccount 的 service-account-token,另一种是用于保存用户自定义保密信息的Opaque。具体已经在第六章介绍过。

2 鉴权

上面认证过程,只是确认通信的双方都确认了对方是可信的,可以相互通信。而鉴权是确定请求方有哪些资源的权限。API Server 目前支持以下几种授权策略 (通过 API Server 的启动参数--authorization-mode设置)

可以使用的参数有:

  • --authorization-mode=ABAC 基于属性的访问控制(ABAC)模式允许你使用本地文件配置策略。
  • --authorization-mode=RBAC 基于角色的访问控制(RBAC)模式允许你使用 Kubernetes API创建和存储策略。
  • --authorization-mode=Webhook WebHook是一种 HTTP 回调模式,允许你使用远程 REST 端点管理鉴权。
  • --authorization-mode=Node 节点鉴权是一种特殊用途的鉴权模式,专门对 kubelet 发出的API请求执行鉴权。
  • --authorization-mode=AlwaysDeny 该标志阻止所有请求。仅将此标志用于测试。
  • --authorization-mode=AlwaysAllow 此标志允许所有请求。仅在你不需要API请求的鉴权时才使用此标志。

你可以选择多个鉴权模块。模块按顺序检查,以便较靠前的模块具有更高的优先级来允许或拒绝请求。

在Kubernetes1.8.0版本中,RBAC授权插件在很多集群上默认开启,它是现在最常用也是默认的鉴权插件。下面就来具体了解一下RBAC。

2.1 动词

因为API服务器对外暴露了REST接口, 用户可以通过向服务器发送HTTP请求来执行动作(当然,首先要通过认证),都有什么动作?

REST 客户端发送GET、POST、PUT、DELETE和其他类型的HTTP请求到特定的URL路径上,这些路径表示特定的REST资源。比如Pod、Service、Secret,等等。

例如RBAC这样的授权插件运行在API服务器中,它会决定一个客户端是否允许在请求的资源上执行请求的动词。下表是认证动词和HTTP方法之间的映射关系:

HTTP 动词 单一资源动词 集合动词
POST create N/A
GET, HEAD get list
PUT update N/A
PATCH patch N/A
DELETE delete deletecollection

Kubernetes有时使用专门的动词以对额外的权限进行鉴权。例如:

  • PodSecurityPolicy
    • policy API 组中 podsecuritypolicies 资源使用 use 动词
  • RBAC
    • rbac.authorization.k8s.io API 组中 rolesclusterroles 资源的 bindescalate 动词
  • 身份认证
    • 对核心 API 组中 usersgroupsserviceaccounts 以及 authentication.k8s.io API 组中的 userextras 所使用的 impersonate 动词

2.2 了解RBAC插件

顾名思义, RBAC授权插件将用户角色作为决定用户能否执行操作的关键因素。主体(可以是一个人、一个ServiceAccount,或者一组用户/ServiceAccount)和一个或多个角色相关联,每个角色被允许在特定的资源上执行特定的动词。通过RBAC插件管理授权是简单的,这一切都通过创建四种RBAC特定的Kubernetes资源来完成,我们会在下面学习这个过程。

相对其它访问控制方式,拥有以下优势:

  • 对集群中的资源和非资源均拥有完整的覆盖
  • 整个RBAC完全由几个API对象完成,同其它API对象一样,可以用kubectl或API进行操作
  • 可以在运行时进行调整,无需重启API Server(动态配置)

RBAC API声明了四种Kubernetes对象:Role、ClusterRole、RoleBinding 和 ClusterRoleBinding。授权规则就是通过这四种资源来进行配置的,它们可以分为两个组:

  • Role(角色)和 ClusterRole(集群角色),它们指定了在资源上可以执行哪些动词
  • RoleBinding(角色绑定)和ClusterRoleBinding(集群角色绑定),它们将上述角色绑定到特定的用户、组或ServiceAccounts上

角色定义了可以做什么操作,而绑定定义了谁可以做这些操作,另外这些权限是纯粹累加的(不存在拒绝某操作的规则):

image-20220110081141337

角色和集群角色,或者角色绑定和集群角色绑定之间的区别在于角色和角色绑定是名称空间的资源,而集群角色和集群角色绑定是集群级别的资源(不是名称空间的),如下图所示:

image-20220110081251066

从图中可以看到,多个角色绑定可以存在于单个名称空间中(对于角色也是如此)。同样地,可以创建多个集群绑定和集群角色。图中显示的另外一件事情是,尽管角色绑定是在名称空间下的,但它们也可以引用不在名称空间下的集群角色。

2.3 Role和ClusterRole

下面是一个位于 “default” 名称空间的 Role 的示例,可用来授予对pod的读访问权限:

1
2
3
4
5
6
7
8
9
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default # Role所在的名称空间,如果不写则默认为当前名称空间
name: pod-reader # Role的名字
rules:
- apiGroups: [""] # 每个资源类型属于一个API组,这里指定的就是API组,""表示为空,
resources: ["pods"] # 资源,必须使用复数形式
verbs: ["get", "watch", "list"] # 动词

ClusterRole 可以和 Role 相同完成授权。 因为 ClusterRole 属于集群范围,所以它也可以为以下资源授予访问权限:

  • 集群范围资源(比如节点Node)

  • 非资源端点(比如 /healthz

  • 跨名字空间访问的名字空间作用域的资源(如 Pods)

    比如,你可以使用 ClusterRole 来允许某特定用户执行 kubectl get pods --all-namespaces

下面是一个 ClusterRole 的示例,可用来为任一特定名字空间中的 Secret 授予读访问权限, 或者跨名字空间的访问权限(取决于该角色是如何绑定的):

1
2
3
4
5
6
7
8
9
10
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
# "namespace" 被忽略,因为 ClusterRoles 不受名字空间限制
name: secret-reader
rules:
- apiGroups: [""]
# 在 HTTP 层面,用来访问 Secret 对象的资源的名称为 "secrets"
resources: ["secrets"]
verbs: ["get", "watch", "list"]

2.4 RoleBinding和ClusterRoleBinding

角色定义了哪些操作可以执行,但没有指定谁可以执行这些操作。要做到这一点,必须将角色绑定一个到主体,通过创建一个RoleBinding/ClusterRoleBinding资源来实现。RoleBinding在指定的名称空间中执行授权,而 ClusterRoleBinding在集群范围执行授权。

一个 RoleBinding 可以引用同一的名称空间中的任何 Role。 或者,一个 RoleBinding 可以引用某 ClusterRole 并将该 ClusterRole 绑定到 RoleBinding 所在的名称空间。 如果你希望将某 ClusterRole 绑定到集群中所有名称空间,你要使用 ClusterRoleBinding。

下面的例子中的 RoleBinding 将 “pod-reader” Role 授予在 “default” 名称空间中的用户 “jane”。 这样,用户 “jane” 就具有了读取 “default” 名字空间中 pods 的权限。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: rbac.authorization.k8s.io/v1
# 此角色绑定允许 "jane" 读取 "default" 名字空间中的 Pods
kind: RoleBinding
metadata:
name: read-pods
namespace: default
subjects:
# 你可以指定不止一个“subject(主体)”
- kind: User
name: jane # "name" 是区分大小写的
apiGroup: rbac.authorization.k8s.io
roleRef:
# "roleRef" 指定与某 Role 或 ClusterRole 的绑定关系
kind: Role # 此字段必须是 Role 或 ClusterRole
name: pod-reader # 此字段必须与你要绑定的 Role 或 ClusterRole 的名称匹配
apiGroup: rbac.authorization.k8s.io

例如,尽管下面的 RoleBinding 引用的是一个 ClusterRole,”dave”只能访问 “development” 名称空间中的 Secrets 对象,因为 RoleBinding 所在的名称空间是 “development”。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: rbac.authorization.k8s.io/v1
# 此角色绑定使得用户 "dave" 能够读取 "development" 名字空间中的 Secrets
# 你需要一个名为 "secret-reader" 的 ClusterRole
kind: RoleBinding
metadata:
name: read-secrets
# RoleBinding 的名字空间决定了访问权限的授予范围。
# 这里隐含授权仅在 "development" 名字空间内的访问权限。
namespace: development
subjects: # 主体
- kind: User
name: dave # 'name' 是区分大小写的
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: secret-reader
apiGroup: rbac.authorization.k8s.io

要跨整个集群完成访问权限的授予,你可以使用一个 ClusterRoleBinding。 下面的 ClusterRoleBinding 允许 “manager” 组内的所有用户访问任何名字空间中的 Secrets。

1
2
3
4
5
6
7
8
9
10
11
12
13
apiVersion: rbac.authorization.k8s.io/v1
# 此集群角色绑定允许 “manager” 组中的任何人访问任何名字空间中的 secrets
kind: ClusterRoleBinding
metadata:
name: read-secrets-global
subjects:
- kind: Group
name: manager # 'name' 是区分大小写的
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: secret-reader
apiGroup: rbac.authorization.k8s.io

创建了绑定之后,你不能再修改绑定对象所引用的 Role 或 ClusterRole。 试图改变绑定对象的 roleRef 将导致合法性检查错误。 如果你想要改变现有绑定对象中 roleRef 字段的内容,必须删除重新创建绑定对象。

2.5 对资源的引用

在 Kubernetes API 中,大多数资源都是使用对象名称的字符串表示来呈现与访问的。 例如,对于 Pod 应使用 “pods”。 RBAC 使用对应 API 端点的 URL 中呈现的名字来引用资源。 有一些 Kubernetes API 涉及子资源(subresource),例如 Pod 的日志。 对 Pod 日志的请求看起来像这样:

1
GET /api/v1/namespaces/{namespace}/pods/{name}/log

在这里,pods 对应名字空间作用域的 Pod 资源,而 logpods 的子资源。 在 RBAC 角色表达子资源时,使用斜线(/)来分隔资源和子资源。 要允许某主体读取 pods 同时访问这些 Pod 的 log 子资源,你可以这么写:

1
2
3
4
5
6
7
8
9
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: pod-and-pod-logs-reader
rules:
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list"]

对于某些请求,也可以通过 resourceNames 列表按名称引用资源。 在指定时,可以将请求限定为资源的单个实例。 下面的例子中限制可以 “get” 和 “update” 一个名为 my-configmap 的 ConfigMap:

1
2
3
4
5
6
7
8
9
10
11
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: configmap-updater
rules:
- apiGroups: [""]
# 在 HTTP 层面,用来访问 ConfigMap 的资源的名称为 "configmaps"
resources: ["configmaps"]
resourceNames: ["my-configmap"]
verbs: ["update", "get"]

2.6 对主体的引用

RoleBinding 或者 ClusterRoleBinding 可绑定角色到某主体(Subject)上。 主体可以是组(Group),用户(User)或者服务账户( ServiceAccount)。

Kubernetes 用字符串来表示用户名。 用户名可以是普通的用户名,像 “alice”;或者是邮件风格的名称,如 “bob@example.com“, 或者是以字符串形式表达的数字 ID。

前缀 system: 是 Kubernetes 系统保留的,所以你要确保所配置的用户名或者组名不能出现上述 system: 前缀。 除了对前缀的限制之外,RBAC 鉴权系统不对用户名格式作任何要求。

下面示例是 RoleBinding 中的片段,仅展示其 subjects 的部分。

1
2
3
4
subjects:
- kind: User
name: "alice@example.com"
apiGroup: rbac.authorization.k8s.io

对于名称为 frontend-admins 的用户组:

1
2
3
4
subjects:
- kind: Group
name: "frontend-admins"
apiGroup: rbac.authorization.k8s.io

对于 kube-system 名字空间中的默认服务账户:

1
2
3
4
subjects:
- kind: ServiceAccount
name: default
namespace: kube-system

2.7 默认Roles和Role Bindings

API 服务器创建一组默认的 ClusterRole 和 ClusterRoleBinding 对象。 这其中许多是以 system: 为前缀的,用以标识对应资源是直接由集群控制面管理的。 所有的默认 ClusterRole 和 ClusterRoleBinding 都有 kubernetes.io/bootstrapping=rbac-defaults 标签。

修改名称包含 system: 前缀的 ClusterRole 和 ClusterRoleBinding 时要格外小心。 对这些资源的更改可能导致集群无法继续工作。

查看集群角色命令:

1
kubectl get clusterroles

结果如下:

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
NAME                                                                   AGE
admin 30h
cluster-admin 30h
edit 30h
flannel 30h
ingress-nginx 29h
system:aggregate-to-admin 30h
system:aggregate-to-edit 30h
system:aggregate-to-view 30h
system:auth-delegator 30h
system:basic-user 30h
system:certificates.k8s.io:certificatesigningrequests:nodeclient 30h
system:certificates.k8s.io:certificatesigningrequests:selfnodeclient 30h
system:controller:attachdetach-controller 30h
system:controller:certificate-controller 30h
system:controller:clusterrole-aggregation-controller 30h
system:controller:cronjob-controller 30h
system:controller:daemon-set-controller 30h
system:controller:deployment-controller 30h
system:controller:disruption-controller 30h
system:controller:endpoint-controller 30h
system:controller:expand-controller 30h
system:controller:generic-garbage-collector 30h
system:controller:horizontal-pod-autoscaler 30h
system:controller:job-controller 30h
system:controller:namespace-controller 30h
system:controller:node-controller 30h
system:controller:persistent-volume-binder 30h
system:controller:pod-garbage-collector 30h
system:controller:pv-protection-controller 30h
system:controller:pvc-protection-controller 30h
system:controller:replicaset-controller 30h
system:controller:replication-controller 30h
system:controller:resourcequota-controller 30h
system:controller:route-controller 30h
system:controller:service-account-controller 30h
system:controller:service-controller 30h
system:controller:statefulset-controller 30h
system:controller:ttl-controller 30h
system:coredns 30h
system:csi-external-attacher 30h
system:csi-external-provisioner 30h
system:discovery 30h
system:heapster 30h
system:kube-aggregator 30h
system:kube-controller-manager 30h
system:kube-dns 30h
system:kube-scheduler 30h
system:kubelet-api-admin 30h
system:node 30h
system:node-bootstrapper 30h
system:node-problem-detector 30h
system:node-proxier 30h
system:persistent-volume-provisioner 30h
system:public-info-viewer 30h
system:volume-scheduler 30h
view 30h

一些默认的 ClusterRole 不是以前缀 system: 开头的。这些是面向用户的角色。 它们包括超级用户(Super-User)角色(cluster-admin)、 使用 ClusterRoleBinding 在集群范围内完成授权的角色(cluster-status)、 以及使用 RoleBinding 在特定名字空间中授予的角色(admineditview)。

查看集群角色绑定命令:

1
kubectl get clusterrolebindings

结果如下:

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
35
36
37
38
39
40
41
42
43
44
NAME                                                   AGE
cluster-admin 30h
flannel 30h
ingress-nginx 29h
kubeadm:kubelet-bootstrap 30h
kubeadm:node-autoapprove-bootstrap 30h
kubeadm:node-autoapprove-certificate-rotation 30h
kubeadm:node-proxier 30h
system:basic-user 30h
system:controller:attachdetach-controller 30h
system:controller:certificate-controller 30h
system:controller:clusterrole-aggregation-controller 30h
system:controller:cronjob-controller 30h
system:controller:daemon-set-controller 30h
system:controller:deployment-controller 30h
system:controller:disruption-controller 30h
system:controller:endpoint-controller 30h
system:controller:expand-controller 30h
system:controller:generic-garbage-collector 30h
system:controller:horizontal-pod-autoscaler 30h
system:controller:job-controller 30h
system:controller:namespace-controller 30h
system:controller:node-controller 30h
system:controller:persistent-volume-binder 30h
system:controller:pod-garbage-collector 30h
system:controller:pv-protection-controller 30h
system:controller:pvc-protection-controller 30h
system:controller:replicaset-controller 30h
system:controller:replication-controller 30h
system:controller:resourcequota-controller 30h
system:controller:route-controller 30h
system:controller:service-account-controller 30h
system:controller:service-controller 30h
system:controller:statefulset-controller 30h
system:controller:ttl-controller 30h
system:coredns 30h
system:discovery 30h
system:kube-controller-manager 30h
system:kube-dns 30h
system:kube-scheduler 30h
system:node 30h
system:node-proxier 30h
system:public-info-viewer 30h
system:volume-scheduler 30h

在每次启动时,API 服务器都会更新默认 ClusterRole 以添加缺失的各种权限,并更新 默认的 ClusterRoleBinding 以增加缺失的各类主体。 这种自动协商机制允许集群去修复一些不小心发生的修改,并且有助于保证角色和角色绑定 在新的发行版本中有权限或主体变更时仍然保持最新。

如果要禁止此功能,请将默认 ClusterRole 以及 ClusterRoleBinding 的 rbac.authorization.kubernetes.io/autoupdate 注解设置成 false。 注意,缺少默认权限和角色绑定主体可能会导致集群无法正常工作。

如果基于 RBAC 的鉴权机制被启用,则自动协商功能默认是被启用的。

有关于更多信息,比如每个ClusterRole的功能描述,详见官方文档

3 准入

准入控制器是一段代码,它会在请求通过认证和授权之后、对象被持久化之前拦截到达 API 服务器的请求。控制器由列表组成,如果请求的内容在列表中,就通过;如果请求的内容不在列表就拒绝。

准入控制器有很多,如下所列,详见官方文档: