一、Authentication認證
本節重點總結 :
-
Authentication的目的
-
Kubernetes 使用身份認證插件利用下面的策略來認證 API 請求的身份
- 客戶端證書
- 持有者令牌(Bearer Token)
- 身份認證代理(Proxy)
- HTTP 基本認證機制
-
union認證的規則
- 如果某一個認證方法報錯就返回,說明認證沒過
- 如果某一個認證方法報ok,說明認證過了,直接return了,無需再運行其他認證了
- 如果所有的認證方法都沒報ok,則認證沒過
Authentication的目的
- 驗證你是誰 確認“你是不是你",包括多種方式,如 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 鑑權相關
- 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