Overview
在 Kubernetes 中,当一个访问请求通过了登录阶段(Authentication),必须还需要请求拥有该对象的访问权限,而授权部分也是Kubernetes API 访问控制中的第二个部分 Authorization .
Authorization 在 Kubernetes中是以评估发起请求的用户,根据其身份特性评估这次请求是被 ”拒绝“ 还是 “允许”,同访问控制三部曲中其他两个插件 (Authentication, Adminssion Control) 一样,Authorization 也可以同时配置多个,当收到用户的请求时,会依次检查这个阶段配置的所有模块,如果任何一个模块对该请求授予权限(拒绝或允许),那么该阶段会直接返回,当所有模块都没有该用户所属的权限时,默认是拒绝,在Kubernetes中,被该插件拒绝的用户显示为HTTP 403。
如有错别字或理解错误地方请多多担待,代码是以1.24进行整理,实验是以1.19环境进行,差别不大
objective:
- 了解kubernetes Authorization机制
- 了解授权系统的设计
- 完成实验,使用 OPA 作为 Kubernetes 外部用户,权限认证模型 RBAC 的替代品
Kubernetes是如何对用户授权的
kubernetes对用户授权需要遵守的shema必须拥有下列属性,代码位于pkg\apis\authorization\types.go
|
|
这里可以看到数据模型是
|
|
由此可得知,在授权部分,kubernetes要求请求必须存在
- 用户类属性:user,group ,extra 由 Authentication 提供的用户信息
- 请求类属性:
- API资源:
curl $API_SERVER_URL/api/v1/namespaces
- 请求路径: 非API资源格式的路径,
/api
,/healthz
- verb:HTTP请求方法,GET,POST..
- API资源:
- 资源类属性:
- 访问的资源的名称或ID,如Pod名
- 要访问的名称空间
- 资源所属组,Kubernetes资源有GVR组成
那么,SubjectAccessReview.Spec
为要审查的对象,SubjectAccessReview.Status
为审查结果,通常在每个请求到来时,入库前必定被审查
Kubernetes中的授权模式
知道授权的对象,就需要知道如何对该对象进行授权,Kubernetes authorizer 提供了下列授权模式
pkg/kubeapiserver/authorizer/modes/modes.go
|
|
可以看出,大致遵循模式进行授权
- ModeABAC (Attribute-based access control):是一种将属性分组,而后属性组分配给用户的模型,通常情况下这种模型很少使用
- ModeRBAC (Role Based Access Control) :是kubernetes主流的授权模型,是将用户分组,将属性分配给用户组的一种模型
- ModeNode:对kubelet授权的方式
- ModeWebhook:用户注入给Kubernetes 授权插件进行回调的一种授权模式
Kubernetes 授权生命周期
在启动 kube-apiserver
是都会初始化被注入一个 Authorizer
而这个被上面模式进行实现,如 RBACAuthorizer
, WebhookAuthorizer
k8s.io/apiserver/pkg/authorization/authorizer/interfaces.go
|
|
在 Run 中会创建一个CreateServerChain,这里面可以看到对应注册进来的 Authorizer
k8s.io\kubernetes\cmd\kube-apiserver\app\server.go
|
|
可以看到在创建这个 Authorizer
时会调用一个 BuildAuthorizer
构建这个 Authorizer
k8s.io/kubernetes/cmd/kube-apiserver/app/server.go
|
|
在代码 BuildAuthorizer
中构建了这个 Authorizer
其中可以看到 s 为 kube-apiserver
对于授权阶段的参数,例如参数,使用哪些模式 --authorization-mode
,使用的webhook的配置 --authentication-token-webhook-config-file
等,通过传入的参数来决定这些
|
|
而对应这部分的数据结构如下所示 k8s.io/pkg/kubeapiserver/options/authorization.go
|
|
例如在客户端部分,如果需要授权,都会使用该操作,可以在代码 k8s.io/pkg/registry/authorization/subjectaccessreview/rest.go 中可以看到REST中会 authorizer.Authorize 去验证是否有权限操作
|
|
authorizer.Authorize 会被实现在每一个该阶段的模式下,在 withAuthentication 构建了一个授权的 http.Handler 函数
k8s.io/apiserver/pkg/endpoints/filters/authorization.go
|
|
接下来在 createAggregatorConfig 调用了 BuildHandlerChainWithStorageVersionPrecondition 而又调用了
cmd/kube-apiserver/app/aggregator.go
|
|
而 k8s.io/apiserver/pkg/server/config.go 返回这个函数 BuildHandlerChainWithStorageVersionPrecondition
|
|
只要知道哪里调用了 handlerChainBuilder 就知道了鉴权步骤在哪里了,可以看到 handlerChainBuilder 被传入了 apiServerHandler,而后被作为参数返回给 listedPathProvider: &GenericAPIServer{}
listedPathProvider在 k8s.io/apiserver/pkg/server/genericapiserver.go
|
|
ListedPaths() 又在代码 k8s.io/apiserver/pkg/server/routes/index.go 中被 构建成这个http服务
|
|
至此,可以知道,每次请求时,我们在配置 kube-apiserver 配置的授权插件 .authorizer.Authorize
,而这个参数会被带至 subjectAccessReview
向下传递,其中 User,Group,Extra,UID 为 authentication 部分提供
Authorization webhook
Authorization webhook 位于 k8s.io/apiserver/plugin/pkg/authorizer/webhook/webhook.go,是通过 kube-apiserver 注入进来的配置,就是上面讲到的如果提供了配置就会加入这种类型的 Authorization 插件来认证。当配置此类型的授权插件,Authorize 会被调用,通过向注入的 URL 发起 REST 请求进行授权,请求对象是 v1beta1.SubjectAccessReview
下面是请求的实例
|
|
webhook 返回的格式
|
|
对于webhook来讲,只要接受请求保持上面格式,而返回格式为下属格式,就可以很好的将Kubernetes 权限体系接入到三方系统中,例如 open policy agent。
同样 webhook 也提供了 Authorize
函数,如同上面一样会被注入到每个handler中被执行
|
|
执行 webhook.Authorize()
会执行 w.subjectAccessReview.Create()
在这里可以看到会发起一个POST请求将 v1beta1.SubjectAccessReview
传入给webhook
k8s.io/apiserver/plugin/pkg/authorizer/webhook/webhook.go.Create
|
|
实验:基于OPA的RBAC模型
通过上面阐述,大致了解到kubernetes认证框架中的用户的分类以及认证的策略由哪些,实验的目的也是为了阐述一个结果,就是使用OIDC/webhook 是比其他方式更好的保护,管理kubernetes集群。首先在安全上,假设网络环境是不安全的,那么任意node节点遗漏 bootstrap token文件,就意味着拥有了集群中最高权限;其次在管理上,越大的团队,人数越多,不可能每个用户都提供单独的证书或者token,要知道传统教程中讲到token在kubernetes集群中是永久有效的,除非你删除了这个secret/sa;而Kubernetes提供的插件就很好的解决了这些问题。
实验环境
- 一个kubernetes集群
- 了解OPA相关技术
实验大致分为以下几个步骤:
- 建立一个HTTP服务器用于返回给kubernetes Authorization服务
- 查询用户操作是否有权限
实验开始
编写webhook Authorization
这里做的就是接收 subjectAccessReview
,将授权结果赋予 subjectAccessReview.Status.Allowed
,true/false,然后返回 subjectAccessReview
即可
|
|
编写rego
这里简单配置了两个权限,admin 组拥有所有操作权限,不包含 watch
,而 conf 组只能 list,在访问控制三部曲中,已授权的会增加一个组,例如 system:authenticated
代表被 Authentication 授予通过的用户,所以 Groups 为一个数组格式,这里检查为两个数组的交集 > 1,则肯定代表这个用户拥有该组的权限。
实验中 rego 部分可以在 playground 中测试
|
|
下面编写 RBACChek 函数,由于go1.16提供了embed功能,就可以直接将 rego embed go中,最后result.Allowed()
如果 input 通过评估则为 true
,反之亦然
|
|
配置kube-apiserver
Authorization webhook 与其他 webhook 一样,启用的方法也是修改 kube-apiserver 参数,并指定 kubeconfig
类型的配置文件,其中对于 Kubernetes 集群来说 kubeconfig
是 kubernetes 客户端访问的信息,而 webhook 这里的 kubeconfig
配置文件要填写的则是 webhook的信息,其中 user,cluster,contexts 属性均为 webhook的配置信息 [1]。
|
|
修改 kube-apiserver 参数
|
|
验证结果
准备三个外部用户,admin,admin1,searchUser,admin,admin1 为 admin 组,拥有所有权限,searchUser 为 conf 组,仅能 list 操作
|
|
测试用户 searchUser ,可以看到只能list操作
|
|
测试用户 admin,可以看出可以进行写与查看的操作
|
|
总结
kubernetes 提供了 Authentication,Authorization,Adminsion Control,Audit 几种webhook,可以自行在Kubernetes之上实现一个4A的标准,Authorization部分提供了一个并行与,但脱离Kubernetes的授权系统,使得外部用户可以很灵活的被授权,而不是手动管理多个clusterrolebinding,rolebingding 之类的资源。
实验中使用了OPA,这里是将rego静态文件embed入go中,在正常情况下OPA给出的架构如下图所示,存在一个 OPA Service,来进行验证,而实验中是直接嵌入到go中,OPA本身也提供了 HTTP Service,可以直接编译运行为 HTTP服务 [2]。 TODO
OPA本身提供了 Gatekeeper ,可以作为Kubernetes 资源使用,官方示例是作为为一个kubernetes准入网关,也提供了ingress浏览的验证 [3]
Notes:实验中还需要注意的一点则是,如果RBAC与webhook同时验证时,需要合理的规划权限,例如集群组件的账户,coreDNS,flannel等,也会被拒绝(在OPA设置的
default allow = false
)。
Reference
[1] Webhook Mode
[2] HTTP APIs
[5] 初探 Open Policy Agent 實作 RBAC (Role-based access control) 權限控管