3.1 Kubernetes 授权简介
在 Kubernetes 早期的版本中,如果你通过某种方式从集群中的一个 Pod 获得了 token,就可以在集群中使用这个 token 执行任何操作。所以从 Kubernetes1.8.0 版本开始,为了提高集群的安全性,RBAC 授权插件在集群上是默认开启的,RBAC 会阻止未授权的账户查看和修改集群状态。只有授权 ServiceAccount 额外的权限才有查看和修改的能力。
在 kubernetes 集群中也可以配置多个授权插件,只要其中一个授权插件验证通过,就算是通过了集群的授权。具体流程如下:
API Server 接收请求,读取请求数据中的多种属性,生成一个访问策略对象,对于没有的属性设置为默认值。然后将访问策略对象与授权插件中的所有访问策略对象进行逐条匹配,只要有一个策略对象匹配成功,那么该请求就通过了授权,否则就会终止调用流程并返回客户端错误码。
访问策略对象包含的属性有:
- user、group、extra。
- API、请求方法(get、post、update、patch、delete)、请求路径(/api)。
- 请求资源和子资源。
- Namespace。
- API Group。
目前 API Server 支持的授权策略如下:
- AlwaysDeny:拒绝所有请求,一般用于测试。
- AlwaysAllow:允许接收所有请求,当集群不需要授权时,采用这个策略。
- ABAC:Attribute-Based Access Control,基于属性的访问控制,使用账户配置的授权规则对账户请求进行匹配和控制。
- Webhook:调用外部 REST 服务对账户进行授权。
- RBAC:Role-Based Access Control,基于角色的访问控制。
- Node:对 kubelet 发出的请求进行访问控制。
其中,Node 和 RBAC 是默认开启的策略。
3.2 RBAC 授权插件
RBAC 授权模式是 kubernetes 系统最常用的、默认的授权模式详细讲解。
使用 RBAC 授权模式的好处在于:
- 覆盖很完整,包括集群中的资源和非资源权限。
- 使用 4 个 API 对象就可以完成对于 RBAC 授权模式的定义。
- 调整了授权模式以后,不需要重新启动 API Server。
Kubernetes API 服务器使用授权插件检查是否允许账户请求的动作执行,API 服务器接口设置为 RESTful 风格,账户通过向服务器发送 HTTP 请求来执行动作,在请求中会包含认证凭证来进行认证(比如:认证 token、账户名和密码或者客户端证书)。
Kubernetes 的一些常用请求动作:
- 获取 Pod(get pod):动词 get 映射到 HTTP 方法上为 GET,名词 pod 为 Kubernetes 上的资源。
- 创建服务(create service):动词 create 映射到 HTTP 方法上为 POST,名词 service 为 Kubernetes 上的资源。
- 更新密钥(update secret):动词 update 映射到 HTTP 方法上为 PUT,名词 secret 为 Kubernetes 上的资源。
下面的表格展示的是认证动词和 HTTP 方法之间的映射关系:
| HTTP 方法 | 单一资源的动词 | 集合的动词 |
|---|---|---|
| GET、HEAD | get(watch 用于监听) | list(watch) |
| POST | create | n/a |
| PUT | update | n/a |
| PATCH | patch | n/a |
| DELETE | delete | deletecollection |
RBAC 授权插件将账户角色作为决定账户是否能够执行操作的关键因素。账户(一个 UserAccount、一个 ServiceAccount、一组 UserAccount、或一组 ServiceAccount)需要与一个(或多个)角色关联才有权限执行对应的操作,每个角色被允许在特定的资源上执行特定的动作。如果账户没有某个角色,那么也就没有该角色所对应的动作权限。
通过创建 4 种特定的 Kubernetes 资源就可以完成使用 RBAC 插件进行管理授权,这 4 种资源又可以被分为两组:
- Role(角色)和 ClusterRole(集群角色):它们确定了在资源上可以执行哪些动词。
- RoleBinding(角色绑定) 和 ClusterRoleBinding(集群角色绑定):将角色绑定到特定的 UserAccounts、组或 ServiceAccounts 上。
简单的说,角色确定了可以执行哪些操作,而角色绑定确定了谁可以执行这些操作。
角色和角色绑定是命名空间的资源,集群角色和集群角色绑定是集群级别的资源。
3.3 Role 和 RoleBinding
首先要确保 RBAC 授权插件在集群中已经开启,并且是唯一配置生效的授权插件。
本次实验中在两个不同的命名空间运行具有相同名称的 Pod,每个 Pod 中运行一个基于 kubectl-proxy 镜像生成的容器,可以直接在容器中使用 kubectl exec 运行 curl 命令,代理会负责验证 HTTPS,进而可以观察 API 服务器的安全授权。
执行如下命令创建命名空间 foo 和 bar,并创建 Pod:
$ kubectl create namespace foo
namespace/foo created
$ kubectl run test --image=luksa/kubectl-proxy -n foo
deployment.apps/test created
$ kubectl create namespace bar
namespace/bar created
$ kubectl run test --image=luksa/kubectl-proxy -n bar
deployment.apps/test created
在一个终端中执行如下命令,在 foo 命名空间中运行 pod 中的 shell,然后验证 RBAC 是否有效的阻止了 pod 读取集群状态:
$ kubectl get pod -n foo
NAME READY STATUS RESTARTS AGE
test-7b4bb6b9ff-gx67l 1/1 Running 0 3m
$ kubectl exec -n foo -it test-7b4bb6b9ff-gx67l sh
/ # curl localhost:8001/api/v1/namespaces/foo/services
{
"kind": "Status",
"apiVersion": "v1",
"metadata": {
},
"status": "Failure",
"message": "services is forbidden: User \"system:serviceaccount:foo:default\" cannot list resource \"services\" in API group \"\" in the namespace \"foo\"",
"reason": "Forbidden",
"details": {
"kind": "services"
},
"code": 403
}
然后新开一个终端执行如下命令,在 bar 命名空间中运行 pod 中的 shell:
$ kubectl get pod -n bar
NAME READY STATUS RESTARTS AGE
test-7b4bb6b9ff-lg6gr 1/1 Running 0 5m46s
$ kubectl exec -n bar -it test-7b4bb6b9ff-lg6gr sh
/ # curl localhost:8001/api/v1/namespaces/foo/services
{
"kind": "Status",
"apiVersion": "v1",
"metadata": {
},
"status": "Failure",
"message": "services is forbidden: User \"system:serviceaccount:bar:default\" cannot list resource \"services\" in API group \"\" in the namespace \"foo\"",
"reason": "Forbidden",
"details": {
"kind": "services"
},
"code": 403
}
kubectl proxy 进程监听的地址为:localhost:8001,这个进程会接收请求并把请求发送给 API 服务器,并且以该命名空间中默认使用的 ServiceAccount 进行身份认证。根据响应结果可以看出权限验证失败了,RBAC 插件已生效,因为 ServiceAccount 默认是不能列出或是修改任何资源的。
Role 资源定义了哪些操作可以在哪些资源上执行,我们定义一个 Role,它允许账户获取并列出 foo 命名空间中的服务。
新建 service-reader.yaml 文件并向其中写入如下代码:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: foo
name: service-reader
rules:
- apiGroups: [""]
verbs: ["get", "list"]
resources: ["services"]
在上面的这个代码中,namespace 表示的是 Role 所在的命名空间;因为获取的是 service,它是 apiGroup 中的核心资源,它是没有 apiGroup 名的,所以这里 apiGroups 为空;verbs: ["get", "list"] 表示的是通过名字获取(get) 独立的 Service 并且列出(list) 所有允许的服务;resources: ["services"] 表示需要获取的资源,注意资源都必须使用复数形式。
现在在 foo 命名空间中创建角色:
$ kubectl create -f service-reader.yaml
role.rbac.authorization.k8s.io/service-reader created
当然也可以使用 kubectl create role 命令创建 service-reader 角色,现在来创建 bar 命名空间中的角色:
$ kubectl create role service-reader --verb=get --verb=list --resource=service -n bar
role.rbac.authorization.k8s.io/service-reader created
接下来将角色绑定到 ServiceAccount 上,RoleBinding 资源就可以实现将角色绑定到主体上。
执行如下命令将角色绑定到 default ServiceAccount 上:
$ kubectl create rolebinding test --role=service-reader --serviceaccount=foo:default -n foo
rolebinding.rbac.authorization.k8s.io/test created
如果要绑定角色到一个账户上,可以使用 --user 参数指定账户名;如果要绑定到组,可以使用 --group 参数。
可以看到绑定已经成功,现在我们再尝试一次在 foo 命名空间中获取 pod 中的 service:
$ kubectl exec -n foo -it test-7b4bb6b9ff-gx67l sh
/ # curl localhost:8001/api/v1/namespaces/foo/services
{
"kind": "ServiceList",
"apiVersion": "v1",
"metadata": {
"selfLink": "/api/v1/namespaces/foo/services",
"resourceVersion": "1787627"
},
"items": []
}
现在 default ServiceAccount 就通过了 RBAC 授权插件的检查并能够成功获取数据了。
由于没有对 bar 命名空间执行任何绑定操作,所以这个命名空间依然是不能查看自己命名空间的 services 服务列表,那就更没有权限查看 foo 命名空间的服务列表。现在修改 foo 命名空间的 RoleBinding 并添加 bar 命名空间中的 default ServiceAccount。
执行如下命令:
$ kubectl edit rolebinding test -n foo
然后在列出的 subjects 下增加如下 3 行:
subjects:
---
- kind: ServiceAccount
name: default
namespace: bar
现在尝试在 bar 命名空间下的容器中运行如下命令,查看是否能够列出 foo 命名空间中的服务:
$ kubectl exec -n bar -it test-7b4bb6b9ff-lg6gr sh
/ # curl localhost:8001/api/v1/namespaces/foo/services
{
"kind": "ServiceList",
"apiVersion": "v1",
"metadata": {
"selfLink": "/api/v1/namespaces/foo/services",
"resourceVersion": "1788447"
},
"items": []
}
可以看到操作执行成功。
那么截止目前,我们的 RBAC 资源状态如下:
在 foo 命名空间中有一个 RoleBinding,它引用了相同命名空间下的 service-reader 角色,并且绑定了 foo 和 bar 命名空间中的 default ServiceAccount。
3.4 ClusterRole 和 ClusterRoleBinding
前面提到过 Role 和 RoleBinding 都是命名空间的资源,但是有一些特定的资源是不在命名空间中的,比如:Node、Namespace 等,API 服务器对外也暴露了一些不表示资源的 URL 路径,比如:/healthz 等,这个时候就需要 ClusterRole 了。
ClusterRole 是一种集群级别的资源,它允许访问没有命名空间的资源以及非资源型的 URL,也可以作为单个命名空间内部绑定的公共角色,这样可以避免在每个命名空间中定义相同的角色。
可以使用如下命令创建一个名为 pv-reader 的 ClusterRole:
$ kubectl create clusterrole pv-reader --verb=get,list --resource=persistentvolumes
clusterrole.rbac.authorization.k8s.io/pv-reader created
首先在 foo 命名空间下验证是否可以列出 PersistentVolume:
$ kubectl exec -n foo -it test-7b4bb6b9ff-gx67l sh
/ # curl localhost:8001/api/v1/persistentvolumes
{
"kind": "Status",
"apiVersion": "v1",
"metadata": {
},
"status": "Failure",
"message": "persistentvolumes is forbidden: User \"system:serviceaccount:foo:default\" cannot list resource \"persistentvolumes\" in API group \"\" at the cluster scope",
"reason": "Forbidden",
"details": {
"kind": "persistentvolumes"
},
"code": 403
}
可以看到是不允许列出的,接下来我们创建 ClusterRoleBinding:
$ kubectl create clusterrolebinding pv-test --clusterrole=pv-reader --serviceaccount=foo:default
clusterrolebinding.rbac.authorization.k8s.io/pv-test created
现在再来查看是否拥有权限:
$ kubectl exec -n foo -it test-7b4bb6b9ff-gx67l sh
/ # curl localhost:8001/api/v1/persistentvolumes
{
"kind": "PersistentVolumeList",
"apiVersion": "v1",
"metadata": {
"selfLink": "/api/v1/persistentvolumes",
"resourceVersion": "1789384"
},
"items": [
{
"metadata": {
"name": "local-pv1",
"selfLink": "/api/v1/persistentvolumes/local-pv1",
"uid": "41004996-b49b-4029-8972-09267a9009bb",
"resourceVersion": "824807",
...
"claimRef": {
"kind": "PersistentVolumeClaim",
"namespace": "kubesphere-system",
"name": "minio",
"uid": "fdc79a14-039d-4a6d-acad-6d933f8ced65",
"apiVersion": "v1",
"resourceVersion": "19859"
},
"persistentVolumeReclaimPolicy": "Retain",
"storageClassName": "nfs-storage",
"volumeMode": "Filesystem"
},
"status": {
"phase": "Released"
}
}
]
}
允许访问非资源型的 URL
在 API 服务器中也包含一些非资源型的 URL,访问这些 URL 也是需要经过授权的,在预定义的集群角色和集群角色绑定中,有一组名为 system:discover 的 ClusterRole 及同名的 ClusterRoleBinding 可以帮助我们自动完成这些授权过程。
使用如下命令可以查看 system:discover ClusterRole:
$ kubectl get clusterrole system:discovery -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
annotations:
...
resourceVersion: "44"
selfLink: /apis/rbac.authorization.k8s.io/v1/clusterroles/system%3Adiscovery
uid: 7dbc8a77-e719-42f2-8062-e41a4d9821ed
rules:
- nonResourceURLs:
- /api
- /api/*
- /apis
- /apis/*
- /healthz
- /livez
- /openapi
- /openapi/*
- /readyz
- /version
- /version/
verbs:
- get
从详情中可以看出:ClusterRole 引用的是 URL 路径而非资源,同时对于这些 URL 只允许使用 GET 方法。
接下来执行如下命令再来查看一下 system:discover ClusterRoleBinding:
$ kubectl get clusterrolebinding system:discovery -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
annotations:
...
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:discovery
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: Group
name: system:authenticated
从结果中可以看出 system:discover ClusterRoleBinding 绑定到了 system:authenticated 组,凡是通过认证的账户都可以访问列在 system:discover ClusterRole 中的 URL。
3.5 默认的 ClusterRole 和 ClusterRoleBinding
Kubernetes 集群中也提供了一组默认的 ClusterRole 和 ClusterRoleBinding,每次 API 服务启动的时候都会更新它们,执行如下命令可以查看默认的集群角色和绑定:
$ kubectl get clusterroles
NAME AGE
admin 8d
cluster-admin 8d
edit 8d
pv-reader 8m
system:aggregate-to-admin 8d
system:aggregate-to-edit 8d
system:aggregate-to-view 8d
system:auth-delegator 8d
system:aws-cloud-provider 8d
system:basic-user 8d
system:certificates.k8s.io:certificatesigningrequests:nodeclient 8d
system:certificates.k8s.io:certificatesigningrequests:selfnodeclient 8d
system:controller:attachdetach-controller 8d
system:controller:certificate-controller 8d
system:controller:clusterrole-aggregation-controller 8d
system:controller:cronjob-controller 8d
system:controller:daemon-set-controller 8d
system:controller:deployment-controller 8d
system:controller:disruption-controller 8d
system:controller:endpoint-controller 8d
system:controller:generic-garbage-collector 8d
system:controller:horizontal-pod-autoscaler 8d
system:controller:job-controller 8d
system:controller:namespace-controller 8d
system:controller:node-controller 8d
system:controller:persistent-volume-binder 8d
system:controller:pod-garbage-collector 8d
system:controller:pv-protection-controller 8d
system:controller:pvc-protection-controller 8d
system:controller:replicaset-controller 8d
system:controller:replication-controller 8d
system:controller:resourcequota-controller 8d
system:controller:route-controller 8d
system:controller:service-account-controller 8d
system:controller:service-controller 8d
system:controller:statefulset-controller 8d
system:controller:ttl-controller 8d
system:coredns 8d
system:csi-external-attacher 8d
system:csi-external-provisioner 8d
system:discovery 8d
system:heapster 8d
system:kube-aggregator 8d
system:kube-controller-manager 8d
system:kube-dns 8d
system:kube-scheduler 8d
system:kubelet-api-admin 8d
system:node 8d
system:node-bootstrapper 8d
system:node-problem-detector 8d
system:node-proxier 8d
system:persistent-volume-provisioner 8d
system:volume-scheduler 8d
view 8d
$ kubectl get clusterrolebindings
NAME AGE
...
system:basic-user 84d
system:controller:attachdetach-controller 84d
system:controller:certificate-controller 84d
system:controller:clusterrole-aggregation-controller 84d
system:controller:cronjob-controller 84d
system:controller:daemon-set-controller 84d
system:controller:deployment-controller 84d
system:controller:disruption-controller 84d
system:controller:endpoint-controller 84d
system:controller:expand-controller 84d
system:controller:generic-garbage-collector 84d
system:controller:horizontal-pod-autoscaler 84d
system:controller:job-controller 84d
system:controller:namespace-controller 84d
system:controller:node-controller 84d
system:controller:persistent-volume-binder 84d
system:controller:pod-garbage-collector 84d
system:controller:pv-protection-controller 84d
system:controller:pvc-protection-controller 84d
system:controller:replicaset-controller 84d
system:controller:replication-controller 84d
system:controller:resourcequota-controller 84d
system:controller:route-controller 84d
...
其中,admin、cluster-admin、edit 和 view ClusterRole 是最重要的角色,它们应该绑定到账户自定义 pod 中的 ServiceAccount 上。
- admin ClusterRole 可以赋予一个命名空间全部的控制权。有这个 ClusterRole 的主体可以读取和修改命名空间中的任何资源,除了 ResourceQuota 和命名空间资源本身。
- cluster-admin ClusterRole 可以赋予集群完全控制权限。如果想要修改 admin ClusterRole 无法读取的资源,就可以创建一个指向 cluster-admin ClusterRole 的 RoleBinding,这样的话 RoleBinding 中包含的账户就可以完全控制创建 RoleBinding 所在命名空间上的方方面面。
- edit ClusterRole 允许修改一个命名空间中的资源、以及读取和修改 Secret。但是不允许查看或修改 Role 和 RoleBinding。
- view ClusterRole 允许读取一个命名空间中的大多数资源,除了 Role、RoleBinding 和 Secret。
- 其它默认的 ClusterRole,这些集群角色以 system: 为前缀,主要用于各种 Kubernetes 组件中,这些集群角色都有一个对应的 ClusterRoleBinding,它会绑定到集群组件用来身份认证的账户上。
最后,我们总结一下关于 Role、ClusterRole、Rolebinding、ClusterRoleBinding 在访问不同的资源类型时,应该如何组合使用,具体如下表所示:
| 访问的资源 | 使用的角色类型 | 使用的绑定类型 |
|---|---|---|
| 集群级别的资源(Nodes、PersistentVolumes 等) | ClusterRole | ClusterRoleBinding |
| 非资源型 URL(/api、/healthz 等) | ClusterRole | ClusterRoleBinding |
| 在任何命名空间中的资源(和跨所有命名空间的资源) | ClusterRole | ClusterRoleBinding |
| 在具体命名空间中的资源(在多个命名空间中重用这个相同的 ClusterRole) | ClusterRole | RoleBinding |
| 在具体命名空间中的资源(Role 必须在每个命名空间中定义好) | Role | RoleBinding |
4.1 Node 授权模式
Node 授权模式用途比较单一,主要是对 kubelet 发出的 API 请求进行限制。需要在 API Server 启动时指明参数 --authorization-mode=Node。
Node 授权模式允许 kubelet 操作 API,主要包括:
- 可读操作:涉及 services、endpoints、nodes、pods、secrets、configmaps、绑定到 kubelet 所在节点的 pv 和 pvc。
- 可写操作:node 和 node status、pods 和 pod status、events。
- 授权操作:可读/可写 API 证书。
为了能够通过授权,kubelet 必须使用一个凭证标识它们在 system:nodes 组中,并使用 system:node:
4.2 ABAC 授权模式
如果使用 ABAC 授权模式,需要在 API Server 启动时指明参数 --authorization-policy-file=SOME_FILENAME(授权策略文件路径)和 --authorization-mode=ABAC(指明采用 ABAC 模式)。
授权策略文件中的每一行都是 Map 类型的 JSON 对象。每一行都被称为“访问策略对象”。
每个授权策略都有 3 个属性:
- apiVersion:为 abac.authorization.kubernetes.io/v1beta1。
- kind:为 Policy。
- spec:详细的策略设置。
对于 spec 有 4 个字段,如下:
- 主体属性:
- user:用户名。来源于 Token 文件或是认证用户的用户名。
- group:用户组。可以设置为 system:authenticated(匹配所有已认证的请求) 或是 system:unauthenticated(匹配所有未认证的请求)。* 资源属性:
- apiGroup:API 组。比如 extensions 或是 *(匹配所有 API 组)。
- namespace:命名空间。比如 kube-system 或是 *(匹配所有命名空间)。
- resource:API 资源对象。比如 pods 或是 *(匹配所有资源对象)。* 非资源属性:
- nonResourcePath:非资源对象类 URL 路径。比如 /version 或是 /apis,_ 表示匹配所有非资源对象类的请求路径,/foo/_ 表示匹配 /foo 路径下的所有子路径。* readonly:只读标识。当值为 true 时,对于所有的资源属性只允许 get、list、和 watch 操作,对于所有的非资源属性只允许 get 操作。
授权算法
一个请求中的属性需要与授权策略中定义的属性相对应。对于未知的属性会设置为空值(比如:empty、0、false 等)。将请求中的属性与授权策略中的属性进行逐一匹配,只要有一条匹配成功,那么该请求就通过了授权。
一些策略设置技巧:
- 如果要允许所有经过身份认证的用户执行某些操作,可以设置 group 属性为 system: authenticated。
- 如果要允许所有未经过身份认证的用户执行某些操作,可以设置 group 属性为 system: unauthenticated。
- 如果要允许一个用户做任何事情,可以将 apiGroup、namespace、resource、nonResourcePath 都设置为 *。
使用 kubectl
kubectl 使用 API Server 的 /api 和 /apis 端点发现服务资源类型。并通过 /openapi/v2 查询 create/update 命令发送给服务器的对象。
当使用 ABAC 授权策略时,需要在 nonResourcePath 属性中设置的特殊资源包括:
- 用于 API 版本协商的 /api、/api/*、/apis、/apis/*。
- 获取版本信息的 /version。
- 创建或是更新操作的 /swaggerapi/*。
使用 ServiceAccount
每个 ServiceAccount 会自动生成一个 ABAC 用户名,命名规则为 system:serviceaccount:
比如设置在 kube-system 命名空间中的 default ServiceAccount 具有所有权限,那么定义的规则为:
{
"apiVersion": "abac.authorization.kubernetes.io/v1beta1",
"kind": "Policy",
"spec": {
"user": "system:serviceaccount:kube-system:default",
"namespace": "*",
"resource": "*",
"apiGroup": "*"
}
}
当定义了新的策略后,需要重启 API Service 才能让设置生效。
例子
下面展示一些策略定义例子,供大家参考。
- 允许用户 alice 对所有资源做任何操作:
{
"apiVersion": "abac.authorization.kubernetes.io/v1beta1",
"kind": "Policy",
"spec": {
"user": "alice",
"namespace": "*",
"resource": "*",
"apiGroup": "*"
}
}
- kubelet 可以读取任意 Pod:
{
"apiVersion": "abac.authorization.kubernetes.io/v1beta1",
"kind": "Policy",
"spec": {
"user": "kubelet",
"namespace": "*",
"resource": "pods",
"readonly": true
}
}
- kubelet 可以读写 Event 对象:
{
"apiVersion": "abac.authorization.kubernetes.io/v1beta1",
"kind": "Policy",
"spec": { "user": "kubelet", "namespace": "*", "resource": "events" }
}
- 用户 Bob 只能读取 projectCaribou 命名空间下的 Pod:
{
"apiVersion": "abac.authorization.kubernetes.io/v1beta1",
"kind": "Policy",
"spec": {
"user": "bob",
"namespace": "projectCaribou",
"resource": "pods",
"readonly": true
}
}
- 任何用户都可以对非资源类路径进行只读请求:
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"group": "system:authenticated", "readonly": true, "nonResourcePath": "*"}}
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"group": "system:unauthenticated", "readonly": true, "nonResourcePath": "*"}}
4.3 Webhook 授权模式
Webhook 是调用外部的 REST 服务对账户进行授权。通过参数 --authorization-webhook-config-file=SOME_FILENAME(HTTP 配置文件)设置远端授权服务的信息,并且通过参数 --runtime-config=authorization.k8s.io/v1beta1=true 启用拓展。
Webhook 定义了一个 HTTP 回调接口,实现 Webhook 的应用会在指定事件发生时,向一个 URL 地址发送(POST)通知消息。
配置文件格式
配置文件使用 kubeconfig 文件格式,其中 clusters 指的是远端服务,users 指的是 API Server webhook。
下面的例子是一个使用 HTTPS 客户端认证的配置:
# 版本号
apiVersion: v1
# 资源对象类型
kind: Config
# clusters 指的是远端服务
clusters:
- name: name-of-remote-authz-service
cluster:
# 验证远端服务的 CA
certificate-authority: /path/to/ca.pem
# 远端服务的 URL,必须使用 HTTPS
server: https://authz.example.com/authorize
# users 指的是 API Server 的 Webhook 配置
users:
- name: name-of-api-server
user:
client-certificate: /path/to/cert.pem # Webhook 插件使用的证书
client-key: /path/to/key.pem # 证书的 key
# kubeconfig 文件需要设置 context
current-context: webhook
contexts:
- context:
cluster: name-of-remote-authz-service
user: name-of-api-server
name: webhook
当开始授权时,API Server 会生成一个 api.authorization.v1beta1.SubjectAccessReview 资源对象描述操作信息(包含尝试访问资源的请求动作的描述、以及被访问资源的属性),当进行 JSON 序列化之后 POST 出来。
下面的例子表示希望获取 Pod 列表:
{
"apiVersion": "authorization.k8s.io/v1beta1",
"kind": "SubjectAccessReview",
"spec": {
"resourceAttributes": {
"namespace": "kittensandponies",
"verb": "get",
"group": "unicorn.example.org",
"resource": "pods"
},
"user": "jane",
"group": ["group1", "group2"]
}
}
远端服务需要填充 status 字段,并返回是否允许访问的结果。
比如允许访问就应该返回:
{
"apiVersion": "authorization.k8s.io/v1beta1",
"kind": "SubjectAccessReview",
"status": {
"allowed": true
}
}
不允许访问就返回:
{
"apiVersion": "authorization.k8s.io/v1beta1",
"kind": "SubjectAccessReview",
"status": {
"allowed": false,
"reason": "user does not have read access to the namespace"
}
}
查询非资源路径,比如 /debug:
{
"apiVersion": "authorization.k8s.io/v1beta1",
"kind": "SubjectAccessReview",
"spec": {
"nonResourceAttributes": {
"path": "/debug",
"verb": "get"
},
"user": "jane",
"group": ["group1", "group2"]
}
}
非资源路径包括:/api、/apis、/metrics、/resetMetrics、/logs、/debug、/healthz、/swagger-ui/、/swaggerapi/、/ui 和 /version。这其中,通常对于 /api、/api/*、/apis、/apis/* 和 /version 默认允许授权,而其他的非资源路径默认禁止。
5.1 Admission Control 简介
当账户经过 API Server 的认证、授权之后,还需要经过准入机制才能够获得 API Server 真正的响应。准入机制由一系列的控制器列表组成,它们被编译成 kube-apiserver 二进制文件,并且只能由集群管理员进行配置。
在这些控制器列表中,有两个特殊的控制器:MutatingAdmissionWebhook 和 ValidatingAdmissionWebhook。它们分别对 Admission Control Webhooks 执行 Mutating(变更) 和 Validating(验证)。
对于很多的 kubernetes 高级功能都是需要 Admission Control 开启的。Admission Control 通过在 API Server 中的 --enable-admission-plugins 参数进行设置,对于多个插件列表,它们之间使用逗号进行分隔,比如:kube-apiserver --enable-admission-plugins=NamespaceLifecycle,LimitRanger ...。如果想要关闭插件,则使用参数 --disable-admission-plugins。
Admission Control 插件列表
如果想要查看开启了哪些 Admission Control 插件,可以执行如下命令查看:
kube-apiserver -h | grep enable-admission-plugins
下面详细介绍一下这些插件的作用:
- AlwaysPullImages:在启动容器之前总是尝试重新下载镜像。这对于多租户场景特别有用,容器启动之前可以使用租户的密钥下载镜像,保证镜像的安全性。
- DefaultStorageClass:默认的 StorageClass。当 PVC 资源对象创建时,如果没有指定 StorageClass,就会使用之前设置的默认 StorageClass。
- DefaultTolerationSeconds:对于没有设置容忍 node.kubernetes.io/not-ready:NoExecute 或是 node.alpha.kubernetes.io/unreachable:NoExecute 的 Pod,设置默认容忍时间为 5 min。
- EventRateLimit:用于应对事件密集情况下对 API Server 造成的洪水攻击。
- ExtendedResourceToleration:自动为申请特殊资源的 Pod 加入 Toleration 定义。
- ImagePolicyWebhook:允许后端的 Webhook 程序完成准入功能。通过启动参数 --admission-control-config-file 进行配置。
- LimitPodHardAntiAffinityTopology:启用 Pod 的反亲和性调度策略设置。在设置亲和性策略参数 requiredDuringSchedulingRequiredDuringExecution 时需要将 topologyKey 的值设置为 kubernetes.io/hostname,否则 Pod 会被拒绝创建。
- LimitRanger:监控进入的请求,保证请求符合在命名空间中定义的 LimitRanger 对象的资源限制。还可以为 Pod 设置默认的资源请求值,启用该插件后,会为 default 命名空间中的所有 Pod 都设置 0.1CPU 的资源请求。
- MutatingAdmissionWebhook:变更符合要求的请求内容,Webhook 以串行的方式顺序执行。
- NamespaceAutoProvision:检测所有进入的具备命名空间的资源请求,如果其中引用的命名空间不存在,就自动创建命名空间。
- NamespaceExists:检测所有进入的具备命名空间的资源请求,如果其中引用的命名空间不存在,就拒绝这一创建过程。
- NamespaceLifecycle:当命名空间删除时不会在其中创建新的资源对象,也会拒绝对于不存在的命名空间的访问请求,同时会阻止删除系统保留的 3 个命名空间:default、kube-system、kube-public。删除命名空间的时候还会删除该命名空间下的所有资源对象。
- NodeRestriction:限制 kubelet 对 Node 和 Pod 的修改行为。
- OwnerReferencesPermissionEnforcement:一个用户如果想要修改对象的 metadata.ownerReferences,必须具备 delete 权限。
- PodNodeSelector:读取命名空间的 annotation 字段和全局配置,对命名空间中对象的 node selectors 设置默认值或限制其取值。
- PersistentVolumeClaimResize:对 PVC 发起的 resize 请求进行额外校验。
- PodPreset:使用 PodSelector 选择 Pod,为符合条件的 Pod 进行注入。
- PodSecurityPolicy:在创建或修改 Pod 时决定是否根据 Pod 的 security context 和可用的 PodSecurityPolicy 对 Pod 的安全策略进行控制。
- PodTolerationRestriction:将 Pod 和其命名空间的 Toleration 进行冲突检测,如果存在冲突就拒绝 Pod 的创建。将命名空间和 Pod 的 Toleration 进行合并,将合并结果与命名空间中的白名单进行比较,如果合并结果不在白名单内就拒绝创建。如果不存在命名空间级的默认 Toleration 和白名单,就采用集群级别默认的 Toleration 和白名单。
- Priority:使用 priorityClassName 字段确定优先级,如果没有找到对应的 Priority Class,该 Pod 就会被拒绝。
- ResourceQuota:用于命名空间下的资源配额管理。
- SecurityContextDeny:使在 Pod 中定义的 SecurityContext 选项全部失效。SecurityContext 在容器中定义了操作系统级别的安全设定。
- ServiceAccount:让 ServiceAccount 实现了自动化。
- StorageObjectInUseProtection:在新创建的 PV 或 PVC 中加入 kubernetes.io/pv-protection 或 kubernetes.io/pvc-protection 的 finalizer。如果想删除 PV 或 PVC,需要等到所有 finalizer 的工作都完成,才会执行删除操作。
- ValidatingAdmissionWebhook:针对符合其选择要求的请求调用校验 Webhook。目标 Webhook 会以并行方式运行,如果其中任何一个 Webhook 拒绝了这个请求,那么请求失败。
官方推荐开启的准入控制插件如下:
--enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,ResourceQuota,DefaultTolerationSeconds
注意,上述插件的顺序也是非常重要的。
评论区