k8s源碼分析7-API核心服務Authorization的認證及鑑權

一、Authentication認證

本節重點總結 :

  • Authentication的目的

  • Kubernetes 使用身份認證插件利用下面的策略來認證 API 請求的身份

    • 客戶端證書
    • 持有者令牌(Bearer Token)
    • 身份認證代理(Proxy)
    • HTTP 基本認證機制
  • union認證的規則

    • 如果某一個認證方法報錯就返回,說明認證沒過
    • 如果某一個認證方法報ok,說明認證過了,直接return了,無需再運行其他認證了
    • 如果所有的認證方法都沒報ok,則認證沒過

Authentication的目的

k8s_authentication.png

  • 驗證你是誰 確認“你是不是你",包括多種方式,如 Client Certificates, Password, and Plain Tokens, Bootstrap Tokens, and JWT Tokens等
  • 文檔 地址 https://kubernetes.io/zh/docs/reference/access-authn-authz/authentication/
  • 所有 Kubernetes 集羣都有兩類用戶:由 Kubernetes 管理的服務賬號和普通用戶
  • 所以認證要圍繞這兩類用戶展開

身份認證策略

  • Kubernetes 使用身份認證插件利用客戶端證書、持有者令牌(Bearer Token)、身份認證代理(Proxy) 或者 HTTP 基本認證機制來認證 API 請求的身份

  • HTTP 請求發給 API 服務器時, 插件會將以下屬性關聯到請求本身:

    • 用戶名:用來辯識最終用戶的字符串。常見的值可以是 kube-admin 或 [email protected]
    • 用戶 ID:用來辯識最終用戶的字符串,旨在比用戶名有更好的一致性和唯一性。
    • 用戶組:取值爲一組字符串,其中各個字符串用來標明用戶是某個命名的用戶邏輯集合的成員。 常見的值可能是 system:masters 或者 devops-team 等。
    • 附加字段:一組額外的鍵-值映射,鍵是字符串,值是一組字符串;用來保存一些鑑權組件可能 覺得有用的額外信息。
  • 你可以同時啓用多種身份認證方法,並且你通常會至少使用兩種方法:

    • 針對服務賬號使用服務賬號令牌
    • 至少另外一種方法對用戶的身份進行認證
  • 當集羣中啓用了多個身份認證模塊時,第一個成功地對請求完成身份認證的模塊會 直接做出評估決定。API 服務器並不保證身份認證模塊的運行順序

  • 對於所有通過身份認證的用戶,system:authenticated 組都會被添加到其組列表中。

  • 與其它身份認證協議(LDAP、SAML、Kerberos、X509 的替代模式等等)都可以通過 使用一個身份認證代理或 身份認證 Webhoook來實現。

代碼解讀

  • D:\go_path\src\github.com\kubernetes\kubernetes\cmd\kube-apiserver\app\server.go
  • 之前構建server之前生成通用配置buildGenericConfig裏
	// Authentication.ApplyTo requires already applied OpenAPIConfig and EgressSelector if present
	if lastErr = s.Authentication.ApplyTo(&genericConfig.Authentication, genericConfig.SecureServing, genericConfig.EgressSelector, genericConfig.OpenAPIConfig, clientgoExternalClient, versionedInformers); lastErr != nil {
		return
	}

真正的 Authentication初始化

  • D:\go_path\src\github.com\kubernetes\kubernetes\pkg\kubeapiserver\options\authentication.go
authInfo.Authenticator, openAPIConfig.SecurityDefinitions, err = authenticatorConfig.New()

New代碼 、創建認證實例,支持多種認證方式:請求 Header 認證、Auth 文件認證、CA 證書認證、Bearer token 認證、

  • D:\go_path\src\github.com\kubernetes\kubernetes\pkg\kubeapiserver\authenticator\config.go

核心變量1 tokenAuthenticators []authenticator.Token 代表Bearer token 認證

// Token checks a string value against a backing authentication store and
// returns a Response or an error if the token could not be checked.
type Token interface {
	AuthenticateToken(ctx context.Context, token string) (*Response, bool, error)
}

  • 不斷添加到數組中,最終創建union對象,最終調用unionAuthTokenHandler.AuthenticateToken
tokenAuth := tokenunion.New(tokenAuthenticators...)
func (authHandler *unionAuthTokenHandler) AuthenticateToken(ctx context.Context, token string) (*authenticator.Response, bool, error) {
	var errlist []error
	for _, currAuthRequestHandler := range authHandler.Handlers {
		info, ok, err := currAuthRequestHandler.AuthenticateToken(ctx, token)
		if err != nil {
			if authHandler.FailOnError {
				return info, ok, err
			}
			errlist = append(errlist, err)
			continue
		}

		if ok {
			return info, ok, err
		}
	}

	return nil, false, utilerrors.NewAggregate(errlist)
}

核心變量 2 authenticator.Request代表 用戶認證的接口 ,其中AuthenticateRequest是對應的認證方法

// Request attempts to extract authentication information from a request and
// returns a Response or an error if the request could not be checked.
type Request interface {
	AuthenticateRequest(req *http.Request) (*Response, bool, error)
}

  • 然後不斷添加到切片中,比如x509認證
	// X509 methods
	if config.ClientCAContentProvider != nil {
		certAuth := x509.NewDynamic(config.ClientCAContentProvider.VerifyOptions, x509.CommonNameUserConversion)
		authenticators = append(authenticators, certAuth)
	}
  • 把上面的unionAuthTokenHandler 也加入到鏈中
authenticators = append(authenticators, bearertoken.New(tokenAuth), websocket.NewProtocolAuthenticator(tokenAuth))
  • 最後創建一個union對象 unionAuthRequestHandler
authenticator := union.New(authenticators...)
  • 最終調用的unionAuthRequestHandler.AuthenticateRequest方法遍歷認證方法認證
// AuthenticateRequest authenticates the request using a chain of authenticator.Request objects.
func (authHandler *unionAuthRequestHandler) AuthenticateRequest(req *http.Request) (*authenticator.Response, bool, error) {
	var errlist []error
	for _, currAuthRequestHandler := range authHandler.Handlers {
		resp, ok, err := currAuthRequestHandler.AuthenticateRequest(req)
		if err != nil {
			if authHandler.FailOnError {
				return resp, ok, err
			}
			errlist = append(errlist, err)
			continue
		}

		if ok {
			return resp, ok, err
		}
	}

	return nil, false, utilerrors.NewAggregate(errlist)
}

  • 代碼解讀:
    • 如果某一個認證方法報錯就返回,說明認證沒過
    • 如果某一個認證方法報ok,說明認證過了,直接return了,無需再運行其他認證了
    • 如果所有的認證方法都沒報ok,則認證沒過

本節重點總結 :

  • Authentication的目的

  • Kubernetes 使用身份認證插件利用下面的策略來認證 API 請求的身份

    • 客戶端證書
    • 持有者令牌(Bearer Token)
    • 身份認證代理(Proxy)
    • HTTP 基本認證機制
  • union認證的規則

    • 如果某一個認證方法報錯就返回,說明認證沒過
    • 如果某一個認證方法報ok,說明認證過了,直接return了,無需再運行其他認證了
    • 如果所有的認證方法都沒報ok,則認證沒過

二、Authorization 鑑權

本節重點總結 :

  • Authorization 鑑權的目的
  • 4種鑑權模塊
  • 鑑權執行鏈unionAuthzHandler

Authorization 鑑權相關

image

  • Authorization鑑權,確認“你是不是有權利做這件事”。怎樣判定是否有權利,通過配置策略
  • Kubernetes 使用 API 服務器對 API 請求進行鑑權
  • 它根據所有策略評估所有請求屬性來決定允許或拒絕請求
  • 一個 API 請求的所有部分都必須被某些策略允許才能繼續。 這意味着默認情況下拒絕權限。
  • 當系統配置了多個鑑權模塊時,Kubernetes 將按順序使用每個模塊。 如果任何鑑權模塊批准或拒絕請求,則立即返回該決定,並且不會與其他鑑權模塊協商。 如果所有模塊對請求沒有意見,則拒絕該請求。 被拒絕響應返回 HTTP 狀態代碼 403。
  • 文檔地址 https://kubernetes.io/zh/docs/reference/access-authn-authz/authorization/

4種鑑權模塊

  • 文檔地址 https://kubernetes.io/zh/docs/reference/access-authn-authz/authorization/#authorization-modules
  • Node - 一個專用鑑權組件,根據調度到 kubelet 上運行的 Pod 爲 kubelet 授予權限。 瞭解有關使用節點鑑權模式的更多信息,請參閱節點鑑權。
  • ABAC - 基於屬性的訪問控制(ABAC)定義了一種訪問控制範型,通過使用將屬性組合 在一起的策略,將訪問權限授予用戶。策略可以使用任何類型的屬性(用戶屬性、資源屬性、 對象,環境屬性等)。要了解有關使用 ABAC 模式的更多信息,請參閱 ABAC 模式。
  • RBAC - 基於角色的訪問控制(RBAC)是一種基於企業內個人用戶的角色來管理對 計算機或網絡資源的訪問的方法。在此上下文中,權限是單個用戶執行特定任務的能力, 例如查看、創建或修改文件。要了解有關使用 RBAC 模式的更多信息,請參閱 RBAC 模式。
    • 被啓用之後,RBAC(基於角色的訪問控制)使用 rbac.authorization.k8s.io API 組來 驅動鑑權決策,從而允許管理員通過 Kubernetes API 動態配置權限策略。
    • 要啓用 RBAC,請使用 --authorization-mode = RBAC 啓動 API 服務器。
  • Webhook - WebHook 是一個 HTTP 回調:發生某些事情時調用的 HTTP POST; 通過 HTTP POST 進行簡單的事件通知。實現 WebHook 的 Web 應用程序會在發生某些事情時 將消息發佈到 URL。要了解有關使用 Webhook 模式的更多信息,請參閱 Webhook 模式。

代碼解析

  • 入口還在buildGenericConfig D:\go_path\src\github.com\kubernetes\kubernetes\cmd\kube-apiserver\app\server.go
genericConfig.Authorization.Authorizer, genericConfig.RuleResolver, err = BuildAuthorizer(s, genericConfig.EgressSelector, versionedInformers)

  • 還是通過New構造 ,位置 D:\go_path\src\github.com\kubernetes\kubernetes\pkg\kubeapiserver\authorizer\config.go
authorizationConfig.New()

構造函數New分析

核心變量1 authorizers

  • D:\go_path\src\github.com\kubernetes\kubernetes\staging\src\k8s.io\apiserver\pkg\authorization\authorizer\interfaces.go
// Authorizer makes an authorization decision based on information gained by making
// zero or more calls to methods of the Attributes interface.  It returns nil when an action is
// authorized, otherwise it returns an error.
type Authorizer interface {
	Authorize(ctx context.Context, a Attributes) (authorized Decision, reason string, err error)
}
  • 鑑權的接口,有對應的Authorize執行鑑權操作,返回參數如下

  • Decision代表鑑權結果,有

    • 拒絕 DecisionDeny
    • 通過 DecisionAllow
    • 未表態 DecisionNoOpinion
  • reason代表拒絕的原因

核心變量2 ruleResolvers

  • D:\go_path\src\github.com\kubernetes\kubernetes\staging\src\k8s.io\apiserver\pkg\authorization\authorizer\interfaces.go
// RuleResolver provides a mechanism for resolving the list of rules that apply to a given user within a namespace.
type RuleResolver interface {
	// RulesFor get the list of cluster wide rules, the list of rules in the specific namespace, incomplete status and errors.
	RulesFor(user user.Info, namespace string) ([]ResourceRuleInfo, []NonResourceRuleInfo, bool, error)
}
  • 獲取rule的接口,有對應的RulesFor執行獲取rule操作,返回參數如下
  • []ResourceRuleInfo代表資源型的rule
  • []NonResourceRuleInfo代表非資源型的如 nonResourceURLs: ["/metrics"]

遍歷鑑權模塊判斷,向上述切片中append

	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.ModeNode:
			node.RegisterMetrics()
			graph := node.NewGraph()
			node.AddGraphEventHandlers(
				graph,
				config.VersionedInformerFactory.Core().V1().Nodes(),
				config.VersionedInformerFactory.Core().V1().Pods(),
				config.VersionedInformerFactory.Core().V1().PersistentVolumes(),
				config.VersionedInformerFactory.Storage().V1().VolumeAttachments(),
			)
			nodeAuthorizer := node.NewAuthorizer(graph, nodeidentifier.NewDefaultNodeIdentifier(), bootstrappolicy.NodeRules())
			authorizers = append(authorizers, nodeAuthorizer)
			ruleResolvers = append(ruleResolvers, nodeAuthorizer)
		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)
    }

  • 最後返回兩個對象的union對象,跟authentication一樣
	return union.New(authorizers...), union.NewRuleResolvers(ruleResolvers...), nil

authorizers的union unionAuthzHandler

  • 位置 D:\go_path\src\github.com\kubernetes\kubernetes\staging\src\k8s.io\apiserver\pkg\authorization\union\union.go
// New returns an authorizer that authorizes against a chain of authorizer.Authorizer objects
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)
}

  • unionAuthzHandler的鑑權執行方法 Authorize同樣是遍歷執行內部的鑑權方法Authorize
  • 如果任一方法的鑑權結果decision爲通過或者拒絕,就直接返回
  • 否則代表不表態,繼續執行下一個Authorize方法

ruleResolvers的union unionAuthzHandler

  • 位置 D:\go_path\src\github.com\kubernetes\kubernetes\staging\src\k8s.io\apiserver\pkg\authorization\union\union.go
// unionAuthzRulesHandler authorizer against a chain of authorizer.RuleResolver
type unionAuthzRulesHandler []authorizer.RuleResolver

// NewRuleResolvers returns an authorizer that authorizes against a chain of authorizer.Authorizer objects
func NewRuleResolvers(authorizationHandlers ...authorizer.RuleResolver) authorizer.RuleResolver {
	return unionAuthzRulesHandler(authorizationHandlers)
}

// RulesFor against a chain of authorizer.RuleResolver objects and returns nil if successful and returns error if unsuccessful
func (authzHandler unionAuthzRulesHandler) RulesFor(user user.Info, namespace string) ([]authorizer.ResourceRuleInfo, []authorizer.NonResourceRuleInfo, bool, error) {
	var (
		errList              []error
		resourceRulesList    []authorizer.ResourceRuleInfo
		nonResourceRulesList []authorizer.NonResourceRuleInfo
	)
	incompleteStatus := false

	for _, currAuthzHandler := range authzHandler {
		resourceRules, nonResourceRules, incomplete, err := currAuthzHandler.RulesFor(user, namespace)

		if incomplete {
			incompleteStatus = true
		}
		if err != nil {
			errList = append(errList, err)
		}
		if len(resourceRules) > 0 {
			resourceRulesList = append(resourceRulesList, resourceRules...)
		}
		if len(nonResourceRules) > 0 {
			nonResourceRulesList = append(nonResourceRulesList, nonResourceRules...)
		}
	}

	return resourceRulesList, nonResourceRulesList, incompleteStatus, utilerrors.NewAggregate(errList)
}

  • unionAuthzRulesHandler的執行方法RulesFor中遍歷內部的authzHandler
  • 執行他們的RulesFor方法獲取resourceRules和nonResourceRules
  • 並將結果添加到resourceRulesList和nonResourceRulesList,返回

本節重點總結 :

  • Authorization 鑑權的目的
  • 4種鑑權模塊
  • 鑑權執行鏈unionAuthzHandler
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章