版本信息如下:
a、操作系统:centos 7.6
b、kubernetes版本:v1.15.0
4A包括账号Account、认证Authentication、鉴权Authorization、审计Audit,是一个IT系统的一个基础的非业务性的通用功能。后台鉴权一般分为接口权限和数据权限,接口权限在在controller层的拦截器中实现,数据权限在dao层中实现。
kube-apiserver是一个http web服务,显然接口鉴权的工作也是在controller层的拦截器中实现。
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响应。
鉴权拦截器是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
}
将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 boolPath stringVerb stringAPIPrefix stringAPIGroup stringAPIVersion stringNamespace stringResource stringSubresource stringName stringParts []string
}
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
}
kube-apiserver是一个普通web http服务,鉴权逻辑清晰易懂,先是从http request中提取主语、谓语和宾语,在鉴权阶段就拿着刚获得的主谓宾信息去逐一匹配所有ClusterRole和Role对象中包含的权限信息。