?前 言?
作為后臺支撐,Kubernetes優勢明顯,咪付的藍牙過閘系統和全態識別AI系統的后臺支撐采用了Kubernetes。Kubernetes平臺穩定運行一個重要因素是安全性的有效保障,其中API的訪問安全是一個重要方面,本文講解Kubernetes如何保證API訪問的安全性。
本篇文章包含以下內容:
介紹Kubernetes API安全防護措施
如何對用戶身份進行認證
如何對訪問資源進行鑒權
介紹Kubernetes準入控制的作用
Kubernetes平臺由6個組件組成:apiserver、scheduler、controller-manager、kubelet、kube-proxy、etcd,其中etcd是Kubernetes數據庫,在生產環境中一般不允許直接訪問。平臺通過apiserver組件對外提供HTTPS協議服務接口,其它scheduler、controller-manager、kubelet、kube-proxy組件不提供交互接口。運維中對平臺的管理操作都需要通過apiserver提供的功能接口完成,因此Kubernetes總體的API安全防護機制是對用戶訪問操作HTTPS服務接口的控制。
用戶可通過客戶端kubectl命令行工具或其它方式訪問Kubernetes的API資源,每個訪問API的請求,都要經過三個步驟校驗:Authentication、Authorization、Admission Control,總體如下圖所示:
Authentication(認證),對用戶身份進行驗證。認證方式現共有8種,可以啟用一種或多種認證方式,只要有一種認證方式通過,就不再進行其它方式的認證。通常啟用X509 Client Certs和Service Accout Tokens兩種認證方式。
Authorization(授權),驗證用戶是否擁有訪問權限。授權方式現共有6種,可以啟用一種或多種授權方式,啟用的任一種方式依據授權策略明確允許或拒絕請求,則立即返回,不再進行其它方式的授權。通常啟用RBAC和Node授權方式。
Admission Control(準入控制),對API資源對象修改、校驗。它有一個插件列表,所有請求需要經過這個列表中的每個準入控制插件修改、校驗,如果某一個準入控制插件驗證失敗,就拒絕請求。
用戶訪問 Kubernetes API時,apiserver認證授權模塊從HTTPS請求中獲取用戶的身份信息、請求資源路徑和業務對象參數。身份信息是用來確認用戶的身份是否合法,資源路徑是用于判定用戶是否擁有訪問操作的權限,業務對象參數則在準入控制中接受修改或校驗參數設置是否符合業務要求。
請求到達后,apiserver從請求的證書、Header中獲取用戶信息:Name(用戶名)、UID(用戶唯一標識)、Groups(用戶分組)、Extra(用戶額外信息)。認證通過后,通過Context將用戶信息向下傳播。
授權模塊從Context、請求的Method及URI提取用戶信息、請求資源屬性。使用請求資源屬性與授權策略匹配或向外部服務驗證判斷是否準予訪問。
準入控制接收到HTTPS請求提交的Body內容、用戶信息、資源信息,根據不同準入控制插件的功能對上述信息做修改或校驗。
? 認 證 ?
認證即身份驗證,認證關注的是誰發送的請求,也就是說用戶必須用某種方式證明自己的身份信息。Kubernetes集群有兩種類型的用戶:普通用戶(normal users)、服務賬戶(service accounts),普通用戶并不被Kubernetes管理和保存;而服務賬戶由Kubernetes存儲在etcd中,并可通過apiserver API管理。
最新的1.14版本中已支持8種認證方式,從身份驗證的方法上可分為4種類型:Token、證書、代理、密碼。Kubernetes可以同時啟用多個認證方式,在這種情況下,每個認證模塊都按順序嘗試驗證用戶身份,直到其中一個成功就認證通過。如果啟用匿名訪問,沒有被認證模塊拒絕的請求將被視為匿名用戶,并將該請求標記為system:anonymous 用戶名和system:unauthenticated組。從1.6版本開始,ABAC和RBAC授權需要對system:anonymous用戶或system:unauthenticated組進行明確授權。
啟用多個認證方式時,驗證順序如下:
?Authenticating Proxy?
Authenticating Proxy(認證代理),apiserver只驗證代理程序身份,不校驗用戶身份,驗證通過后,apiserver通過指定的Header提取訪問的用戶信息。
啟用此認證方式是通過設置apiserver參數--requestheader-username-headers和--requestheader-client-ca-file。
--requestheader-username-headers(必需,不區分大小寫)。設定Header中的用戶標識,第一個包含value的Header將被作為用戶名。
--requestheader-client-ca-file(必需)。PEM 編碼的CA證書。用來校驗代理端客戶端證書,防止 Header 欺騙。
--requestheader-allowed-names(可選)。證書名稱(CN)列表。如果設置了,則必須提供名稱列表中的有效客戶端證書。如果為空,則允許使用任何名稱客戶端證書。
--requestheader-group-headers( 1.6 以上版本支持,可選,不區分大小寫)。建議為 “X-Remote-Group”,設定Header中的用戶組標識。指定 Header 中的所有值都將作為用戶分組。
--requestheader-extra-headers-prefix (1.6 以上版本支持,可選,不區分大小寫)。建議為 “X-Remote-Extra-”,標題前綴可用于查找用戶的擴展信息,以任何指定的前綴開頭的 Header刪除前綴后其余部分將成為用戶擴展信息的鍵值,而 Header值則是擴展的值。
例如下面的配置:
--requestheader-username-headers=X-Remote-User--requestheader-group-headers=X-Remote-Group--requestheader-extra-headers-prefix=X-Remote-Extra-
代理請求:
GET/HTTP/1.1X-Remote-User: mifpayX-Remote-Group: adminX-Remote-Extra-Channel: vpnX-Remote-Extra-Scopes: openidX-Remote-Extra-Scopes: profile
提取后將得到以下用戶信息:
?Static Password File?
Static Password File(靜態密碼認證),啟用此認證方式是通過設置apiserver參數--basic-auth-file=SOMEFILE。密碼是無限期使用,更改密碼后需要重啟apiserver才能生效。
靜態密碼文件是CSV格式文件,至少包含3列:密碼,用戶名,用戶ID。從Kubernetes 1.6版本開始,第四列(可選)為用戶分組,如果有多個用戶分組,則該列必須加雙引號。例如:
password,name,uid,"group1,group2,group3"
當使用HTTPS客戶端連接時,apiserver從HTTPS Header中,如Authorization: Basic BASE64ENCODED,提取出用戶名和密碼。BASE64ENCODED解碼后格式為USER:PASSWORD。
從請求中獲取到用戶名和密碼,與靜態密碼文件中的用戶逐一進行比較,匹配成功則認證通過,否則認證失敗。認證通過時從CSV中提取匹配的用戶信息用于后續的授權等。
?X509 Client Certs?
X509 Client Certs(客戶端證書認證),啟用此認證方式是通過設置apiserver參數--client-ca-file=SOMEFILE。當用戶訪問API時,apiserver使用SOMEFILE(CA證書),通過TLS雙向認證驗證客戶端證書是否由它簽發。
證書主題屬性與用戶身份屬性的對應關系如下所示:
一個用戶屬于多個分組,則客戶端證書包含多個機構字段。
Static Token File
Static Token File(靜態Token認證),啟用此認證方式是通過設置apiserver參數--token-auth-file=SOMEFILE。Token長期有效,更改Token列表后需要重啟apiserver才能生效。
SOMEFILE是CSV格式文件,至少包含3列:Token、用戶名、用戶的UID,其次是可選的分組。每一行表示一個用戶。例如:
token,name,uid,"group1,group2,group3"
當使用HTTPS客戶端連接時,apiserver從HTTPS Header中提取身份Token。Token必須是一個字符序列,在HTTPS Header格式如下:
Authorization: Bearer 31ada4fd-adec-460c-809a-9e56ceb75269
從請求中提取身份Token,與靜態Token文件中的用戶逐一進行比較,匹配成功則認證通過,否則認證失敗。認證通過后將匹配的用戶信息用于后續的授權等。
?Service Account Tokens?
Service Account Tokens(服務賬戶)是一個自動啟用的認證方式,采用Token驗證請求。有兩個可選參數:
--service-account-key-file 給Token簽名的私鑰。如果未指定,將使用apiserver配置參數--tls-private-key-file指定的私鑰。
--service-account-lookup 如果啟用,被Kubernetes API刪除的Token將被廢除。
服務賬戶通常是Kubernetes API服務自動創建,并關聯到pod。賬戶通過podspec里的serviceAccountName字段與pod進行關聯。通常情況下,serviceAccountName不需要手動賦值,這是自動完成。
要手動創建服務賬戶,只需要使用 kubectl create serviceaccount (NAME) 命令。這將在當前的命名空間中創建一個服務賬戶和一個相關聯的 secret,其包含apiserver的公共CA證書和JSON Web令牌(JWT)。JWT用于服務賬戶的身份驗證。
JWT的內容如下:
Header:{"alg":"RS256","kid":""} Payload:{"iss":"kubernetes/serviceaccount","kubernetes.io/serviceaccount/namespace":"default","kubernetes.io/serviceaccount/secret.name":"test-token-z9tzx","kubernetes.io/serviceaccount/service-account.name":"test","kubernetes.io/serviceaccount/service-account.uid":"c12e8998-7896-11e9-80fb-000c296ce5e0","sub":"system:serviceaccount:default:test"} Signature:js1yoyrzmIn1zccyOc4KD1l887IoBIz-ulKvPnQD7wzn-KLBrQZ7_BzO1laRL71szIjA-iZH0salqTz20yZrXoyhsRzfpdda1o9IIoykblc15H_PUUBqdIPmB7u0XjDyBIraRHU5GO9OMaPwoeWNbt_uzKclckpe53lCtYwLnSfxM8G8kJEb8H3Mg7VnGJpxyuGII2ZG_ZEIO8Le-l_i3rZv-DcJYBpjrcG5DuXX7dcR0NfZM8Ex-VKOaUb9nuA0osXo9s02ATKBWn3zCUDb_O5KZ40fRISkCEmbioKJQLUWdJKvTUu7N-g9m0I9flufczwHO9hf6fC_X7x8-YhMew
服務賬戶驗證是使用secret中的CA證書,對JWT Signature進行校驗,驗證通過后將用戶名設置為 system:serviceaccount:(NAMESPACE):(SERVICEACCOUNT),被指定到組system:serviceaccounts和system:serviceaccounts:(NAMESPACE)。
?Bootstrap Tokens?
Bootstrap Tokens(Bootstrap令牌),此功能目前處于beta階段。
為了簡化新集群的引導,Kubernetes引用了一個動態管理令牌類型,稱為Bootstrap令牌。Bootstrap令牌是一種簡單的承載令牌,用于創建新集群或將新節點連接到現有集群。這些令牌作為Secrets存儲在kube-system命名空間中,可以動態管理和創建它們。
controller-manager包含一個TokenCleaner控制器,可在過期時自動刪除Bootstrap令牌。
使用--enable-bootstrap-token-auth參數在apiserver上啟用Bootstrap Token 認證。通過--controllers 在controller-manager上啟用TokenCleaner控制器。例如--controllers=*,tokencleaner。
令牌形式為[a-z0-9]{6}.[a-z0-9]{16}。第一部分是令牌ID,第二部分是令牌密鑰。可以在HTTPS頭信息中指定令牌,如下所示:
Authorization:Bearer781292.db7bc3a58fc5f07e
進行身份驗證時,驗證在kube-system命名空間中是否存在名稱為bootstrap-token-的secret,如果存在則驗證secret中的令牌ID、令牌密鑰是否與請求中的一致,令牌是否過期。認證通過后使用system:bootstrap:作為用戶名,并且是用戶分組system:bootstrappers的成員。
secrets內包含的令牌信息如下:
?OpenID Connect Tokens?
OpenID Connect Tokens是OAuth2中的一種,被許多供應商支持,尤其是Azure Active Directory、Salesforce和 Google。該協議對OAuth2的主要擴展是返回一個額外的訪問Token,叫ID Token。這個ID Token是JSON Web Token(JWT)。
在請求中,使用ID Token作為bearer token(而不是 access_token)。
要啟用此插件,需要設置下面兩個參數:
--oidc-issuer-url=https://xxx,用于讓apiserver獲取到OAuth2的公有密鑰。
?--oidc-client-id 客戶端的ID。
該方式通過OAuth2服務的公有密鑰對JWT中Signature校驗實現身份認證。
Webhook Token Authentication
該認證方式類似于Static Token File,但它提供擴展Token身份驗證的能力,具體的Token合法性驗證由外部系統完成,Kubernetes平臺將不再管理或配置具體的用戶信息。
要啟用此插件,需要設置下面兩個參數:
--authentication-token-webhook-config-file 一個kubeconfig文件描述如何訪問遠程webhook服務。
--authentication-token-webhook-cache-ttl 用于設置緩存時間,默認為兩分鐘。
當客戶端嘗試使用承載令牌向API服務器進行身份驗證時,身份驗證webhook將包含令牌的JSON序列化對象POST 發送到遠程服務。
POST正文將采用以下格式:
{"apiVersion":"authentication.k8s.io/v1beta1","kind":"TokenReview","spec": {"token":"(BEARERTOKEN)"? }}
認證成功將返回:
{"apiVersion":"authentication.k8s.io/v1beta1","kind":"TokenReview","status": {"authenticated":true,"user": {"username":"ms@mifpay.com","uid":"42","groups": ["quantai","ms"? ? ? ],"extra": {"extrafield1": ["extravalue1","extravalue2"? ? ? ? ]? ? ? }? ? }? }}
認證不成功將返回:
{"apiVersion":"authentication.k8s.io/v1beta1","kind":"TokenReview","status": {"authenticated":false? }}
授權
在Kubernetes中,授權作為一個獨立的步驟。根據所有授權策略匹配請求資源屬性,決定允許或拒絕請求。
授權包括六種方式:AlwaysDeny、AlwaysAllow、ABAC、RBAC、Webhook、Node。配置多個授權時,將按順序檢查每個授權,任何一個匹配的授權策略允許或拒絕請求時,則立即返回該決定,并且不會再檢查其他授權策略;所有授權策略都沒有允許或拒絕時,最終則拒絕該請求。
通過設置apiserver配置參數(--authorization-mode)啟用授權插件。啟用多個時,以逗號分隔。例如:
--authorization-mode=AlwaysAllow,AlwaysDeny,ABAC,Webhook,RBAC,Node
啟用多個授權時,授權驗證順序由配置參數中的順序決定。
授權只依據以下的屬性進行判斷:
其中API、Resource、Subresource、Namespace、APIGroup是從請求的URI中解析獲取得到。
?AlwaysDeny?
阻止所有的請求,僅用于測試。
?AlwaysAllow?
允許所有的請求。
?ABAC模式?
ABAC(基于屬性的訪問控制)定義了訪問控制范例,通過將屬性組合在一起的策略向用戶授予訪問權限。
要使用ABAC模式,還需要指定策略文件參數:--authorization-policy-file=SOMEFILE。
策略文件的格式為每行一個json對象。每一個對象為一個授權策略。授權策略json的屬性包括apiVersion、kind、spec,其中spec的屬性包括:user、group、readonly、apiGroup、namespace、resource、nonResourcePath。使用"*"匹配相應屬性的任何值。
例如用戶Alice可以對所有資源做任何操作:
{"apiVersion":"abac.authorization.kubernetes.io/v1beta1","kind":"Policy","spec": {"user":"Alice","namespace":"*","resource":"*","apiGroup":"*"}}
進行授權驗證時,將請求的屬性與策略文件中的每一個策略進行匹配。至少與一條策略能夠匹配的才能被授權。
?RBAC模式?
RBAC(Role Based Access Control):基于角色訪問控制授權。允許管理員通過Kubernetes API動態配置授權策略。RBAC就是用戶通過角色與權限進行關聯。簡單地說,一個用戶擁有若干角色,每一個角色擁有若干權限。這樣就構造成“用戶-角色-權限”的授權模型。在這種模型中,用戶與角色之間,角色與權限之間,一般是多對多的關系。具體關系如下圖:
RBAC包括四種類型:Role、ClusterRole、RoleBinding、ClusterRoleBinding。Role、ClusterRole包含一組權限規則,其中ClusterRole能應用于所有的命名空間(配置中沒有命名空間選項),常用于沒有命名空間的資源,如nodes;而Role只能包含單個命名空間的權限規則。RoleBinding、ClusterRoleBinding是將角色中定義的權限授予用戶、用戶組或服務賬戶。
Node模式
Node模式(節點授權)是一種特殊用途的授權模式,專門授權由kubelet發出的API請求。在進行認證時,先通過用戶名、用戶分組驗證是否是集群中的Node節點。只有是Node節點的請求才能使用Node模式授權。
節點授權允許kubelet執行API操作如下:
讀操作:services、endpoints、nodes、pods、secrets、configmaps、persistent volume claims、綁定到kubelet節點的pod相關的持久卷。
寫操作:節點、節點狀態、pod、pod狀態、事件。
認證相關的操作:對certificationsigningrequests API的讀/寫權限;創建tokenreviews和subjectaccessreviews的權限。
在未來版本中,節點授權將支持添加或刪除權限,以確保kubelet具有正確操作所需的最小權限集。
為了獲得Node授權者的授權,kubelet必須使用一個憑證來標識其用戶名為system:node:<nodeName>,并且該用戶屬于system:nodes分組。值<nodeName> 必須與kubelet注冊的節點名稱完全匹配。
?Webhook模式?
Webhook模式,到指定的外部服務驗證用戶是否擁有權限。啟用此模式,還需要設置apiserver參數--authorization-webhook-config-file=SOMEFILE,配置文件的示例如下:
# clusters refers to the remote service.clusters:? - name: name-of-remote-authz-service? ? cluster:certificate-authority: /path/to/ca.pem# CA for verifying the remote service.server: https://authz.example.com/authorize# URL of remote service to query. Must use 'https'. # users refers to the API Server's webhook configuration.users:? - name: name-of-api-server? ? user:client-certificate: /path/to/cert.pem# cert for the webhook plugin to useclient-key: /path/to/key.pem# key matching the cert # kubeconfig files require a context. Provide one for the API Server.current-context: webhookcontexts:- context:? ? cluster: name-of-remote-authz-service? ? user: name-of-api-sever??name:?webhook
Kubernetes API接收到請求后會向Webhook服務發送一個POST請求來驗證是否有權限訪問API。請求的內容是api.authorization.v1beta1.SubjectAccessReview對象的JSON序列化對象。
遠程服務將返回允許或禁止訪問的授權結果,如:
{"apiVersion":"authorization.k8s.io/v1beta1","kind":"SubjectAccessReview","status": {"allowed":true? }}
準入控制
準入控制(Admission Control)是Kubernetes apiserver用于攔截請求的一種方式,運行在認證、授權之后,是權限認證鏈上的最后一環,對請求API資源對象進行修改(Mutation)和校驗(Validating)。
準入控制功能分為兩大部分:
修改 (Mutation):修改API對象的主體內容。
驗證 (Validation):校驗API對象內容并決定是否接受請求。
在Kubernetes 1.10+之后,用戶在使用參數--enable-admission-plugins配置啟用準入控制時不需要關注它們的排列順序,apiserver在注冊準入控制插件時,已經定義好了所有插件的執行順序。
?核心接口 ?
在Kubernetes v1.14版本中準入控制有31個插件,每個插件實現不同的功能,如AlwaysDeny、PodNodeSelector這些具有單一功能職責的插件。它們都實現了兩個關鍵接口MutationInterface、ValidationInterface的三個函數Handles()、Admit()、Validate()。
Handles(),在執行Admit()和Validate()之前,校驗對資源的操作類型屬于CREATE、UPDATE、DELETE、CONNECT這些類型,否則不執行Admit()和Validate()。
Admit(),實現對請求參數及API對象的校驗、對API對象修改,如果返回error則拒絕請求。
Validate(),實現對請求參數及API對象的校驗,如果返回error則拒絕請求。
Admission主要插件列表
?Webhook Admission?
Kubernetes向用戶提供了Webhook Admission擴展機制,方便用戶接入自己的準入控制器,Service Mesh Istio是通過該方式向Pod中注入Sidecar容器。Webhook Admission的應用場景很多,如修改Pod對象、多租戶平臺限定Pod的namespace、自定義資源的驗證方法等。
接入Webhook Admission方式如下:
由Webhook Admission插件將數據AdminssionReview通過HTTPS協議請求Remote Service處理,在Remote Service服務內完成對數據內容修改或者驗證后,返回約定的v1beta1.AdmissionResponse對象,其Allowed屬性表示請求是否被允許。
目前Kubernetes對API的訪問安全已提供了完整的認證授權體系,在實際的生產環境中,可以根據各自的情況采用合適自己的認證授權方式,也可以通過相應的接口擴展Kubernetes認證授權能力。