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自带工具),按照如下步骤 ...
随机推荐
- PHP echo 和 print 语句
在 PHP 中,有两种基本的输出方法:echo 和 print. PHP echo 和 print 语句 echo 和 print 之间的差异: echo - 能够输出一个以上的字符串 print - ...
- 【技术实战】Vue技术实战【二】
需求实战一 效果展示 代码展示 <template> <div> <a-table :dataSource="dataSource" :columns ...
- NOIP 2023 模拟赛 20230712 C 论剑
首先是伟大的题面 然后是数据范围 先解决1-4号数据点 1.枚举每个gcd的值p,统计一次答案,得到最小值(期望得分20) \[ans=\min_{p=2}^{\max a}\sum^n_{i=1}\ ...
- 【微信小程序的开发】初步认识
目录 项目结构 页面组成 json配置文件 app.json project.config.json sitemap.json 每个页面的json 实例 wxml 标签名称 ...
- Django跨域问题解决方案: django-cors-headers安装与配置
django-cors-headers安装与配置 官方文档:https://pypi.org/project/django-cors-headers/ 安装 pip install django-co ...
- Jmeter使用jp@gc-stepping thread group做性能测试
扩展插件 下载地址:http://jmeter-plugins.org/downloads/all 将JMeterPlugins-Standard.jar复制到jmeter的lib目录下的ext目录下 ...
- 拖拽宫格vue-grid-layout详细应用及案例
目录 1.前言 2.安装 3.属性 4.事件 5.占位符样式修改 6.案例 1.前言 vue-grid-layout是一个适用于vue的拖拽栅格布局库,功能齐全,适用于拖拽+高度/宽度自由调节的布局需 ...
- 形象谈JVM-第二章-认识编译器
我在上一章<形象谈JVM-第一章-认识JVM>提到的"翻译",其实就是我们今天所说的"编译"的概念. 上一章原文链接:https://www.cnb ...
- mall :rabbit项目源码解析
目录 一.mall开源项目 1.1 来源 1.2 项目转移 1.3 项目克隆 二.RabbitMQ 消息中间件 2.1 rabbit简介 2.2 分布式后端项目的使用流程 2.3 分布式后端项目的使用 ...
- 拼多多根据ID取商品详情 API 返回值说明
item_get-根据ID取商品详情 注册开通 pinduoduo.item_get 公共参数 名称 类型 必须 描述 key String 是 调用key(必须以GET方式拼接在URL中) se ...