1 概述
1.1 环境
版本信息如下:
a、操作系统:centos 7.6
b、kubernetes版本:v1.15.0
1.2 鉴权Authorization
4A包括账号Account、认证Authentication、鉴权Authorization、审计Audit,是一个IT系统的一个基础的非业务性的通用功能。后台鉴权一般分为接口权限和数据权限,接口权限在在controller层的拦截器中实现,数据权限在dao层中实现。
kube-apiserver是一个http web服务,显然接口鉴权的工作也是在controller层的拦截器中实现。
1.3 kube-apiserver中的RBAC鉴权思路
kube-apiserver支持ABAC和RBAC,但RBAC往往使用得最广泛,因此本文只讨论RBAC。
对于RBAC而言,kubernetes的Role对象和ClusterRole对象,保存着一组权限,而权限就是谓语(action,或者叫verb)和宾语(object)(例如:get是谓语,pod是宾语)。kube-apiserver从http request对象的tls信息中获得主语(subject),从http mechod和http path推断出谓语和宾语,具备了主语、谓语和宾语后,遍历所有ClusterRole对象和Role对象中的权限信息,逐一对比,匹配正确则放行请求,不匹配则返回403响应。
2 源码
2.1 在拦截器链中注册鉴权相关的两个拦截器
鉴权拦截器是genericapifilters.WithAuthorization( ),而推断了主语、谓语和宾语的拦截器是genericapifilters.WithRequestInfo( )。在拦截器链中,因为genericapifilters.WithRequestInfo( )是较后配置的,因此是较先执行的。
# kubernetes/staging/src/k8s.io/apiserver/pkg/server/config.go
func DefaultBuildHandlerChain(apiHandler http.Handler, c *Config) http.Handler {
handler := genericapifilters.WithAuthorization(apiHandler, c.Authorization.Authorizer, c.Serializer)
/*
其他拦截器
*/
handler = genericapifilters.WithRequestInfo(handler, c.RequestInfoResolver)
/*
其他拦截器
*/
return handler
}
2.2 拦截器genericapifilters.WithRequestInfo( )
将http request的信息解析之后,得到k8s对象的gvr信息、namespace、verb等信息,并保存到名称为info且类型为RequestInfo结构体的对象中,这些信息会在authorize阶段中被使用到。
func WithRequestInfo(handler http.Handler, resolver request.RequestInfoResolver) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
ctx := req.Context()
// 解析http请求的path和query参数,形成info对象,info对象会在authorize阶段中被使用到
info, err := resolver.NewRequestInfo(req)
if err != nil {
responsewriters.InternalError(w, req, fmt.Errorf("failed to create RequestInfo: %v", err))
return
}
// 将info对象放入http request中的ctx对象中
req = req.WithContext(request.WithRequestInfo(ctx, info))
handler.ServeHTTP(w, req)
})
}
// RequestInfo holds information parsed from the http.Request
type RequestInfo struct {
IsResourceRequest bool
Path string
Verb string
APIPrefix string
APIGroup string
APIVersion string
Namespace string
Resource string
Subresource string
Name string
Parts []string
}
2.3 拦截器genericapifilters.WithAuthorization( )
func WithAuthorization(handler http.Handler, a authorizer.Authorizer, s runtime.NegotiatedSerializer) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
ctx := req.Context()
// 从request的ctx对象中提取数据
attributes, err := GetAuthorizerAttributes(ctx)
// 使用鉴权器进行鉴权,这行代码是重点
authorized, reason, err := a.Authorize(attributes)
// 鉴权结果是通过,则放行请求
if authorized == authorizer.DecisionAllow {
handler.ServeHTTP(w, req)
return
}
// 服务器内部发生错误,返回5xx响应
if err != nil {
responsewriters.InternalError(w, req, err)
return
}
// 鉴权结果是不通过,返回403响应
responsewriters.Forbidden(ctx, attributes, w, req, reason, s)
})
}
RBAC鉴权器的主要逻辑如下:
func (r *RBACAuthorizer) Authorize(requestAttributes authorizer.Attributes) (authorizer.Decision, string, error) {
ruleCheckingVisitor := &authorizingVisitor{requestAttributes: requestAttributes}
// VisitRulesFor( )从http request的ctx对象中拿出info对象,然后遍历ClusterRoleBinding和RoleBinding找出所有role
// 最终逐一进行比对role中表示的权限
r.authorizationRuleResolver.VisitRulesFor(requestAttributes.GetUser(), requestAttributes.GetNamespace(), ruleCheckingVisitor.visit)
// 鉴权结果是一个布尔值,放在ruleCheckingVisitor的属性allowed中
if ruleCheckingVisitor.allowed {
return authorizer.DecisionAllow, ruleCheckingVisitor.reason, nil
}
/*
其他代码
*/
return authorizer.DecisionNoOpinion, reason, nil
}
3 小结
kube-apiserver是一个普通web http服务,鉴权逻辑清晰易懂,先是从http request中提取主语、谓语和宾语,在鉴权阶段就拿着刚获得的主谓宾信息去逐一匹配所有ClusterRole和Role对象中包含的权限信息。