日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > C# >内容正文

C#

Kubernetes:kube-apiserver 之鉴权

發布時間:2023/11/9 C# 57 coder
生活随笔 收集整理的這篇文章主要介紹了 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

進入 DefaultBuildHandlerChainAuthorization 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 chainhandler 處理順序是由下往上的,即處理完 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.AuthorizerBuildGenericConfigBuildAuthorizer 函數內創建。

# 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.ModeAlwaysAllowmodes.ModeAlwaysDenymodes.ModeABACmodes.ModeWebhookmodes.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.AuthorizationModesauthzmodes.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)
}

前面 handlera.Authorize(ctx, attributes) 實際執行的是鑒權器組 unionAuthzHandlerAuthorize 方法。在 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-apiserverAdimission 準入流程。


總結

以上是生活随笔為你收集整理的Kubernetes:kube-apiserver 之鉴权的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。