Kubernetes:kube-apiserver 之鉴权
kubernetes:kube-apiserver 系列文章:
- Kubernetes:kube-apiserver 之 scheme(一)
- Kubernetes:kube-apiserver 之 scheme(二)
- Kubernetes:kube-apiserver 之启动流程(一)
- Kubernetes:kube-apiserver 之启动流程(二)
- Kubernetes:kube-apiserver 和 etcd 的交互
- Kubernetes:kube-apiserver 之认证
0. 前言
上一篇文章介绍了 kube-apiserver 的认证机制。这里继续往下走,介绍 kube-apiserver 的鉴权。kube-apiserver 处理认证和鉴权非常类似,建议阅读鉴权机制前先看看 kube-apiserver 的 认证。
1. 鉴权 Authorization
1.1 Authorization handler
进入 DefaultBuildHandlerChain 看 Authorization handler 创建过程。
# kubernetes/vendor/k8s.io/apiserver/pkg/server/config.go
func DefaultBuildHandlerChain(apiHandler http.Handler, c *Config) http.Handler {
handler := apiHandler
handler = genericapifilters.WithAuthorization(handler, c.Authorization.Authorizer, c.Serializer)
handler = filterlatency.TrackStarted(handler, c.TracerProvider, "authorization")
handler = genericapifilters.WithAuthentication(handler, c.Authentication.Authenticator, failedHandler, c.Authentication.APIAudiences, c.Authentication.RequestHeaderConfig)
handler = filterlatency.TrackStarted(handler, c.TracerProvider, "authentication")
}
这里 handler chain 的 handler 处理顺序是由下往上的,即处理完 authentication handler 在处理 authorization handler。
进入 genericapifilters.WithAuthorization 查看鉴权 handler 的创建过程。
# kubernetes/vendor/k8s.io/apiserver/pkg/endpoints/filters/authorization.go
func WithAuthorization(hhandler http.Handler, auth authorizer.Authorizer, s runtime.NegotiatedSerializer) http.Handler {
return withAuthorization(hhandler, auth, s, recordAuthorizationMetrics)
}
func withAuthorization(handler http.Handler, a authorizer.Authorizer, s runtime.NegotiatedSerializer, metrics recordAuthorizationMetricsFunc) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
ctx := req.Context()
authorizationStart := time.Now()
// 获取 request 携带的用户信息
attributes, err := GetAuthorizerAttributes(ctx)
if err != nil {
responsewriters.InternalError(w, req, err)
return
}
// 对用户信息进行鉴权
authorized, reason, err := a.Authorize(ctx, attributes)
...
})
}
鉴权过程包括两部分。
一部分通过 GetAuthorizerAttributes 获取 RESTful API 请求中携带的用户信息。
# kubernetes/vendor/k8s.io/apiserver/pkg/endpoints/filters/authorization.go
func GetAuthorizerAttributes(ctx context.Context) (authorizer.Attributes, error) {
attribs := authorizer.AttributesRecord{}
user, ok := request.UserFrom(ctx)
if ok {
attribs.User = user
}
requestInfo, found := request.RequestInfoFrom(ctx)
if !found {
return nil, errors.New("no RequestInfo found in the context")
}
// Start with common attributes that apply to resource and non-resource requests
attribs.ResourceRequest = requestInfo.IsResourceRequest
attribs.Path = requestInfo.Path
attribs.Verb = requestInfo.Verb
attribs.APIGroup = requestInfo.APIGroup
attribs.APIVersion = requestInfo.APIVersion
attribs.Resource = requestInfo.Resource
attribs.Subresource = requestInfo.Subresource
attribs.Namespace = requestInfo.Namespace
attribs.Name = requestInfo.Name
return &attribs, nil
}
获取到用户信息后通过 a.Authorize(ctx, attributes) 对用户及其请求进行鉴权。其中 a 是实现了 Authorizer 鉴权器接口的实例。
type Authorizer interface {
Authorize(ctx context.Context, a Attributes) (authorized Decision, reason string, err error)
}
查看 a.Authorize(ctx, attributes) 实际是看 Config.Authorization.Authorizer 中的实例。
func DefaultBuildHandlerChain(apiHandler http.Handler, c *Config) http.Handler {
handler = genericapifilters.WithAuthorization(handler, c.Authorization.Authorizer, c.Serializer)
}
1.2 Authorization authorizer
Config.Authorization.Authorizer 在 BuildGenericConfig 的 BuildAuthorizer 函数内创建。
# kubernetes/pkg/controlplane/apiserver/config.go
func BuildGenericConfig(
s controlplaneapiserver.CompletedOptions,
schemes []*runtime.Scheme,
getOpenAPIDefinitions func(ref openapicommon.ReferenceCallback) map[string]openapicommon.OpenAPIDefinition,
) (
genericConfig *genericapiserver.Config,
versionedInformers clientgoinformers.SharedInformerFactory,
storageFactory *serverstorage.DefaultStorageFactory,
lastErr error,
) {
genericConfig.Authorization.Authorizer, genericConfig.RuleResolver, err = BuildAuthorizer(s, genericConfig.EgressSelector, versionedInformers)
if err != nil {
lastErr = fmt.Errorf("invalid authorization config: %v", err)
return
}
}
进入 BuildAuthorizer 查看 Authorizer 是怎么创建的。
# kubernetes/pkg/controlplane/apiserver/config.go
func BuildAuthorizer(s controlplaneapiserver.CompletedOptions, EgressSelector *egressselector.EgressSelector, versionedInformers clientgoinformers.SharedInformerFactory) (authorizer.Authorizer, authorizer.RuleResolver, error) {
authorizationConfig := s.Authorization.ToAuthorizationConfig(versionedInformers)
if EgressSelector != nil {
egressDialer, err := EgressSelector.Lookup(egressselector.ControlPlane.AsNetworkContext())
if err != nil {
return nil, nil, err
}
authorizationConfig.CustomDial = egressDialer
}
return authorizationConfig.New()
}
创建 Authorizer 分为两块。首先创建 authorizationConfig,接着通过 authorizationConfig.New() 实例化 authorizer。
# kubernetes/pkg/kubeapiserver/authorizer/config.go
func (config Config) New() (authorizer.Authorizer, authorizer.RuleResolver, error) {
var (
authorizers []authorizer.Authorizer
ruleResolvers []authorizer.RuleResolver
)
for _, authorizationMode := range config.AuthorizationModes {
switch authorizationMode {
case modes.ModeNode:
...
case modes.ModeAlwaysAllow:
...
case modes.ModeAlwaysDeny:
...
case modes.ModeABAC:
...
case modes.ModeWebhook:
...
case modes.ModeRBAC:
...
default:
return nil, nil, fmt.Errorf("unknown authorization mode %s specified", authorizationMode)
}
}
return union.New(authorizers...), union.NewRuleResolvers(ruleResolvers...), nil
}
可以看到,authorizationConfig.New() 内根据 config.AuthorizationModes 确定需要创建的鉴权器类型。这里有 modes.ModeNode,modes.ModeAlwaysAllow,modes.ModeAlwaysDeny,modes.ModeABAC,modes.ModeWebhook 和 modes.ModeRBAC 六种鉴权器。
config.AuthorizationModes 是在创建选项 NewOptions 中定义的,实例化过程如下:
func (o *BuiltInAuthorizationOptions) ToAuthorizationConfig(versionedInformerFactory versionedinformers.SharedInformerFactory) authorizer.Config {
return authorizer.Config{
AuthorizationModes: o.Modes,
}
}
# kubernetes/pkg/controlplane/apiserver/options/options.go
func NewOptions() *Options {
s := Options{
Authorization: kubeoptions.NewBuiltInAuthorizationOptions()
}
return &s
}
# kubernetes/pkg/kubeapiserver/options/authorization.go
func NewBuiltInAuthorizationOptions() *BuiltInAuthorizationOptions {
return &BuiltInAuthorizationOptions{
Modes: []string{authzmodes.ModeAlwaysAllow},
WebhookVersion: "v1beta1",
WebhookCacheAuthorizedTTL: 5 * time.Minute,
WebhookCacheUnauthorizedTTL: 30 * time.Second,
WebhookRetryBackoff: genericoptions.DefaultAuthWebhookRetryBackoff(),
}
}
这里的 config.AuthorizationModes 为 authzmodes.ModeAlwaysAllow。那么,将创建 alwaysAllowAuthorizer 鉴权器。
# kubernetes/pkg/kubeapiserver/authorizer/config.go
func (config Config) New() (authorizer.Authorizer, authorizer.RuleResolver, error) {
var (
authorizers []authorizer.Authorizer
ruleResolvers []authorizer.RuleResolver
)
for _, authorizationMode := range config.AuthorizationModes {
switch authorizationMode {
case modes.ModeAlwaysAllow:
alwaysAllowAuthorizer := authorizerfactory.NewAlwaysAllowAuthorizer()
authorizers = append(authorizers, alwaysAllowAuthorizer)
ruleResolvers = append(ruleResolvers, alwaysAllowAuthorizer)
}
}
return union.New(authorizers...), union.NewRuleResolvers(ruleResolvers...), nil
}
# kubernetes/vendor/k8s.io/apiserver/pkg/authorization/authorizerfactory/builtin.go
func NewAlwaysAllowAuthorizer() *alwaysAllowAuthorizer {
return new(alwaysAllowAuthorizer)
}
type alwaysAllowAuthorizer struct{}
func (alwaysAllowAuthorizer) Authorize(ctx context.Context, a authorizer.Attributes) (authorized authorizer.Decision, reason string, err error) {
return authorizer.DecisionAllow, "", nil
}
func (alwaysAllowAuthorizer) RulesFor(user user.Info, namespace string) ([]authorizer.ResourceRuleInfo, []authorizer.NonResourceRuleInfo, bool, error) {
return []authorizer.ResourceRuleInfo{
&authorizer.DefaultResourceRuleInfo{
Verbs: []string{"*"},
APIGroups: []string{"*"},
Resources: []string{"*"},
},
}, []authorizer.NonResourceRuleInfo{
&authorizer.DefaultNonResourceRuleInfo{
Verbs: []string{"*"},
NonResourceURLs: []string{"*"},
},
}, false, nil
}
alwaysAllowAuthorizer 鉴权器实现了 Authorizer 接口,其总是返回 authorizer.DecisionAllow。
每种鉴权器通过 union.New 加到鉴权器组中。
func New(authorizationHandlers ...authorizer.Authorizer) authorizer.Authorizer {
return unionAuthzHandler(authorizationHandlers)
}
// Authorizes against a chain of authorizer.Authorizer objects and returns nil if successful and returns error if unsuccessful
func (authzHandler unionAuthzHandler) Authorize(ctx context.Context, a authorizer.Attributes) (authorizer.Decision, string, error) {
var (
errlist []error
reasonlist []string
)
for _, currAuthzHandler := range authzHandler {
decision, reason, err := currAuthzHandler.Authorize(ctx, a)
if err != nil {
errlist = append(errlist, err)
}
if len(reason) != 0 {
reasonlist = append(reasonlist, reason)
}
switch decision {
case authorizer.DecisionAllow, authorizer.DecisionDeny:
return decision, reason, err
case authorizer.DecisionNoOpinion:
// continue to the next authorizer
}
}
return authorizer.DecisionNoOpinion, strings.Join(reasonlist, "\n"), utilerrors.NewAggregate(errlist)
}
前面 handler 的 a.Authorize(ctx, attributes) 实际执行的是鉴权器组 unionAuthzHandler 的 Authorize 方法。在 unionAuthzHandler.Authorize 内遍历执行每种鉴权器的 Authorize 方法,如果有一种鉴权器鉴权通过,则返回鉴权成功。如果鉴权器返回 authorizer.DecisionNoOpinion 则执行下一个鉴权器。如果鉴权器鉴权失败则返回鉴权失败。
1.3 authorization rules
前面介绍 alwaysAllowAuthorizer 鉴权器的时候我们看到 alwaysAllowAuthorizer.RulesFor 方法,该方法内定义了用户可以访问的 RESTful API 资源和非 RESTful API 资源。如 RESTful API 资源定义了访问资源的动作 Verbs,资源组APIGroups 和资源 Resources。
上例的 alwaysAllowAuthorizer 并没有看出 RulesFor 的运用是因为 alwaysAllowAuthorizer 总是允许请求访问 RESTful API 资源和非 RESTful API 资源。
我们以 rbacAuthorizer 鉴权器为例,看 RulesFor 是怎么被用上的。
func (config Config) New() (authorizer.Authorizer, authorizer.RuleResolver, error) {
for _, authorizationMode := range config.AuthorizationModes {
// Keep cases in sync with constant list in k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes/modes.go.
switch authorizationMode {
case modes.ModeRBAC:
rbacAuthorizer := rbac.New(
&rbac.RoleGetter{Lister: config.VersionedInformerFactory.Rbac().V1().Roles().Lister()},
&rbac.RoleBindingLister{Lister: config.VersionedInformerFactory.Rbac().V1().RoleBindings().Lister()},
&rbac.ClusterRoleGetter{Lister: config.VersionedInformerFactory.Rbac().V1().ClusterRoles().Lister()},
&rbac.ClusterRoleBindingLister{Lister: config.VersionedInformerFactory.Rbac().V1().ClusterRoleBindings().Lister()},
)
authorizers = append(authorizers, rbacAuthorizer)
ruleResolvers = append(ruleResolvers, rbacAuthorizer)
}
}
}
func New(roles rbacregistryvalidation.RoleGetter, roleBindings rbacregistryvalidation.RoleBindingLister, clusterRoles rbacregistryvalidation.ClusterRoleGetter, clusterRoleBindings rbacregistryvalidation.ClusterRoleBindingLister) *RBACAuthorizer {
authorizer := &RBACAuthorizer{
authorizationRuleResolver: rbacregistryvalidation.NewDefaultRuleResolver(
roles, roleBindings, clusterRoles, clusterRoleBindings,
),
}
return authorizer
}
func (r *RBACAuthorizer) Authorize(ctx context.Context, requestAttributes authorizer.Attributes) (authorizer.Decision, string, error) {
ruleCheckingVisitor := &authorizingVisitor{requestAttributes: requestAttributes}
r.authorizationRuleResolver.VisitRulesFor(requestAttributes.GetUser(), requestAttributes.GetNamespace(), ruleCheckingVisitor.visit)
if ruleCheckingVisitor.allowed {
return authorizer.DecisionAllow, ruleCheckingVisitor.reason, nil
}
}
可以看到,rbacAuthorizer 鉴权器的 Authorize 方法内的 r.authorizationRuleResolver.VisitRulesFor 结合用户信息和鉴权器的 rules 判断鉴权是否通过。
2. 总结
通过本篇文章介绍了 kube-apiserver 中的 Authorization 鉴权流程,下一篇将继续介绍 kube-apiserver 的 Adimission 准入流程。
Kubernetes:kube-apiserver 之鉴权的更多相关文章
- 第13章:Kubernetes 鉴权框架与用户权限分配
1.Kubernetes的安全框架 访问K8S集群的资源需要过三关:认证.鉴权.准入控制 普通用户若要安全访问集群API Server,往往需要证书.Token或者用户名+密码:Pod访问,需要Ser ...
- Kubernetes环境鉴权与自动发现
概览文章中提到了k8s的鉴权模式,简单回顾下: RBAC: Role-based access control 是基于角色的访问控制 ABAC: Atrribute-based access cont ...
- Kubernetes K8S之鉴权RBAC详解
Kubernetes K8S之鉴权概述与RBAC详解 K8S认证与授权 认证「Authentication」 认证有如下几种方式: 1.HTTP Token认证:通过一个Token来识别合法用户. H ...
- 深入理解k8s中的访问控制(认证、鉴权、审计)流程
Kubernetes自身并没有用户管理能力,无法像操作Pod一样,通过API的方式创建/删除一个用户实例,也无法在etcd中找到用户对应的存储对象. 在Kubernetes的访问控制流程中,用户模型是 ...
- 部署kubernetes-dashboard并配置ServiceAccount和登录鉴权
"种草" kubernetes-dashboard 安装部署dashboard 创建用于登录面板的ServiceAccount 权限控制 "种草" kubern ...
- ApiAuthValue鉴权机制总结
一.背景介绍 1.自动化的配置工具autoconfig介绍 项目开发过程中,有些配置会随着运行环境的变化而各不相同.如jdbc驱动的配置,在开发环境可能链接到开发本地的数据库,测试环境则有一套测试专用 ...
- 搭建一个分布式MongoDB鉴权集群
今天休假在家,测试并搭建了一个replica set shard MongoDB鉴权集群.replica set shard 鉴权集群中文资料比较少,本文是个人笔记,同时也希望对后来者有所帮助.本文仅 ...
- 开放平台鉴权以及OAuth2.0介绍
OAuth 2.0 协议 OAuth是一个开发标准,允许用户授权第三方网站或应用访问他们存储在另外的服务提供者上的信息,而不需要将用户名和密码提供给第三方网站或分享他们数据的内容. OAuth 2.0 ...
- 取消mod_sofia的呼叫鉴权
FreeSWITCH中默认的SIP呼叫是要鉴权的,流程如下. 终端 FreeSWITCH A -----Invite------> FS A <----Trying------ FS A ...
- android 高德地图出现【定位失败key鉴权失败】
如题:android 高德地图出现[定位失败key鉴权失败] 原因:使用的是debug模式下的SHA1,发布的版本正确获取SHA1的方式见: 方法二使用 keytool(jdk自带工具),按照如下步骤 ...
随机推荐
- 1.简述Hibernate的工作原理。
(1).首先,Configuration读取Hibernate的配置文件和映射文件中的信息,即加载配置文件和映射文件,并通过Hibernate配置文件生成一个多线程的SessionFactory对象: ...
- #Powerbi 1分钟学会利用AI,为powerbi报表进行高端颜色设计
在BI报表的设计中,配色方案往往成为一大难题,一组切合主题.搭配合理的颜色设计往往能为我们的报表,加分不少. 今天,就介绍一个AI配色的网站,利用AI为pbi报表进行配色设计. 一:网站网址 http ...
- linux下卸载vnc
sudo apt-get purge realvnc-vnc-server推荐连接:https://askubuntu.com/questions/653321/how-to-uninstall-re ...
- pyqt5中的布局方法
addLayout():用于在布局中插入子布局 addWidget():用于在布局中插入控件
- python入门,一篇就够了
python规范 函数必须写注释:文档注释格式'''注释内容''' 参数中的等号两边不要用空格 相邻函数用两个空行隔开 小写 + 下划线 函数名 模块名 实例名 驼峰法 类名 tips # 一行代码太 ...
- [USACO22DEC] Cow College B 题解
洛谷 P8897 AcWing 4821 题目描述 有\(n\)头奶牛,每头奶牛愿意交的最大学费为\(c_i\),问如何设置学费,可以使赚到的钱最多. \(1\le n\le 10^5,1\le c_ ...
- 让nodejs开启服务更简单--koa篇
在nodejs原始的http模块中,开启一个服务编码相对麻烦,需要对请求方式及上传的数据进行各种判断,而koa给我们提供了比较便捷的编码方式,同时它还有很多中间件可以直接拿来使用. 首先来看,如何 ...
- C#程序的启动显示方案(无窗口进程发送消息) - 开源研究系列文章
今天继续研究C#的WinForm的实例显示效果. 我们上次介绍了Winform窗体的唯一实例运行代码(见博文:基于C#的应用程序单例唯一运行的完美解决方案 - 开源研究系列文章 ).这就有一个问题,程 ...
- 在Python中使用LooseVersion进行软件版本号比对
技术背景 Python是一门极其热门.极其灵活的开发语言,其更新迭代的速度也非常的快速.有时候我们遇到不同的软件版本不同方法处理的情况,此时就需要用到版本号比对的工具.举一个例子说,我们要在pytho ...
- 如何调用api接口获取到商品数据
要调用API接口获取商品数据,需要进行以下步骤: 确定API接口 首先需要确定要使用的API接口,可以通过搜索引擎或者相关文档来查找适合的API接口.以淘宝开放平台为例,可以使用淘宝的商品信息查询AP ...