在任何將資源或服務提供給有限使用者的系統上,認證和授權是兩個必不可少的功能,前者用於身份鑒別,負責驗證“來者是誰”,而后者則實現權限分派,負責鑒證“他有權做哪些事”。Kubernetes系統完全分離了身份驗證和授權功能,將二者分別以多種不同的插件實現,而且,特有的准入控制機制,還能在“寫”請求上輔助完成更為精細的操作驗證及變異功能。
Kubernetes集群中所有資源的訪問和變更都是通過Kubernetes API Server的REST API來實現的,所以集群安全的關鍵點就在於如何識別並認證客戶端身份(Authentication),以及隨后訪問權限的授權(Authorization)這兩個關鍵問題。
一、Kubernetes的訪問控制介紹
1、apiserver的認證
API Server作為Kubernetes集群系統的網關,是訪問及管理資源對象的唯一入口,它默認監聽TCP的6443端口,通過HTTPS協議暴露了一個RESTful風格的接口。所有需要訪問集群資源的集群組件或客戶端,包括kube-controller-manager、kube-scheduler、kubelet和kube-proxy等集群基礎組件,CoreDNS等集群的附加組件,以及此前使用的kubectl命令等都必須要經此網關請求與集群進行通信。所有客戶端均要經由API Server訪問或改變集群狀態以及完成數據存儲,並且API Server會對每一次的訪問請求進行合法性檢驗,包括用戶身份鑒別、操作權限驗證以及操作是否符合全局規范的約束等。所有檢查均正常完成且對象配置信息合法性檢驗無誤之后才能訪問或存入數據到后端存儲系統etcd中。
客戶端認證操作由API Server配置的一到多個認證插件完成。收到請求后,API Server依次調用配置的認證插件來校驗客戶端身份,直到其中一個插件可以識別出請求者的身份為止。授權操作則由一到多個授權插件完成,這些插件負責確定通過認證的用戶是否有權限執行發出的資源操作請求,該類操作包括創建、讀取、刪除或修改指定的對象等。隨后,通過授權檢測的用戶請求修改相關的操作還要經由一到多個准入控制插件的遍歷式檢測,例如使用默認值補足要創建的目標資源對象中未定義的各字段、檢查目標Namespace資源對象是否存在、檢查請求創建的Pod對象是否違反系統資源限制等,而其中任何的檢查失敗都可能會導致寫入操作失敗。
1).認證(Authenticating)是對客戶端的認證,通俗點就是用戶名密碼驗證。認證通過,則客戶端可以與Kubernetes集群進行交互。
2).授權(Authorization)是對資源的授權,Kubernetes中的資源無非是容器,最終其實就是容器的計算,網絡,存儲資源,當一個請求經過認證后,需要訪問某一個資源(比如創建一個pod),授權檢查都會通過訪問策略比較該請求上下文的屬性,(比如用戶,資源和Namespace),根據授權規則判定該資源(比如某namespace下的pod)是否是該客戶可訪問的。授權通過,則客戶端可以對集群中授權的資源做增、刪、改、查的操作。
3).准入(Admission Control)機制是一種在改變資源的持久化之前(比如某些資源的創建或刪除,修改等之前)的機制。不會攔截查詢操作。
2、Kubernetes的用戶賬戶及用戶組
Kubernetes系統上的用戶賬戶及用戶組的實現機制與常規應用略有不同。Kubernetes集群將那些通過命令行工具kubectl、客戶端庫或者直接使用RESTful接口向API Server發起請求的客戶端上的請求主體分為兩個不同的類別:現實中的“人”和Pod對象,它們的用戶身份分別對應用戶賬戶(User Account,也稱為普通用戶)和服務賬戶(Service Account,簡稱SA)。
1)用戶賬戶:其使用主體往往是“人”,一般由外部的用戶管理系統存儲和管理,Kubernetes本身並不維護這一類的任何用戶賬戶信息,它們不會存儲到API Server之上,僅僅用於檢驗用戶是否有權限執行其所請求的操作。
2)服務賬戶:其使用主體是“應用程序”,專用於為Pod資源中的服務進程提供訪問Kubernetes API時的身份標識(identity);ServiceAccount資源通常要綁定到特定的名稱空間,它們由API Server自動創建或通過API調用,由管理員手動創建,通常附帶着一組訪問API Server的認證憑據——Secret,可由同一名稱的Pod應用訪問API Server時使用。
3)用戶賬戶通常用於復雜的業務邏輯管控,作用於系統全局,因而名稱必須全局唯一。Kubernetes並不會存儲由認證插件從客戶端請求中提取的用戶及所屬的組信息,因而也就沒有辦法對普通用戶進行身份認證,它們僅僅用於檢驗該操作主體是否有權限執行其所請求的操作。
4)服務賬戶則隸屬於名稱空間級別,僅用於實現某些特定操作任務,因此功能上要輕量得多。這兩類賬戶都可以隸屬於一個或多個用戶組。
舉例說明:
sa賬號:訪問pod資源中提供的服務的賬號。如:登陸dashboard使用的賬號。
user account:這個是登陸Kubernetes集群物理機器的賬號。如:配置一個賬號讓其只能訪問集群中namespace=dev下的資源。
用戶組只是用戶賬戶的邏輯集合,它本身沒有執行系統操作的能力,但附加於組上的權限可由其內部的所有用戶繼承,以實現高效的授權管理機制。Kubernetes有以下幾個內置用於特殊目的的組。
▪system:unauthenticated:未能通過任何一個授權插件檢驗的賬戶的、所有未通過認證測試的用戶統一隸屬的用戶組。
▪system:authenticated:認證成功后的用戶自動加入的一個專用組,用於快捷引用所有正常通過認證的用戶賬戶。
▪system:serviceaccounts:所有名稱空間中的所有ServiceAccount對象。
▪system:serviceaccounts:<namespace>:特定名稱空間內所有的ServiceAccount對象。
3、認證、授權與准入控制基礎知識
Kubernetes使用身份驗證插件對API請求進行身份驗證,它允許管理員自定義服務賬戶和用戶賬戶要啟用或禁用的插件,並支持各自同時啟用多種認證機制。具體設定時,至少應該為服務賬戶和用戶賬戶各自啟用一個認證插件。 如果啟用了多種認證機制,賬號認證過程由認證插件以串行方式進行,直到其中一種認證機制成功完成即結束。若認證失敗,服務器則響應以401狀態碼,反之,請求者就會被Kubernetes識別為某個具體的用戶(以其用戶名進行標識),並且該連接上隨后的操作都會以此用戶身份進行。API Server對於接收到的每個訪問請求會調用認證插件,嘗試將以下屬性與訪問請求相關聯。
▪Username:用戶名,例如kubernetes-admin等。
▪UID:用戶的數字標簽符,用於確保用戶身份的唯一性。
▪Groups:用戶所屬的組,用於權限指派和繼承。
▪Extra:鍵值數據類型的字符串,用於提供認證需要用到的額外信息。
3.1 Kubernetes的認證方式
1)靜態密碼文件認證:將用戶名和密碼等信息以明文形式存儲在CSV格式的文件中,由kube-apiserver在啟動時通過--basic-auth-file選項予以加載,添加或刪除用戶都需要重啟API Server;客戶端通過在HTTP Basic認證(Authorization: Basic base64-encoded-username:password標頭)方式中將用戶名和密碼編碼后對該文件進行認證;不建議生產環境中使用。
說明:靜態密碼文件認證插件自Kubernetes v1.20版本中預以棄用,該插件的測試操作部分在v1.20及之后的版本上不可用,但在v1.19及之前的版本中,仍然可用。
2)靜態令牌文件認證:即保存用於認證的令牌信息的靜態文件,由kube-apiserver的命令行選項--token-auth-file加載,且API Sever進程啟動后不可更改;HTTP協議的客戶端能基於承載令牌(Authorization: Bearer <token>標頭)對靜態令牌文件進行身份驗證,它將令牌編碼后通過請求報文中的Authorization頭部承載並傳遞給API Server即可;不建議生產環境中使用。
3)X509客戶端證書認證:客戶端在請求報文中攜帶X.509格式的數字證書用於認證,其認證過程類似於HTTPS協議通信模型;認證通過后,證書中的主體標識(Subject)將被識別為用戶標識,其中的字段CN(Common Name)的值是用戶名,字段O(Organization)的值是用戶所屬的組。例如/CN=ilinux/O=opmasters/O=admin中,用戶名為ilinux,它屬於opmasters和admin兩個組;該認證方式可通過--client-ca-file=SOMEFILE選項啟用。
4)引導令牌(Bootstrap Token)認證:一種動態管理承載令牌進行身份認證的方式,常用於簡化組建新Kubernetes集群時將節點加入集群的認證過程,需要由kube-apiserver通過--experimental-bootstrap-token-auth選項啟用;新的工作節點首次加入時,Master使用引導令牌確認節點身份的合法性之后自動為其簽署數字證書以用於后續的安全通信,kubeadm初始化的集群也是這種認證方式;這些令牌作為Secrets存儲在kube-system命名空間中,可以動態管理和創建它們,並由TokenCleaner控制器負責刪除過期的引導令牌。 5)ServiceAccount令牌認證:該認證方式會由kube-apiserver程序自動啟用,它同樣使用簽名的承載令牌來驗證請求;該認證方式還支持通過可選項--service-account-key-file加載簽署承載令牌的密鑰文件,未指定時將使用API Server自己的TLS私鑰;ServiceAccount通常由API Server自動創建,並通過ServiceAccount准入控制器將其注入Pod對象,包括ServiceAccount上的承載令牌,容器中的應用程序請求API Server的服務時以此完成身份認證。 6)OpenID Connect令牌認證:簡稱為OIDC,是OAuth 2協議的一種擴展,由Azure AD、Salesforce和Google Accounts等OAuth 2服務商所支持,協議的主要擴展是返回的附加字段,其中的訪問令牌也稱為ID令牌;它屬於JSON Web令牌(JWT)類型,有服務器簽名過的常用字段,例如email等;kube-apiserver啟用這種認證功能的相關選項較多。
7)Webhook令牌認證:Webhook身份認證是用於驗證承載令牌的鈎子;HTTP協議的身份驗證允許將服務器的URL注冊為Webhook,並接收帶有承載令牌的POST請求進行身份認證;客戶端使用kubeconfig格式的配置文件,在文件中,users指的是API Server的Webhook,而clusters則指的是API Server。 8)代理認證:API Server支持從請求頭部的值中識別用戶,例如常用的X-Remote-User、X-Remote-Group和幾個以X-Remote-Extra-開頭的頭部,它旨在與身份驗證代理服務相結合,由該代理設置相應的請求頭部;為了防止頭欺騙,在檢查請求標頭之前,需要身份認證代理服務向API Server提供有效的客戶端證書,以驗證指定CA(由選項--requestheader-client-ca-file等進行指定)的代理服務是否合法。
9)那些未能被任何驗證插件明確拒絕的請求中的用戶即為匿名用戶,該類用戶會被冠以system:anonymous用戶名,隸屬於system:unauthenticated用戶組。若API Server啟用了除AlwaysAllow以外的認證機制,則匿名用戶處於啟用狀態。但是,出於安全因素的考慮,建議管理員通過--anonymous-auth=false選項將其禁用。
注意:
API Server還允許用戶通過模擬頭部冒充另一個用戶,這些請求可以以手動方式覆蓋請求中用於身份驗證的用戶信息。例如,管理員可以使用此功能臨時模擬其他用戶來查看請求是否被拒絕,以進行授權策略調試。
除了身份信息,請求報文還需要提供操作方法及其目標對象,例如針對某Pod資源對象進行的創建、查看、修改或刪除操作等:
▪API:用於定義請求的目標是否為一個API資源。 ▪Request path:請求的非資源型路徑,例如/api或/healthz。 ▪API group:要訪問的API組,僅對資源型請求有效;默認為core API group。 ▪Namespace:目標資源所屬的名稱空間,僅對隸屬於名稱空間類型的資源有效。 ▪API request verb:API請求類的操作,即資源型請求(對資源執行的操作),包括get、list、create、update、patch、watch、proxy、redirect、delete和deletecollection等。 ▪HTTP request verb:HTTP請求類的操作,即非資源型請求要執行的操作,如get、post、put和delete。 ▪Resource:請求的目標資源的ID或名稱。 ▪Subresource:請求的子資源。
3.2 Kubernetes的授權
▪Node:基於Pod資源的目標調度節點來實現對kubelet的訪問控制。 ▪ABAC:Attribute-based access control,基於屬性的訪問控制。 ▪RBAC:Role-based access control,基於角色的訪問控制。 ▪Webhook:基於HTTP回調機制實現外部REST服務檢查,確認用戶授權的訪問控制。 另外,還有AlwaysDeny和AlwaysAllow兩個特殊的授權插件,其中AlwaysDeny(總是拒絕)僅用於測試,而AlwaysAllow(總是允許)則用於不期望進行授權檢查時直接在授權檢查階段放行所有的操作請求。--authorization-mode選項用於定義API Server要啟用的授權機制,多個選項值彼此間以逗號進行分隔。
3.3 Kubernetes的准入控制
1)AlwaysAdmit和AlwaysDeny:前者允許所有請求,后者則拒絕所有請求。 2)AlwaysPullImages:總是下載鏡像,即每次創建Pod對象之前都要去下載鏡像,常用於多租戶環境中,以確保私有鏡像僅能夠由擁有權限的用戶使用。 3)NamespaceLifecycle:拒絕在不存在的名稱空間中創建資源,而刪除名稱空間則會級聯刪除其下的所有其他資源。 4)LimitRanger:可用資源范圍界定,用於對設置了LimitRange的對象所發出的所有請求進行監控,以確保其資源請求不會超限。 5)ServiceAccount:用於實現服務賬戶管控機制的自動化,實現創建Pod對象時自動為其附加相關的Service Account對象。 6)PersistentVolumeLabel:為那些由雲計算服務商提供的PV自動附加region或zone標簽,以確保這些存儲卷能正確關聯且僅能關聯到所屬的region或zone。 7)DefaultStorageClass:監控所有創建PVC對象的請求,以保證那些沒有附加任何專用StorageClass的請求會被自動設定一個默認值。 8)ResourceQuota:用於為名稱空間設置可用資源上限,並確保當其中創建的任何設置了資源限額的對象時,不會超出名稱空間的資源配額。 9)DefaultTolerationSeconds:如果Pod對象上不存在污點寬容期限,則為它們設置默認的寬容期,以寬容notready:NoExecute和unreachable:NoExecute類的污點5分鍾時間。 10)ValidatingAdmissionWebhook:並行調用匹配當前請求的所有驗證類的Webhook,任何一個校驗失敗,請求即失敗。 11)MutatingAdmissionWebhook:串行調用匹配當前請求的所有變異類的Webhook,每個調用都可能會更改對象。
二、ServiceAccount及認證
Kubernetes原生的應用程序意味着專為運行於Kubernetes系統之上而開發的應用程序,這些程序托管運行在Kubernetes之上,能夠直接與API Server進行交互,並進行資源狀態的查詢或更新,例如Flannel和CoreDNS等。API Server同樣需要對這類來自Pod資源中的客戶端程序進行身份驗證,服務賬戶也是專用於這類場景的賬號。ServiceAccount資源一般由用戶身份信息及保存了認證信息的Secret對象組成。
1、ServiceAccount自動化
[root@k8s-master01 k8s-yaml]# kubectl describe pod test-pod ...... Mounts: /var/run/secrets/kubernetes.io/serviceaccount from default-token-btlhc (ro) ...... Volumes: default-token-btlhc: Type: Secret (a volume populated by a Secret) SecretName: default-token-btlhc Optional: false ......
容器的該掛載點目錄中通常存在3個文件:ca.crt、namespace和token,其中,token文件保存了ServiceAccount的認證令牌,容器中的進程使用該賬戶認證到API Server,進而由認證插件完成用戶認證並將其用戶名傳遞給授權插件。
[root@k8s-master01 k8s-yaml]# kubectl exec -it test-pod -- bash [root@test-pod /]# ls /var/run/secrets/kubernetes.io/serviceaccount/ ca.crt namespace token
每個Pod對象只有一個服務賬戶,若創建Pod資源時未予明確指定,則ServiceAccount准入控制器會為其自動附加當前名稱空間中默認的服務賬戶,其名稱通常為default。下面的命令顯示了default這個服務賬戶的詳細信息。
[root@k8s-master01 k8s-yaml]# kubectl describe serviceaccounts default -n default Name: default Namespace: default Labels: <none> Annotations: <none> Image pull secrets: <none> Mountable secrets: default-token-btlhc Tokens: default-token-btlhc Events: <none>
Kubernetes系統通過3個獨立的組件間相互協作實現了上面描述的Pod對象服務賬戶的自動化過程:ServiceAccount准入控制器、令牌控制器和ServiceAccount控制器。ServiceAccount控制器負責為名稱空間管理相應的資源對象,它需要確保每個名稱空間中都存在一個名為default的服務賬戶對象。ServiceAccount准入控制器內置在API Server中,負責在創建或更新Pod時按需進行ServiceAccount資源對象相關信息的修改。
▪若Pod沒有顯式定義使用的ServiceAccount對象,則將其設置為default。
▪若Pod顯式引用了ServiceAccount,則負責檢查被引用的對象是否存在,不存在時將拒絕Pod資源的創建請求。
▪若Pod中不包含ImagePullSecerts,則把ServiceAccount的ImagePullSecrets附加其上。
▪為帶有訪問API的令牌的Pod對象添加一個存儲卷。
▪為Pod對象中的每個容器添加一個volumeMounts,將ServiceAccount的存儲卷掛至/var/run/secrets/kubernetes.io/serviceaccount。
令牌控制器是控制平面組件Controller Manager中的一個專用控制器,它工作於異步模式,負責完成如下任務:
▪監控ServiceAccount的創建操作,並為其添加用於訪問API的Secret對象;
▪監控ServiceAccount的刪除操作,並刪除其相關的所有ServiceAccount令牌密鑰;
▪監控Secret對象的添加操作,確保其引用的ServiceAccount存在,並在必要時為Secret對象添加認證令牌;
▪監控Secret對象的刪除操作,以確保刪除每個ServiceAccount對此Secret的引用。
2、ServiceAccount基礎應用
ServiceAccount是Kubernetes API上的一種資源類型,它屬於名稱空間級別,用於讓Pod對象內部的應用程序在與API Server通信時完成身份認證。同樣名為ServiceAccount的准入控制器實現了服務賬戶自動化,該准入控制器為每個名稱空間都自動生成了一個名為default的默認資源對象。 每個Pod對象可附加其所屬名稱空間中的一個ServiceAccount資源,且只能附加一個。不過,一個ServiceAccount資源可由其所屬名稱空間中的多個Pod對象共享使用。創建Pod資源時,用戶可使用spec.serviceAccountName屬性直接指定要使用的ServiceAccount對象,或者省略此字段而由准入控制器自動附加當前名稱空間中默認的ServiceAccount,以確保每個Pod對象至少基於該服務賬戶有權限讀取當前名稱空間中其他資源對象的元數據信息。 Kubernetes也支持用戶按需創建ServiceAccount資源並將其指定到特定應用的Pod對象之上,結合集群啟用的授權機制為該ServiceAccount資源賦予所需要的更多權限,從而構建出更加靈活的權限委派模型。
2.1 命令式ServiceAccount資源創建
kubectl create serviceaccount命令能夠快速創建自定義的ServiceAccount資源,我們僅需要在命令后給出目標ServiceAccount資源的名稱。
[root@k8s-master01 ~]# kubectl create serviceaccount my-sa serviceaccount/my-sa created [root@k8s-master01 ~]# kubectl get sa my-sa NAME SECRETS AGE my-sa 1 48s
Kubernetes會為創建的ServiceAccount資源自動生成並附加一個Secret對象,該對象以ServiceAccount資源名稱為前綴,后面加token-隨機數的方式。
[root@k8s-master01 ~]# kubectl get secrets NAME TYPE DATA AGE default-token-btlhc kubernetes.io/service-account-token 3 15d my-sa-token-5kw7c kubernetes.io/service-account-token 3 95s
該Secret對象屬於特殊的kubernetes.io/service-account-token類型,它包含ca.crt、namespace和token這3個數據項,它們分別包含Kubernetes Root CA證書、Secret對象所屬的名稱空間和訪問API Server的token令牌。由Pod對象以Secret存儲卷的方式將該類型的Secret對象掛載至/var/run/secrets/kubernetes.io/serviceaccount目錄后,這3個數據項映射為同名的3個文件。
[root@k8s-master01 ~]# kubectl get secrets my-sa-token-5kw7c -o yaml apiVersion: v1 data: ca.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0...... namespace: ZGVmYXVsdA== token: ZXlKaGJHY2lPaUpTVXpJMU5pSXNJbXRwWkNJNklqVkdSV...... kind: Secret metadata: annotations: kubernetes.io/service-account.name: my-sa kubernetes.io/service-account.uid: 07ce8903-29d5-456f-a0d4-5a7cf50c2527 ...... name: my-sa-token-5kw7c namespace: default resourceVersion: "2650574" uid: 9adfd7d5-1a3f-4cef-b8d5-2229f8cc3d8f type: kubernetes.io/service-account-token
2.2 ServiceAccount資源清單
完善地創建ServiceAccount資源的方式是使用資源規范,該規范比較簡單,它沒有spec字段,而是將幾個關鍵定義直接通過一級字段給出。
apiVersion: v1 # ServiceAccount所屬的API群組及版本 kind: ServiceAccount # 資源類型標識 metadata: name <string> # 資源名稱 namespace <string> # ServiceAccount是名稱空間級別的資源 automountServiceAccountToken <boolean> # 是否讓Pod自動掛載API令牌 secrets <[]Object> # 以該SA運行的Pod要使用的Secret對象所組成的列表 apiVersion <string> # 引用的Secret對象所屬的API群組及版本,可省略 kind <string> # 引用的資源類型,這里是指Secret,可省略 name <string> # 引用的Secret對象的名稱,通常僅給出該字段即可 namespace <string> # 引用的Secret對象所屬的名稱空間 uid <string> # 引用的Secret對象的標識符 imagePullSecrets <[]Object> # 引用的用於下載Pod中容器鏡像的Secret對象列表 name <string> # docker-registry類型的Secret資源名稱
serviceacceount實例
[root@k8s-master01 k8s-yaml]# vim sa-demo.yaml apiVersion: v1 kind: ServiceAccount metadata: name: namespace-admin namespace: default automountServiceAccountToken: true
將配置清單中定義的default-ns-admin資源創建到集群上,ServiceAccount控制器會自動為其附加以該資源名稱為前綴的Secret對象。隨后,用戶便可以在創建的Pod對象上引用該ServiceAccount對象,以借助權限管理機制實現自主控制Pod對象資源訪問權限。
[root@k8s-master01 k8s-yaml]# kubectl apply -f sa-demo.yaml serviceaccount/namespace-admin created [root@k8s-master01 k8s-yaml]# kubectl get sa NAME SECRETS AGE default 1 15d my-sa 1 15m namespace-admin 1 74s [root@k8s-master01 k8s-yaml]# kubectl get secrets NAME TYPE DATA AGE default-token-btlhc kubernetes.io/service-account-token 3 15d my-sa-token-5kw7c kubernetes.io/service-account-token 3 16m namespace-admin-token-gqmrb kubernetes.io/service-account-token 3 91s
另外,ServiceAccount資源還可以基於spec.imagePullSecret字段附帶一個由下載鏡像專用的Secret資源組成的列表,讓Pod對象在創建容器時且從私有鏡像倉庫下載鏡像文件之前完成身份認證。下面的示例定義了一個從本地私有鏡像倉庫Harbor下載鏡像文件時的Secret對象信息的ServiceAccount。
--- #定義harbor的賬號密碼在secret中 apiVersion: v1 kind: Secret metadata: name: local-harbor-secret type: Opaque data: username: YWRtaW4= #harbor的賬號,使用base64加密 password: MTIzNDU2 #harbor的密碼,使用base64加密 --- apiVersion: v1 kind: ServiceAccount metadata: name: eshop-sa namespace: eshop imagePullSecrets: - name: local-harbor-secret #調用secret
2.3 Pod資源上的服務賬戶
借助權限分配模型,按需應用“最小權限法則”將不同的資源操作權限配置給不同的賬戶,是有效降低安全風險的法則之一。有相當一部分Kubernetes原生應用程序依賴的權限都會大於從Pod默認ServiceAccount繼承到的權限,且彼此間各有不同,為這類應用定制一個專用的ServiceAccount並授予所需的全部權限是主流的解決方案。
[root@k8s-master01 k8s-yaml]# vim pod-with-sa.yml apiVersion: v1 kind: Pod metadata: name: pod-with-sa namespace: default spec: containers: - name: adminbox image: ikubernetes/admin-toolbox:v1.0 imagePullPolicy: IfNotPresent serviceAccountName: namespace-admin #調用前面創建的sa
該Pod資源創建完成后會以Secret存儲卷的形式自動掛載serviceaccounts/default-ns-admin的Secret對象
[root@k8s-master01 k8s-yaml]# kubectl apply -f pod-with-sa.yml pod/pod-with-sa created [root@k8s-master01 k8s-yaml]# kubectl get pod NAME READY STATUS RESTARTS AGE pod-with-sa 1/1 Running 0 29s [root@k8s-master01 k8s-yaml]# kubectl describe pod pod-with-sa Name: pod-with-sa Namespace: default ...... Containers: ...... Mounts: /var/run/secrets/kubernetes.io/serviceaccount from namespace-admin-token-gqmrb (ro) ...... Volumes: namespace-admin-token-gqmrb: Type: Secret (a volume populated by a Secret) SecretName: namespace-admin-token-gqmrb ......
Secret對象默認的掛載路徑是/var/run/secrets/kubernetes.io/serviceaccount。與API Server交互時,工作負載進程會使用該目錄下的ca.crt證書文件驗證API Server的服務器證書是否為自己信任的證書頒發機構(所在集群的kubernetes-ca)所簽發;驗證服務器身份成功通過后,工作負載向API Server請求操作namespace文件指定的名稱空間中的資源時,會將token文件中的令牌以承載令牌的認證方式提交給API Server進行驗證,權限校驗則由授權插件完成。
1)切換到pods/pod-with-sa的adminbox容器的Secret對象的掛載點為工作目錄以便於加載所需要的文件:
[root@k8s-master01 k8s-yaml]# kubectl exec -it pod-with-sa -- bash [root@pod-with-sa /]$ ls /var/run/secrets/kubernetes.io/serviceaccount ca.crt namespace token
2)在容器中使用curl命令向API Server發起訪問請求,--cacert選項用於指定驗證服務器端的CA證書,而-H選項用於自定義頭部,它指定了使用的承載令牌;下面的命令使用了“命令引用”機制來加載token和namespace文件的內容,其結果顯示容器進程使用指定的ServiceAccount進行身份認證成功。因為沒有授權所有該sa賬戶沒有該namespace下所有資源的操作權限。
[root@pod-with-sa /]$ cd /var/run/secrets/kubernetes.io/serviceaccount/ [root@pod-with-sa /var/run/secrets/kubernetes.io/serviceaccount]$ curl --cacert ./ca.crt -H "Authorization: Bearer $(cat ./token)" https://kubernetes/api/v1/namespaces/$(cat ./namespace)/ { "kind": "Status", "apiVersion": "v1", "metadata": { }, "status": "Failure", "message": "namespaces \"default\" is forbidden: User \"system:serviceaccount:default:namespace-admin\" cannot get resource \"namespaces\" in API group \"\" in the namespace \"default\"", "reason": "Forbidden", "details": { "name": "default", "kind": "namespaces" }, "code": 403
1)切換至kubectl管理終端運行如下資源創建命令:
[root@k8s-master01 k8s-yaml]# kubectl create rolebinding namespace-admin-binding-admin --clusterrole=admin \ > --serviceaccount=default:namespace-admin -n default rolebinding.rbac.authorization.k8s.io/namespace-admin-binding-admin created
2)回到pods/pod-with-sa的adminbox容器中再次運行訪問測試命令即可驗證授權結果,如下命令表示namespace-admin用戶已然有權限訪問default名稱空間。事實上,它擁有該名稱空間中所有資源的CRUD權限。
[root@k8s-master01 k8s-yaml]# kubectl exec -it pod-with-sa -- bash [root@pod-with-sa /]$ [root@pod-with-sa /]$ cd /var/run/secrets/kubernetes.io/serviceaccount/ [root@pod-with-sa /var/run/secrets/kubernetes.io/serviceaccount]$ curl --cacert ./ca.crt -H "Authorization: Bearer $(cat ./token)" https://kubernetes/api/v1/namespaces/$(cat ./namespace)/ { "kind": "Namespace", "apiVersion": "v1", "metadata": { "name": "default", "uid": "84b5b410-0c48-4163-a0cb-0af27a941b32", "resourceVersion": "195", "creationTimestamp": "2021-04-23T02:20:54Z", "managedFields": [ { "manager": "kube-apiserver", "operation": "Update", "apiVersion": "v1", "time": "2021-04-23T02:20:54Z", "fieldsType": "FieldsV1", "fieldsV1": {"f:status":{"f:phase":{}}} } ] }, "spec": { "finalizers": [ "kubernetes" ] }, "status": { "phase": "Active" } } #授權后可以正常訪問該namespace下的資源
三、 X509數字證書認證
X509數字證書認證常用的方式有“單向認證”和“雙向認證”。SSL / TLS最常見的應用場景是將X.509數字證書與服務器端關聯,但客戶端不使用證書。
單向認證是客戶端能夠驗證服務端的身份,但服務端無法驗證客戶端的身份,至少不能通過SSL / TLS協議進行。之所以如此,是因為SSL / TLS安全性最初是為互聯網應用開發,保護客戶端是高優先級的需求,它可以讓客戶端確保目標服務器不會被冒名頂替。
雙向認證的場景中,服務端與客戶端需各自配備一套數字證書,並擁有信任的簽證機構的證書列表。使用私有簽證機構頒發的數字證書時,除了證書管理和分發,通常還要依賴用戶手動將此私有簽證機構的證書添加到信任的簽證機構列表中。X509數字證書認證是Kubernetes默認使用的認證機制,采用雙向認證模式。
1、Kubernetes的X509數字證書認證體系
構建安全基礎通信環境的Kubernetes集群時,需要用到PKI基礎設施以完成獨立HTTPS安全通信及X509數字證書認證的場景有多種。API Server是整個Kubernetes集群的通信網關,controller-manager、scheduler、kubelet及kube-proxy等API Server的客戶端均需要經由API Server與etcd通信,完成資源狀態信息獲取及更新等。同樣出於安全通信的目的,Master的各組件(API Server、controller-manager和scheduler)需要基於SSL/TLS向外提供服務,而且與集群內部組件間通信時(主要是各節點上的kubelet和kube-proxy)還需要進行雙向身份驗證。
1.1 Kubernetes集群中的PKI設施與X509數字證書認證體系
kubernetes集群中的CA:
(2)Kubernetes集群CA及相關的數字證書
Kubernetes集群的其他各組件均需要通過kube-apiserver訪問集群資源,同樣出於安全性等目的,API Server也要借助HTTPS協議與其客戶端通信,而X509雙向數字證書認證僅是API Server支持的認證方式中的一種,客戶端也可能會使用HTTP Basic或Bearer Token認證方式接入到API Server。
kubelet也通過HTTPS端點暴露了一組API,這些API提供了多個不同級別的敏感數據接口,並支持來自客戶端的請求在節點和容器上執行不同級別的操作。默認情況下,匿名請求將自動隸屬於system:unauthenticated用戶組,其用戶名為system:anonymous。不過,kubelet可使用--anonymous-auth=false選項拒絕匿名訪問,並通過--client-ca-file選項指定CA方式驗證客戶端身份。kubelet可直接使用kubernetes-ca,同時應該為kube-apiserver使用--kubelet-client-certificate和--kubelet-client-key選項指定認證到kubelet的客戶端證書與私鑰。
(3)認證代理服務體系CA及相關的數字證書
API Server支持將認證功能交由外部的其他認證服務代為完成,這些服務通過特定的響應頭部返回身份驗證的結果狀態,API Server擴展服務就是認證代理的最常見應用場景之一。
除了API Server提供的核心API,Kubernetes還支持通過聚合層(aggregation layer)對其進行擴展。簡單來說,聚合層允許管理員在群集中部署使用其他Kubernetes風格的API,例如service catalog或用戶自定義的API Server等。聚合層本身打包在kube-apiserver程序中,並作為進程的一部分運行,但僅在管理員通過指定的APIService對象注冊擴展資源之后,它才會代理轉發相應的請求。而APIService則會由運行在Kubernetes集群上的Pod中的extention-apiserver實現。
創建一個APIService資源時,作為注冊和發現過程的一部分,kube-aggregator控制器(位於kube-apiserver內部)將與extention-apiserver的HTTP2連接[1],而后將經過身份驗證的用戶請求經由此連接代理到extention-apiserver上,於是,kube-aggregator被設置為執行RequestHeader客戶端認證。 不過,只有kube-apiserver在啟動時使用了如下選項時,才能啟用其內置的聚合層:
▪--requestheader-client-ca-file=<path to aggregator CA cert> ▪--requestheader-allowed-names=front-proxy-client ▪--requestheader-extra-headers-prefix=X-Remote-Extra- ▪--requestheader-group-headers=X-Remote-Group ▪--requestheader-username-headers=X-Remote-User ▪--proxy-client-cert-file=<path to aggregator proxy cert> ▪--proxy-client-key-file=<path to aggregator proxy key>
1.2 Kubernetes集群需要的數字證書
完整運行的Kubernetes系統需要為etcd、API Server及前端代理(front proxy)生成多個數字證書
普通用戶使用這種認證方式的前提是,它們各自擁有自己的數字證書,證書中的CN和O屬性分別提供了准確的用戶標識和用戶組。API Server可接受或拒絕這些證書,評估標准在於證書是否由API Server信任的客戶端證書CA(由選項--client-ca-file指定,默認為kubernetes-ca)所簽發,但API Server自身並不了解這些證書,因此也不了解各個用戶,它僅知道負責為各個客戶端頒發證書的CA。因此,相較於靜態密碼文件認證和靜態令牌文件認證來說,X509數字證書認證實現了用戶管理與Kubernetes集群的分離,且有着更好的安全性。
X509數字證書認證因其可不依賴第三方服務、有着更好的安全性以及與API Server相分離等優勢,成為Kubernetes系統內部默認使用的認證方式。但是,將X509數字證書用於普通用戶認證的缺陷也是顯而易見的,它主要表現在如下兩個方面。
▪證書的到期時間在頒發時設定,其生命周期往往很長(數月甚至數年),且事實上的身份驗證功能也是在頒發時完成,若撤銷用戶的可用身份只能使用證書吊銷功能完成。
▪現實使用中,證書通常由一些通用的簽證機構簽發,而API Server需要信任該CA;顯然,獲得該CA使用權限的用戶便能夠授予自己可認證到的Kubernetes的任意憑據或身份,因而集群管理員必須自行集中管理證書,管理員的工作量比較大。
2、TLS Bootstrapping機制
TLS Bootstrapping機制有什么用途呢?
新的工作節點接入Kubernetes集群時需要事先配置好相關的證書和私鑰等以進行安全通信,管理員可以手動管理這些證書,也可以選擇由kubelet自行生成私鑰和自簽證書。集群略具規模后,第一種方式無疑會為管理員帶來不小的負擔,但對於保障集群安全運行卻又必不可少。第二種方式降低了管理員的工作量,卻也損失了PKI本身具有的諸多優勢。取而代之,Kubernetes采用了由kubelet自行生成私鑰和證書簽署請求,而后發送給集群上的證書簽署進程(CA),由管理員審核后予以簽署或直接由控制器進程自動統一簽署。這種方式就是kubelet TLS Bootstrapping機制,它實現了前述第一種方式的功能,卻基本不增加管理員工作量。
一旦開啟TLS Bootstrapping功能,任何kubelet進程都可以向API Server發起驗證請求並加入到集群中,包括那些非計划或非授權主機,這必將增大管理驗證操作時的審核工作量。為此,API Server設計了可經由--enable-bootstrap-token-auth選項啟用的Bootstrap Token(引導令牌)認證插件。該插件用於加強TLS Bootstrapping機制,僅那些通過Bootstrap Token認證的請求才可以使用TLS Bootstrapping發送證書簽署請求給控制平面,並由相應的審批控制器(approval controller)完成證書簽署和分發。
說明:
kubeadm啟用了節點加入集群時的證書自動簽署功能,因此加入過程在kubeadm join命令成功后即完成。
~# kubeadm alpha certs check-expiration
~# kubeadm alpha certs renew all
四、kubeconfig配置文件
基於無狀態協議HTTP/HTTPS的API Server需要驗證每次連接請求中的用戶身份,因而kube-controller-manager、kube-scheduler和kube-proxy等各類客戶端組件必須能自動完成身份認證信息的提交,但通過程序選項來提供這些信息會導致敏感信息泄露。另外,管理員還面臨着使用kubectl工具分別接入不同集群時的認證及認證信息映射難題。為此,Kubernetes設計了一種稱為kubeconfig的配置文件,它保存有接入一到多個Kubernetes集群的相關配置信息,並允許管理員按需在各配置間靈活切換。
1、kubeconfig文件格式
kubeconfig文件中,各集群的接入端點以列表形式定義在clusters配置段中,每個列表項代表一個Kubernetes集群,並擁有名稱標識;各身份認證信息(credentials)定義在users配置段中,每個列表項代表一個能夠認證到某Kubernetes集群的憑據。將身份憑據與集群分開定義以便復用,具體使用時還要以context(上下文)在二者之間按需建立映射關系,各context以列表形式定義在contexts配置段中,而當前使用的映射關系則定義在current-context配置段中。
kubectl config view命令能打印kubeconfig文件的內容,下面的命令結果顯示了默認路徑下的文件配置,包括集群列表、用戶列表、上下文列表以及當前使用的上下文(current-context)等。
[root@k8s-master01 ~]# kubectl config view apiVersion: v1 #k8s的集群列表 clusters: - cluster: certificate-authority-data: DATA+OMITTED server: https://192.168.32.201:6443 name: cluster1 #上下文列表 contexts: - context: cluster: cluster1 user: admin name: context-cluster1-admin #當前上下文列表 current-context: context-cluster1-admin #用戶列表 kind: Config preferences: {} users: - name: admin user: client-certificate-data: REDACTED client-key-data: REDACTED
kubectl config的常用子命令有如下幾項:
▪view:打印kubeconfig文件內容。 ▪set-cluster:設定新的集群信息,以單獨的列表項保存於clusters配置段。 ▪set-credentials:設置認證憑據,保存為users配置段的一個列表項。 ▪set-context:設置新的上下文信息,保存為contexts配置段的一個列表項。 ▪use-context:設定current-context配置段,確定當前以哪個用戶的身份接入到哪個集群之中。 ▪delete-cluster:刪除clusters中指定的列表項。 ▪delete-context:刪除contexts中指定的列表項。 ▪get-clusters:獲取clusters中定義的集群列表。 ▪get-contexts:獲取contexts中定義的上下文列表。
2、自定義kubeconfig文件
一個完整kubeconfig配置的定義至少應該包括集群、身份憑據、上下文及當前上下文4項,但在保存有集群和身份憑據的現有kubeconfig文件基礎上添加新的上下文時,可能只需要提供身份憑據而復用已有的集群定義,具體的操作步驟要按實際情況進行判定。
2.1 基於靜態密碼文件認證
例如,我們下面嘗試創建一個新的kubeconfig文件,設定它使用此前定義的基於靜態密碼文件認證的ilinux用戶接入到現有的Kubernetes集群,該集群API Server的網絡端點為https://192.168.32.248:6443,相關的CA證書保存在Master節點上的/etc/kubernetes/pki/ca.crt文件中,而配置結果則使用--kubeconfig選項保存在當前用戶主目錄下的.kube/kube-dev.config文件中。
步驟1:添加集群配置,包括集群名稱、API Server URL和信任的CA的證書;clusters配置段中的各列表項名稱需要唯一。
kubeadm部署的k8s集群
[root@k8s-master01 ~]# kubectl config set-cluster kube-dev --embed-certs=true \ --certificate-authority=/etc/kubernetes/pki/ca.crt \ --server="https://192.168.32.248:6443" \ --kubeconfig=$HOME/.kube/kube-dev.config
步驟2:添加身份憑據,使用靜態密碼文件認證的客戶端提供用戶名和密碼即可。
[root@k8s-master01 ~]# kubectl config set-credentials ilinux \ --username=ilinux --password=ilinux@123 \ --kubeconfig=$HOME/.kube/kube-dev.config User "ilinux" set.
步驟3:以用戶ilinux的身份憑據與kube-dev集群建立映射關系。
[root@k8s-master01 ~]# kubectl config set-context ilinux@kube-dev \ --cluster=kube-dev --user=ilinux \ --kubeconfig=$HOME/.kube/kube-dev.config Context "ilinux@kube-dev" created.
步驟4:設置當前上下文為ilinux@kube-dev
[root@k8s-master01 ~]# kubectl config use-context ilinux@kube-dev --kubeconfig=$HOME/.kube/kube-dev.config Switched to context "ilinux@cluster1".
步驟5:預覽kube-dev.config文件,確認其配置信息。
[root@k8s-master01 ~]# kubectl config view --kubeconfig=$HOME/.kube/kube-dev.config apiVersion: v1 clusters: - cluster: certificate-authority-data: DATA+OMITTED server: https://192.168.32.248:6443 name: kube-dev contexts: - context: cluster: kube-dev user: ilinux name: ilinux@kube-dev current-context: ilinux@kube-dev kind: Config preferences: {} users: - name: ilinux user: password: ilinux@123 username: ilinux
步驟6:使用該kubeconfig中的當前上下文進行測試訪問;該用戶僅被授權了default名稱空間的所有權限,因而不具有列出集群級別資源的權限,但能查看default名稱空間的狀態。
[root@k8s-master01 ~]#kubectl get namespaces --kubeconfig=$HOME/.kube/kube-dev.config Error from server (Forbidden): namespaces is forbidden: User "ilinux" cannot list resource "namespaces" in API group "" at the cluster scope ~$ kubectl get namespaces/default --kubeconfig=$HOME/.kube/kube-dev.config NAME STATUS AGE default Active 3d #有時需要重新拷貝admin.conf文件 #[root@k8s-master01 ~]#cp /etc/kubernetes/admin.conf /root/.kube/config
步驟7:添加身份憑據,使用靜態令牌文件認證的客戶端認證時只需要提供靜態令牌信息
~$ TOKEN=$(sudo awk -F "," '$2=="ik8s"{print $1}' /etc/kubernetes/authfiles/token.csv) ~$ kubectl config set-credentials ik8s --token="$TOKEN" \ --kubeconfig=$HOME/.kube/kube-dev.config User "ik8s" set.
步驟8:為用戶ik8s的身份憑據與kube-dev集群建立映射關系。
~$ kubectl config set-context ik8s@kube-dev \ --cluster=kube-dev --user=ik8s \ --kubeconfig=$HOME/.kube/kube-dev.config Context "ik8s@kube-dev" created.
步驟9:將當前上下文切換為ik8s@kube-dev。
~$ kubectl config use-context ik8s@kube-dev --kubeconfig=$HOME/.kube/kube-dev.config Switched to context "ik8s@kube-dev".
步驟10:預覽kube-dev.config文件,確認ik8s用戶相關的各項配置信息。
$ kubectl config view --kubeconfig=$HOME/.kube/kube-dev.config
步驟11:依舊使用當前上下文發起集群訪問測試,ik8s用戶未獲得任何授權,但它能夠被系統識別為ik8s用戶,這表示身份認證請求成功返回;我們這次使用kubectl的whoami插件進行測試:
~ $ kubectl whoami --kubeconfig=$HOME/.kube/kube-dev.config ik8s
說明:
事實上除了靜態令牌,客戶端認證到API Server的各種令牌都可以使用前面的這種方式添加到kubeconfig文件中,包括ServiceAccount令牌、OpenID Connnect令牌和Bootstrap令牌等。
當kubectl引用了擁有兩個及以上context的kubeconfig文件時,可隨時通過kubectl config use-context命令在不同上下文之間切換,它們可能使用不同的身份憑據接入相同的集群或不同的集群之上。如下命令結果表示當前加載的配置文件中共有兩個context,而擁有星號標識的是當前使用的context,即current-context。
~ kubectl config get-contexts --kubeconfig=HOME/.kube/kube-dev.config CURRENT NAME CLUSTER AUTHINFO NAMESPACE - ik8s@kube-dev kube-dev ik8s ilinux@kube-dev kube-dev ilinux
2.2 X509數字證書身份憑據
kubeadm部署Kubernetes集群的過程中會自動生成多個kubeconfig文件,它們是默認位於/etc/kubernetes目錄下以.conf為后綴名的文件,前綴名稱代表了它的適用場景,其中的admin.conf中保存了以X509數字證書格式提供身份憑據的kubernetes-admin用戶,該用戶能夠以管理員的身份對當前集群發起資源操作請求。
由kubeadm初始化的Kubernetes集群上,kube-apiserver默認信任的CA就是集群自己的kubernetes-ca,該CA的數字證書是Master節點之上的/etc/kubernetes/pki/ca.crt文件。於是,客戶端按需生成證書簽署請求,再由管理員通過kubernetes-ca為客戶端簽署證書,便可讓客戶端以其證書中的CN為用戶名認證到API Server上。為了便於說明問題,下面將客戶端生成私鑰和證書簽署請求,服務器簽署該請求,以及客戶端將證書配置為kubeconfig文件的步驟統一進行說明,所有操作都在Master節點上運行。
步驟1:以客戶端的身份,生成目標用戶賬號mason的私鑰及證書簽署請求,保存在用戶主目錄下的.certs目錄中。
① 生成私鑰文件,注意其權限應該為600以阻止其他用戶讀取。
[root@k8s-master01 ~]# mkdir $HOME/.certs [root@k8s-master01 ~]# (umask 077; openssl genrsa -out $HOME/.certs/mason.key 2048) Generating RSA private key, 2048 bit long modulus .............................................+++ ...............................+++ e is 65537 (0x10001)
② 創建證書簽署請求,-subj選項中CN的值將被API Server識別為用戶名,O的值將被識別為用戶組。
[root@k8s-master01 ~]# openssl req -new -key $HOME/.certs/mason.key \ -out $HOME/.certs/mason.csr \ -subj "/CN=mason/O=developers"
① 基於kubernetes-ca簽署證書,並為其設置合理的生效時長,例如365天。
[root@k8s-master01 ~]# openssl x509 -req -days 365 -CA /etc/kubernetes/pki/ca.crt \ -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial \ -in $HOME/.certs/mason.csr -out $HOME/.certs/mason.crt Signature ok subject=/CN=mason/O=developers Getting CA Private Key
② 必要時,還可以驗證生成的數字證書的相關信息(可選)。
[root@k8s-master01 ~]# openssl x509 -in $HOME/.certs/mason.crt -text -noout
① 根據X509數字證書及私鑰創建身份憑據,列表項名稱同目標用戶名。
[root@k8s-master01 ~]# kubectl config set-credentials mason --embed-certs=true \ --client-certificate=$HOME/.certs/mason.crt \ --client-key=$HOME/.certs/mason.key User "mason" set.
② 配置context,以mason的身份憑據訪問已定義的Kubernetes集群,該context的名稱為mason@kubernetes。
[root@k8s-master01 ~]# kubectl config set-context mason@kubernetes --cluster=kubernetes --user=mason Context "mason@kubernetes" created.
③ 將當前上下文切換為mason@kubernetes,或直接在kubectl命令上使用“--context= 'mason@kubernetes'”以完成該用戶的認證測試,下面的命令選擇了以第二種方式進行認證,雖然提示權限錯誤,但mason用戶已被API Server正確識別;
[root@k8s-master01 ~]# kubectl get namespaces/default --context='mason@kubernetes' Error from server (Forbidden): namespaces "default" is forbidden: User "mason" cannot get resource "namespaces" in API group "" in the namespace "default"
通過創建自定義的數字證書,實現了將mason用戶認證到API Server,並將該用戶的身份憑據保存至kubeconfig文件中。還沒有給該用戶授權。
kubectl config一次僅能使用單個kubeconfig文件。事實上,若將兩個文件路徑以冒號分隔並賦值給KUBECONFIG環境變量,也能夠讓kubectl config一次加載多個文件信息,優先級由高到低為各文件自左而右的次序。下面兩條命令的結果顯示,左側文件擁有較高的優先級,因而其配置的current-context成為默認使用的context。
[root@k8s-master01 ~]# export KUBECONFIG="$HOME/.kube/config:$HOME/.kube/kube-dev.config" [root@k8s-master01 ~]# kubectl config get-contexts CURRENT NAME CLUSTER AUTHINFO NAMESPACE ilinux@kube-dev kube-dev ilinux * kubernetes-admin@kubernetes kubernetes kubernetes-admin mason@kubernetes kubernetes mason
聯合使用多個kubeconfig時,同樣可以按需調整當前使用的context,其實現方式同使用單個kubeconfig文件並沒有不同之處。
[root@k8s-master01 ~]# kubectl config use-context mason@kubernetes Switched to context "mason@kubernetes". [root@k8s-master01 ~]# kubectl config get-contexts CURRENT NAME CLUSTER AUTHINFO NAMESPACE ilinux@kube-dev kube-dev ilinux kubernetes-admin@kubernetes kubernetes kubernetes-admin * mason@kubernetes kubernetes mason
kubectl config view命令會將多個配置文件的內容按給定的次序連接並輸出,其風格類似於Linux系統的cat命令。但我們也能夠在view命令中將加載的多個配置文件展平為單個配置文件的格式予以輸出,並將結果保存在指定路徑下便能將多個kubeconfig文件合並為一。例如,下面的命令便將KUBECONFIG環境變量中指定的兩個kubeconfig文件合並成了單個配置文件。
[root@k8s-master01 ~]#kubectl config view --merge --flatten > $HOME/.kube/kube.config
此時,切換kubectl config加載新生成的kubeconfig配置文件,它便直接擁有了此前兩個文件中定義的所有信息,且current-context亦遵循此前命令中的設定,即mason@kubernetes。
[root@k8s-master01 ~]# KUBECONFIG="$HOME/.kube/kube.config" [root@k8s-master01 ~]# kubectl config get-contexts CURRENT NAME CLUSTER AUTHINFO NAMESPACE ilinux@kube-dev kube-dev ilinux kubernetes-admin@kubernetes kubernetes kubernetes-admin * mason@kubernetes kubernetes mason
4.1 生成私鑰和證書簽署請求
① 生成私鑰文件,注意其權限應該為600以阻止其他用戶讀取。
[root@k8s-master01 ~]# mkdir $HOME/.certs [root@k8s-master01 ~]# (umask 077; openssl genrsa -out $HOME/.certs/mason.key 2048) Generating RSA private key, 2048 bit long modulus .............................................+++ ...............................+++ e is 65537 (0x10001)
② 創建證書簽署請求,-subj選項中CN的值將被API Server識別為用戶名,O的值將被識別為用戶組。
[root@k8s-master01 ~]# openssl req -new -key $HOME/.certs/mason.key \ -out $HOME/.certs/mason.csr \ -subj "/CN=mason/O=developers"
基於kubernetes-ca簽署證書,並為其設置合理的生效時長,例如365天。
[root@k8s-master01 ~]# openssl x509 -req -days 365 -CA /etc/kubernetes/pki/ca.crt \ -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial \ -in $HOME/.certs/mason.csr -out $HOME/.certs/mason.crt Signature ok subject=/CN=mason/O=developers Getting CA Private Key
4.3 生成mason-kube.config
① 生成kubeconfig授權文件:
[root@k8s-master01 ~]#kubectl config set-cluster kubernetes \ --certificate-authority=/etc/kubernetes/pki/ca.crt \ --embed-certs=true \ --server=https://192.168.32.248:6443 \ --kubeconfig=$HOME/.kube/mason-kube.config
② 設置客戶端認證
[root@k8s-master01 ~]#kubectl config set-credentials mason \ --client-key=$HOME/.certs/mason.key \ --client-certificate=$HOME/.certs/mason.crt \ --embed-certs=true \ --kubeconfig=$HOME/.kube/mason-kube.config
③配置context,以mason的身份憑據訪問已定義的Kubernetes集群,該context的名稱為mason@kubernetes。
[root@k8s-master01 ~]#kubectl config set-context mason@kubernetes \ --cluster=kubernetes \ --user=mason \ --kubeconfig=$HOME/.kube/mason-kube.config
④切換當前上下文
[root@k8s-master01 ~]# kubectl config use-context mason@kubernetes --kubeconfig=$HOME/.kube/mason-kube.config
⑤直接在kubectl命令上使用“--context= 'mason@kubernetes'”以完成該用戶的認證測試,下面的命令選擇了以第二種方式進行認證,雖然提示權限錯誤,但mason用戶已被API Server正確識別。
[root@k8s-master01 .kube]# kubectl get ns --kubeconfig=$HOME/.kube/mason-kube.config Error from server (Forbidden): namespaces is forbidden: User "mason" cannot list resource "namespaces" in API group "" at the cluster scope
DAC(自主訪問控制)、MAC(強制訪問控制)、RBAC(基於角色的訪問控制)和ABAC(基於屬性的訪問控制)這4種主流的權限管理模型中,Kubernetes支持使用后兩種完成普通賬戶和服務賬戶的權限管理,另外支持的權限管理模型還有Node和Webhook兩種。
RBAC是一種新型、靈活且使用廣泛的訪問控制機制,它將權限授予角色,通過讓“用戶”扮演一到多個“角色”完成靈活的權限管理,這有別於傳統訪問控制機制中將權限直接賦予使用者的方式。相對於Kubernetes支持的ABAC和Webhook等授權機制,RBAC具有如下優勢:
▪對集群中的資源和非資源型URL的權限實現了完整覆蓋。
▪整個RBAC完全由少數幾個API對象實現,而且與其他API對象一樣可以用kubectl或API調用進行操作。
▪支持權限的運行時調整,無須重新啟動API Server。
1、RBAC授權模型
RBAC中用戶、角色與權限之間的關系:
簡單來說,RBAC就是一種訪問控制模型,它以角色為中心界定“誰”(subject)能夠“操作”(verb)哪個或哪類“對象”(object)。動作的發出者即“主體”,通常以“賬號”為載體,在Kubernetes系統上,它可以是普通賬戶,也可以是服務賬戶。“動作”用於表明要執行的具體操作,包括創建、刪除、修改和查看等行為,對於API Server來說,即PUT、POST、DELETE和GET等請求方法。而“對象”則是指管理操作能夠施加的目標實體,對Kubernetes API來說主要指各類資源對象以及非資源型URL。 API Server是RESTful風格的API,各類客戶端由認證插件完成身份驗證,而后通過HTTP協議的請求方法指定對目標對象的操作請求,並由授權插件進行授權檢查,而操作的對象則是URL路徑指定的REST資源。
Kubernetes系統上的普通賬戶或服務賬戶向API Server發起資源操作請求,並以相應HTTP方法承載,如下圖所示,由運行在API Server之上的授權插件RBAC進行鑒權。
Kubernetes系統的RBAC授權插件將角色分為Role和ClusterRole兩類,它們都是Kubernetes內置支持的API資源類型,其中Role作用於名稱空間級別,用於承載名稱空間內的資源權限集合,而ClusterRole則能夠同時承載名稱空間和集群級別的資源權限集合。Role無法承載集群級別的資源類型的操作權限,這類的資源包括集群級別的資源(例如Nodes)、非資源類型的端點(例如/healthz),以及作用於所有名稱空間的資源(例如跨名稱空間獲取任何資源的權限)等。 利用Role和ClusterRole兩類角色進行賦權時,需要用到另外兩種資源RoleBinding和ClusterRoleBinding,它們同樣是由API Server內置支持的資源類型。RoleBinding用於將Role綁定到一個或一組用戶之上,它隸屬於且僅能作用於其所在的單個名稱空間。RoleBinding可以引用同一名稱中的Role,也可以引用集群級別的ClusterRole,但引用ClusterRole的許可權限會降級到僅能在RoleBinding所在的名稱空間生效。而ClusterRoleBinding則用於將ClusterRole綁定到用戶或組,它作用於集群全局,且僅能夠引用ClusterRole。
Kubernetes集群用戶大體規划為集群管理員、名稱空間管理員和用戶(通常為開發人員)3類。
▪集群管理員可以創建、讀取、更新和刪除任何策略對象,能夠創建命名空間並將其分配給名稱空間管理員;此角色適用於在整個集群中管理所有租戶或項目的管理員。
▪名稱空間管理員可以管理其名稱空間中的用戶,此角色適用於特定單一租戶或項目的管理員。
▪開發者用戶可以創建、讀取、更新和刪除名稱空間內的非策略對象,如Pod、Job和Ingress等,但只在它們有權訪問的名稱空間中擁有這些權限。
2.1 、Role與ClusterRole
Role和ClusterRole是API Server內置的兩種資源類型,它們在本質上都只是一組許可權限的集合。Role和ClusterRole的資源規范完全相同,該規范沒有使用spec字段,而是直接使用rules字段嵌套授權規則列表。規則的基本要素是動作(verb)和相關的目標資源,后者支持指定一個或多個資源類型、特定資源類型下的單個或多個具體的資源,以及非資源類型的URL等。在Role和ClusterRole資源上定義的rules也稱為PolicyRule,即策略規則,它可以內嵌的字段有如下幾個。
1)apiGroups <[]string>:目標資源的API群組名稱,支持列表格式指定多個組,空值("")表示核心群組。 2)resources <[]string>:規則應用的目標資源類型,例如pods、services、deployments和daemonsets等,未同時使用resourceNames字段時,表示指定類型下的所有資源。ResourceAll表示所有資源。 3)resourceNames <[]string>:可選字段,指定操作適用的具體目標資源名稱。 4)nonResourceURLs <[]string>:用於定義用戶有權限訪問的網址列表,它並非名稱空間級別的資源,因此只能應用於ClusterRole,Role支持此字段僅是為了格式上的兼容;該字段在一條規則中與resources和resourceNames互斥。 5)verbs <[]string>:可應用在此規則匹配到的所有資源類型的操作列表,可用選項有get、list、create、update、patch、watch、proxy、redirect、delete和deletecollection;此為必選字段。
下面的配置清單示例(pods-reader-rbac.yaml)在default名稱空間中定義了一個名稱為Role的資源,它設定了讀取、列出及監視pods和services資源,以及pods/log子資源的許可權限。
kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: namespace: default name: pods-reader rules: - apiGroups: [""] # "" 表示核心API群組 resources: ["pods", "services", "pods/log"] #資源對象 verbs: ["get", "list", "watch"] #授權
絕大多數資源可通過其資源類型的名稱引用,例如pods或services等,這些名稱與它們在API endpoint中的形式相同。另外,有些資源類型支持子資源,例如Pod對象的/log,Node對象的/status等,它們在API Server上的URL形如下面的表示格式。
/api/v1/namespaces/{namespace}/pods/{name}/log
ClusterRole資源隸屬於集群級別,它引用名稱空間級別的資源意味着相關的操作權限能夠在所有名稱空間生效,同時,它也能夠引用Role所不支持的集群級別的資源類型,例如nodes和persistentvolumes等。下面的清單示例(nodes-admin-rbac.yaml)定義了ClusterRole資源nodes-admin,它擁有管理集群節點信息的權限。ClusterRole不屬於名稱空間,所以其配置不能夠使用metadata.namespace字段。
kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: nodes-admin rules: - apiGroups: [""] resources: ["nodes"] verbs: ["*"]
將上面兩個清單中分別定義的Role和ClusterRole資源創建到集群上,以便按需調用並驗證其權限。
[root@k8s-master01 apps]# kubectl apply -f pods-reader-rbac.yaml -f nodes-admin-rbac.yaml role.rbac.authorization.k8s.io/pods-reader created clusterrole.rbac.authorization.k8s.io/nodes-admin created
Role或ClusterRole資源的詳細描述能夠以比較直觀的方式打印相關的規則定義,由kubectl describe roles/pods-reader clusterroles/nodes-admin命令輸出的規則定義。
[root@k8s-master01 apps]# kubectl describe roles pods-reader Name: pods-reader Labels: <none> Annotations: <none> PolicyRule: Resources Non-Resource URLs Resource Names Verbs --------- ----------------- -------------- ----- pods/log [] [] [get list watch] pods [] [] [get list watch] service [] [] [get list watch] [root@k8s-master01 apps]# kubectl describe clusterroles nodes-admin Name: nodes-admin Labels: <none> Annotations: <none> PolicyRule: Resources Non-Resource URLs Resource Names Verbs --------- ----------------- -------------- ----- nodes [] [] [*]
kubectl命令也分別提供了創建Role和ClusterRole資源的命令式命令,create role和create clusterrole,它們支持如下幾個關鍵選項。
▪--verb:指定可施加於目標資源的動作,支持以逗號分隔的列表值,也支持重復使用該選項分別指定不同的動作,例如--verb=get,list,watch,或者--verb=get --verb=list --verb=watch。 ▪--resource:指定目標資源類型,使用格式類似於--verb選項。 ▪--resource-name:指定目標資源,使用格式類似於--verb選項。 ▪--non-resource-url:指定非資源類型的URL,使用格式類似於--verb選項,但僅適用於clusterrole資源。
例如,下面的第一條命令創建了dev名稱空間,第二條命令在該名稱空間創建了一個具有所有資源管理權限的roles/admin資源,第三條命令則創建了一個有PVC和PV資源管理權限的clusterroles/pv-admin資源。
[root@k8s-master01 ~]# kubectl create namespace dev namespace/dev created [root@k8s-master01 ~]# kubectl create role admin -n dev --resource="*.*" \ --verb="get,list,watch,create,delete,deletecollection,patch,update" role.rbac.authorization.k8s.io/admin created [root@k8s-master01 ~]# kubectl create clusterrole pv-admin --verb="*" \ --resource="persistentvolumeclaims,persistentvolumes" clusterrole.rbac.authorization.k8s.io/pv-admin created
2.2 RoleBinding與ClusterRoleBinding
RoleBinding負責在名稱空間級別向普通賬戶、服務賬戶或組分配Role或ClusterRole,而ClusterRoleBinding則只能用於在集群級別分配ClusterRole。但二者的配置規范格式完全相同,它們沒有spec字段,直接使用subjects和roleRef兩個嵌套的字段。其中,subjects的值是一個對象列表,用於給出要綁定的主體,而roleRef的值是單個對象,用於指定要綁定的Role或ClusterRole資源。
subjects字段的可嵌套字段如下:
▪apiGroup <string>:要引用的主體所屬的API群組,對於ServiceAccount類的主體來說默認為"",而User和Group類主體的默認值為"rbac.authorization.k8s.io"。 ▪kind <string>:要引用的資源對象(主體)所屬的類別,可用值為User、Group和ServiceAccount,必選字段。 ▪name <string>:引用的主體的名稱,必選字段。 ▪namespace <string>:引用的主體所屬的名稱空間,對於非名稱空間類型的主體,例如User和Group,其值必須為空,否則授權插件將返回錯誤信息。
roleRef的可嵌套字段如下:
▪apiGroup <string>:引用的資源(Role或ClusterRole)所屬的API群組,必選字段。 ▪kind <string>:引用的資源所屬的類別,可用值為Role或ClusterRole,必選字段。 ▪name <string>:引用的資源(Role或ClusterRole)的名稱。
例如下面配置清單中的RoleBindings在dev名稱空間中把admin角色分配給前面配置的用戶mason,從而mason擁有了此角色之上的所有許可授權。
配置role角色admin
[root@k8s-master01 ~]# kubectl create namespace dev namespace/dev created [root@k8s-master01 ~]# kubectl create role admin -n dev --resource="*.*" \ --verb="get,list,watch,create,delete,deletecollection,patch,update" role.rbac.authorization.k8s.io/admin created [root@k8s-master01 apps]# kubectl describe roles admin -n dev Name: admin Labels: <none> Annotations: <none> PolicyRule: Resources Non-Resource URLs Resource Names Verbs --------- ----------------- -------------- ----- *.* [] [] [get list watch create delete deletecollection patch update]
將role角色admin與普通用戶mason綁定
[root@k8s-master01 apps]# vim mason-rolebindings.yaml kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: mason-admin namespace: dev subjects: - kind: User name: mason apiGroup: rbac.authorization.k8s.io roleRef: kind: Role name: admin apiGroup: rbac.authorization.k8s.io
在綁定role角色之前,mason用戶無法在dev命名空間下創建pod
[root@k8s-master01 apps]# kubectl run demoapp --image="ikubernetes/demoapp:v1.0" -n dev --kubeconfig=/root/.kube/mason-kube.config Error from server (Forbidden): pods is forbidden: User "mason" cannot create resource "pods" in API group "" in the namespace "dev"
示例中的RoleBinding資源mason-admin創建到集群上,便能夠以該用戶的身份測試其繼承而來的權限是否已然生效。下面以--config選項臨時將用戶切換為mason進行資源管理,測試命令及結果顯示,mason已然具有dev名稱空間下的資源操作權限。
[root@k8s-master01 apps]# kubectl apply -f mason-rolebindings.yaml rolebinding.rbac.authorization.k8s.io/mason-admin created [root@k8s-master01 apps]# kubectl run demoapp --image="ikubernetes/demoapp:v1.0" -n dev --kubeconfig=/root/.kube/mason-kube.config pod/demoapp created [root@k8s-master01 apps]# kubectl get pods -n dev --kubeconfig=/root/.kube/mason-kube.config NAME READY STATUS RESTARTS AGE demoapp 1/1 Running 0 11s [root@k8s-master01 apps]# kubectl delete pods demoapp -n dev --kubeconfig=/root/.kube/mason-kube.config pod "demoapp" deleted
一種高效分配權限的做法是,由集群管理員在集群范圍預先定義好一組具有名稱空間級別資源權限的ClusterRole資源,而后由RoleBinding分別在不同名稱空間中引用它們,從而在多個名稱空間向不同用戶授予RoleBinding所有名稱空間下的相同權限。
Role和RoleBinding是名稱空間級別的資源,它們僅能用於完成單個名稱空間內的訪問控制,需要賦予某主體多個名稱空間中的訪問權限時就不得不在各名稱空間分別進行。若需要完成集群全局的資源管理授權,或者希望資源操作能夠針對Nodes、Namespaces和PersistentVolumes等集群級別的資源進行,或者針對/api、/apis、/healthz或/version等非資源型URL路徑進行,就需要使用ClusterRoleBinding。
說明;nonResourceURLs資源僅支持get訪問權限。
下面的配置清單示例(rolebinding-and-clusterrolebinding-rbac.yaml)中,rolebinding/mason-pvc-admin資源位於dev名稱空間,它使用RoleBinding為用戶mason分配了pv-admin這一集群角色,而clusterrolebinding/ik8s-pv-admin隸屬集群級別,它使用ClusterRoleBinding為ik8s分配了pv-admin這一集群。
[root@k8s-master01 apps]#vim rolebinding-and-clusterrolebinding-rbac.yaml kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: mason-pvc-admin namespace: dev subjects: - kind: User name: mason apiGroup: rbac.authorization.k8s.io roleRef: kind: ClusterRole name: pv-admin apiGroup: rbac.authorization.k8s.io --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: ik8s-pv-admin subjects: - kind: User name: ik8s apiGroup: rbac.authorization.k8s.io roleRef: kind: ClusterRole name: pv-admin apiGroup: rbac.authorization.k8s.io
運行rolebinding-and-clusterrolebinding-rbac.yaml
[root@k8s-master01 apps]# kubectl apply -f rolebinding-and-clusterrolebinding-rbac.yaml rolebinding.rbac.authorization.k8s.io/mason-pvc-admin created clusterrolebinding.rbac.authorization.k8s.io/ik8s-pv-admin created
我們使用mason用戶進行測試,它僅能訪問dev名稱空間下的名稱空間級別的PVC資源,且無法通過RoleBinding從clusterroles/pv-admin繼承指定名稱空間之外的任何權限,如下面的命令及結果所示。
[root@k8s-master01 apps]# kubectl get pvc -n dev --kubeconfig=/root/.kube/mason-kube.config No resources found in dev namespace. [root@k8s-master01 apps]# kubectl get pv -n dev --kubeconfig=/root/.kube/mason-kube.config Error from server (Forbidden): persistentvolumes is forbidden: User "mason" cannot list resource "persistentvolumes" in API group "" at the cluster scope [root@k8s-master01 apps]# kubectl get pv -n default --kubeconfig=/root/.kube/mason-kube.config Error from server (Forbidden): persistentvolumes is forbidden: User "mason" cannot list resource "persistentvolumes" in API group "" at the cluster scope [root@k8s-master01 apps]# kubectl get pvc -n default --kubeconfig=/root/.kube/mason-kube.config Error from server (Forbidden): persistentvolumeclaims is forbidden: User "mason" cannot list resource "persistentvolumeclaims" in API group "" in the namespace "default"
使用ik8s用戶進行測試,它通過ClusterRoleBinding從clusterroles/pv-admin繼承了該集群角色的所有授權,如下面的命令及結果所示。
[root@k8s-master01 apps]# kubectl get pvc -n default --context="ik8s@kube-dev" No resources found in default namespace. [root@k8s-master01 apps]# kubectl get pvc -n dev --context="ik8s@kube-dev" No resources found in dev namespace. [root@k8s-master01 apps]# kubectl get pv --context="ik8s@kube-dev" No resources found in default namespace.
kubectl也提供了分別創建RoleBinding和ClusterRoleBinding資源的命令式命令:create rolebinding和create clusterrolebinding,它們使用的選項基本相同,常用的選項如下。
▪--role="":綁定的角色,僅RoleBinding支持。 ▪--clusterrole="":綁定的集群角色,RoleBinding和ClusterRoleBinding均支持。 ▪--group=[]:綁定的組,支持逗號分隔的列表格式。 ▪--user=[]:綁定的普通賬戶,支持逗號分隔的列表格式。 ▪--serviceaccount=[]:綁定的服務賬戶,支持逗號分隔的列表格式。
下面的命令為用戶組kubeusers分配了集群角色nodes-admin,從而該組的所有用戶均自動繼承該角色上的所有許可權限。
[root@k8s-master01 apps]#kubectl create clusterrolebinding kubeusers-nodes-admin \ --clusterrole='nodes-admin' --group='kubeusers' clusterrolebinding.rbac.authorization.k8s.io/kubeusers-nodes-admin created
Kubernetes自1.9版本開始支持在ClusterRole的rules字段中嵌套aggregationRule字段來整合其他ClusterRole資源的規則,這種類型的ClusterRole對象的實際可用權限受控於控制器,具體許可授權由所有被標簽選擇器匹配到的ClusterRole的聚合授權規則合並生成。 下面的配置清單中首先定義了兩個擁有標簽的集群角色global-resources-view和global-resources-edit,而后在第三個集群角色資源global-resources-admin上使用聚合規則的標簽選擇器來匹配前兩個資源的標簽,因此,集群角色global-resources-admin的權限將由匹配到的其他ClusterRole資源的規則列表自動聚合而成。
kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: global-resources-view labels: rbac.ilinux.io/aggregate-to-global-admin: "true" rules: - apiGroups: [""] resources: ["nodes", "namespaces", "persistentvolumes", "clusterroles"] verbs: ["get", "list", "watch"] --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: global-resources-edit labels: rbac.ilinux.io/aggregate-to-global-admin: "true" rules: - apiGroups: [""] resources: ["nodes", "namespaces", "persistentvolumes"] verbs: ["create", "delete", "deletecollection", "patch", "update"] --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: global-resources-admin aggregationRule: clusterRoleSelectors: - matchLabels: rbac.ilinux.io/aggregate-to-global-admin: "true" rules: [] # 該規則列表為空,它將由控制器自動聚合生成
事實上,Kubernetes系統上面向用戶的內置ClusterRole admin和edit也是聚合型的ClusterRole對象,因為這可以使得默認角色中包含自定義資源的相關規則,例如由CustomResourceDefinitions或Aggregated API服務器提供的規則等。
4、面向用戶的內置ClusterRole
API Server內置了一組默認的ClusterRole和ClusterRoleBinding資源預留給系統使用,其中大多數都以system:為前綴。另外有一些不以system:為前綴的默認的ClusterRole資源是為面向用戶的需求而設計,包括集群管理員角色cluster-admin,以及專用於授予特定名稱空間級別權限的集群角色admin、edit和view,如下圖所示。掌握這些默認的內置ClusterRole資源有助於按需創建用戶並分配相應權限。
內置的clusterroles/cluster-admin資源擁有管理集群所有資源的權限,而內置的clusterrolebindings/cluster-admn將該角色分配給了system:masters用戶組,這意味着所有隸屬於該組的用戶都將自動具有集群的超級管理權限。kubeadm安裝設置集群時,自動創建的配置文件/etc/kubernetes/admin.conf中定義的用戶kubernetes-admin使用證書文件/etc/kubernetes/pki/apiserver-kubelet-client.crt向API Server進行驗證。而該數字證書的Subject屬性值為/O=system:masters,API Server會在成功驗證該用戶的身份之后將其識別為system: master用戶組的成員。
[root@k8s-master01 apps]# openssl x509 -in /etc/kubernetes/pki/apiserver-kubelet-client.crt -noout -subject subject= /O=system:masters/CN=kube-apiserver-kubelet-client
為Kubernetes集群自定義超級管理員的方法至少有兩種:一是將用戶歸入system:masters組,二是通過ClusterRoleBinding直接將用戶綁定至內置的集群角色cluster-admin上。
[root@k8s-master01 apps]#kubectl create rolebinding ik8s-admin --clusterrole=admin --user=ik8s -n dev
若僅需要授予編輯或只讀權限,在創建RoleBinding時引用ClusterRole的edit或view便能實現。
每次啟動時,API Server都會自動為所有默認的ClusterRole重新賦予缺失的權限,同時為默認的ClusterRoleBinding綁定缺失的主體。這種機制給了集群從意外修改中自動恢復的能力,以及升級版本后自動將ClusterRole和ClusterRoleBinding升級到滿足新版本需求的能力。
說明:
必要時,在默認的ClusterRole或ClusterRoleBinding上設置annnotation中的rbac.authorization.kubernetes.io/autoupdate屬性的值為false,即可禁止這種自動恢復功能。
啟用RBAC后,Kubernetes系統的各核心組件、附加組件,以及由controller-manager運行的核心控制器等,幾乎都要依賴於合理的授權才能正常運行。因而,RBAC權限模型為這些組件內置了可獲得最小化的資源訪問授權的ClusterRole和ClusterRoleBinding,例如system:kube-sheduler、system:kube-controller-manager、system:node、system:node-proxier和system:kube-dns等,其中大多數組件都可以做到見名知義。
1、dashboard的介紹及部署
https://raw.githubusercontent.com/kubernetes/dashboard/v2.2.0/aio/deploy/recommended.yaml下載dashboard清單文件,手動修改service暴露nodeport的端口為30001
# Copyright 2017 The Kubernetes Authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. apiVersion: v1 kind: Namespace metadata: name: kubernetes-dashboard --- apiVersion: v1 kind: ServiceAccount metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard namespace: kubernetes-dashboard --- kind: Service apiVersion: v1 metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard namespace: kubernetes-dashboard spec: type: NodePort ports: - port: 443 targetPort: 8443 nodePort: 30001 selector: k8s-app: kubernetes-dashboard --- apiVersion: v1 kind: Secret metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard-certs namespace: kubernetes-dashboard type: Opaque --- apiVersion: v1 kind: Secret metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard-csrf namespace: kubernetes-dashboard type: Opaque data: csrf: "" --- apiVersion: v1 kind: Secret metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard-key-holder namespace: kubernetes-dashboard type: Opaque --- kind: ConfigMap apiVersion: v1 metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard-settings namespace: kubernetes-dashboard --- kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard namespace: kubernetes-dashboard rules: # Allow Dashboard to get, update and delete Dashboard exclusive secrets. - apiGroups: [""] resources: ["secrets"] resourceNames: ["kubernetes-dashboard-key-holder", "kubernetes-dashboard-certs", "kubernetes-dashboard-csrf"] verbs: ["get", "update", "delete"] # Allow Dashboard to get and update 'kubernetes-dashboard-settings' config map. - apiGroups: [""] resources: ["configmaps"] resourceNames: ["kubernetes-dashboard-settings"] verbs: ["get", "update"] # Allow Dashboard to get metrics. - apiGroups: [""] resources: ["services"] resourceNames: ["heapster", "dashboard-metrics-scraper"] verbs: ["proxy"] - apiGroups: [""] resources: ["services/proxy"] resourceNames: ["heapster", "http:heapster:", "https:heapster:", "dashboard-metrics-scraper", "http:dashboard-metrics-scraper"] verbs: ["get"] --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard rules: # Allow Metrics Scraper to get metrics from the Metrics server - apiGroups: ["metrics.k8s.io"] resources: ["pods", "nodes"] verbs: ["get", "list", "watch"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard namespace: kubernetes-dashboard roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: kubernetes-dashboard subjects: - kind: ServiceAccount name: kubernetes-dashboard namespace: kubernetes-dashboard --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: kubernetes-dashboard roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: kubernetes-dashboard subjects: - kind: ServiceAccount name: kubernetes-dashboard namespace: kubernetes-dashboard --- kind: Deployment apiVersion: apps/v1 metadata: labels: k8s-app: kubernetes-dashboard name: kubernetes-dashboard namespace: kubernetes-dashboard spec: replicas: 1 revisionHistoryLimit: 10 selector: matchLabels: k8s-app: kubernetes-dashboard template: metadata: labels: k8s-app: kubernetes-dashboard spec: containers: - name: kubernetes-dashboard image: kubernetesui/dashboard:v2.2.0 imagePullPolicy: Always ports: - containerPort: 8443 protocol: TCP args: - --auto-generate-certificates - --namespace=kubernetes-dashboard # Uncomment the following line to manually specify Kubernetes API server Host # If not specified, Dashboard will attempt to auto discover the API server and connect # to it. Uncomment only if the default does not work. # - --apiserver-host=http://my-address:port volumeMounts: - name: kubernetes-dashboard-certs mountPath: /certs # Create on-disk volume to store exec logs - mountPath: /tmp name: tmp-volume livenessProbe: httpGet: scheme: HTTPS path: / port: 8443 initialDelaySeconds: 30 timeoutSeconds: 30 securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true runAsUser: 1001 runAsGroup: 2001 volumes: - name: kubernetes-dashboard-certs secret: secretName: kubernetes-dashboard-certs - name: tmp-volume emptyDir: {} serviceAccountName: kubernetes-dashboard nodeSelector: "kubernetes.io/os": linux # Comment the following tolerations if Dashboard must not be deployed on master tolerations: - key: node-role.kubernetes.io/master effect: NoSchedule --- kind: Service apiVersion: v1 metadata: labels: k8s-app: dashboard-metrics-scraper name: dashboard-metrics-scraper namespace: kubernetes-dashboard spec: ports: - port: 8000 targetPort: 8000 selector: k8s-app: dashboard-metrics-scraper --- kind: Deployment apiVersion: apps/v1 metadata: labels: k8s-app: dashboard-metrics-scraper name: dashboard-metrics-scraper namespace: kubernetes-dashboard spec: replicas: 1 revisionHistoryLimit: 10 selector: matchLabels: k8s-app: dashboard-metrics-scraper template: metadata: labels: k8s-app: dashboard-metrics-scraper annotations: seccomp.security.alpha.kubernetes.io/pod: 'runtime/default' spec: containers: - name: dashboard-metrics-scraper image: kubernetesui/metrics-scraper:v1.0.6 ports: - containerPort: 8000 protocol: TCP livenessProbe: httpGet: scheme: HTTP path: / port: 8000 initialDelaySeconds: 30 timeoutSeconds: 30 volumeMounts: - mountPath: /tmp name: tmp-volume securityContext: allowPrivilegeEscalation: false readOnlyRootFilesystem: true runAsUser: 1001 runAsGroup: 2001 serviceAccountName: kubernetes-dashboard nodeSelector: "kubernetes.io/os": linux # Comment the following tolerations if Dashboard must not be deployed on master tolerations: - key: node-role.kubernetes.io/master effect: NoSchedule volumes: - name: tmp-volume emptyDir: {}
[root@k8s-master01 apps]# kubectl apply -f dashboard.yaml namespace/kubernetes-dashboard created serviceaccount/kubernetes-dashboard created service/kubernetes-dashboard created secret/kubernetes-dashboard-certs created secret/kubernetes-dashboard-csrf created secret/kubernetes-dashboard-key-holder created configmap/kubernetes-dashboard-settings created role.rbac.authorization.k8s.io/kubernetes-dashboard created clusterrole.rbac.authorization.k8s.io/kubernetes-dashboard created rolebinding.rbac.authorization.k8s.io/kubernetes-dashboard created clusterrolebinding.rbac.authorization.k8s.io/kubernetes-dashboard created deployment.apps/kubernetes-dashboard created service/dashboard-metrics-scraper created deployment.apps/dashboard-metrics-scraper created [root@k8s-master01 apps]# kubectl get svc -A NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE default kubernetes ClusterIP 172.26.0.1 <none> 443/TCP 2d2h kube-system kube-dns ClusterIP 172.26.0.10 <none> 53/UDP,53/TCP,9153/TCP 2d2h kubernetes-dashboard dashboard-metrics-scraper ClusterIP 172.26.186.123 <none> 8000/TCP 12s kubernetes-dashboard kubernetes-dashboard NodePort 172.26.237.12 <none> 443:30001/TCP 12s
Dashboard的默認登錄頁面,它支持直接通過目標Service Account的令牌加載身份憑據,或者以該令牌為身份憑據生成專用的kubeconfig文件,並通過指定的文件路徑向Dashboard提交認證信息。訪問https://192.168.32.204:30001(https://nodeIP:端口)
2、dashboard的認證與授權
2.1 使用token登錄dashboard
Kubernetes Dashboard自身並不進行任何形式的身份驗證和鑒權,它僅是把用戶提交的身份憑據轉發至后端的API Server完成驗證,資源操作請求及權限檢查同樣會提交至后端的API Server進行。從某種意義上講,Dashboard更像是用戶訪問Kubernetes的代理程序,發送給API Server的身份認證及資源操作請求都是由Dashboard應用程序完成,因而用戶提交的身份憑據需要關聯至某個Service Account。
集群全局的資源管理操作依賴於集群管理員權限,因而需要為專用於訪問Dashboard的Service Account分配內置的cluster-admin集群角色。隨后,將相應Service Account的令牌信息提交給Dashboard並認證到API Server,便可使得Dashboard繼承了該賬戶的所有管理權限。
例如,下面在kubernetes-dashboard名稱空間創建一個名為dashboard-admin的Service Account完成該目標。
[root@k8s-master01 apps]# kubectl create serviceaccount admin-dashboard -n kubernetes-dashboard serviceaccount/admin-dashboard created [root@k8s-master01 apps]# kubectl create clusterrolebinding admin-dashboard --clusterrole=cluster-admin \ --serviceaccount=kubernetes-dashboard:admin-dashboard clusterrolebinding.rbac.authorization.k8s.io/admin-dashboard-clusterrolebinding created
配置清單
--- #配置訪問dashboard的sa apiVersion: v1 kind: ServiceAccount metadata: name: admin-dashboard namespace: kubernetes-dashboard --- #把訪問dashboaed的sa與clusterrole的cluster-admin綁定 apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: admin-dashboard subjects: - kind: ServiceAccount name: admin-dashboard namespace: kubernetes-dashboard apiGroup: "" #sa default "" ; user and group default rbac.authorization.k8s.io roleRef: kind: ClusterRole name: cluster-admin apiGroup: rbac.authorization.k8s.io
獲取到服務賬戶kubernetes-dashboard:admin-dashboard關聯的Secret對象中的令牌信息,提交給Dashboard即可完成認證。下面第一個命令檢索到該服務賬戶的Secret對象名稱並保存到變量中,第二個命令則從該Secret中獲取經過Base64編碼后的令牌信息,並打印出解碼后的令牌信息。
[root@k8s-master01 apps]# kubectl get secrets -n kubernetes-dashboard NAME TYPE DATA AGE admin-dashboard-token-jldx4 kubernetes.io/service-account-token 3 10m default-token-gp58x kubernetes.io/service-account-token 3 23m kubernetes-dashboard-certs Opaque 0 23m kubernetes-dashboard-csrf Opaque 1 23m kubernetes-dashboard-key-holder Opaque 2 23m kubernetes-dashboard-token-w9lgr kubernetes.io/service-account-token 3 23m [root@k8s-master01 apps]# kubectl describe secrets admin-dashboard-token-jldx4 -n kubernetes-dashboard Name: admin-dashboard-token-jldx4 Namespace: kubernetes-dashboard Labels: <none> Annotations: kubernetes.io/service-account.name: admin-dashboard kubernetes.io/service-account.uid: 0fa14043-bae1-400b-b373-437b0b2155aa Type: kubernetes.io/service-account-token Data ==== namespace: 20 bytes token: eyJhbGciOiJSUzI1NiIsImtpZCI6IkQ2cXNJdXJONzB2MmY1UDdadlFQZkxGSjFwUEI2TW0wVnIwdkhZWi1vakkifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlcm5ldGVzLWRhc2hib2FyZCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhZG1pbi1kYXNoYm9hcmQtdG9rZW4tamxkeDQiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiYWRtaW4tZGFzaGJvYXJkIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiMGZhMTQwNDMtYmFlMS00MDBiLWIzNzMtNDM3YjBiMjE1NWFhIiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50Omt1YmVybmV0ZXMtZGFzaGJvYXJkOmFkbWluLWRhc2hib2FyZCJ9.GLRbOy-sWiFhd6fuBxvHRH2Zd3nykDoBDCJAz1IIm98-swb9IdgYCcYv5T67lKPZDYZC28ZXj0XM8vKSg5GipjBmxoD_TQkDgHjDB38z2HNYjOaT_3oB7VnDVGGbSdPx9OvojPgPcmqYN0VStVnXm6jx09qg6MiE3BWhIe9krAE7yRFmBALuY33FDL0FOxXU0g0cWBzLAaJJblk8zIJBl_eggydYgYXNU0FDS6rd1nq4WNXqEX3QB_w6l3VhJiNX1ocvTYPnL07p_dKbjM317JIJKgu3VFL-23685N6rComDAYh-H9__CuS_gmx6jGkTZdAuZqlIxhgARmiFuWnPtg ca.crt: 1066 bytes
將令牌信息復制到Dashboard的登錄界面便可完成認證。
每次訪問Dashboard之前都要先通過如上命令獲取相應的令牌是件相當煩瑣的事情,更簡便的辦法是依該身份憑據創建出一個專用的kubeconfig文件並存儲到客戶端,隨后登錄時在瀏覽器中通過本地路徑加載該kubeconfig文件即可完成認證,更加安全和便捷。
下面僅給出相關步驟,實現為服務賬戶kubernetes-dashboard:admin-user創建相關的配置文件
1)創建admin-user私鑰及證書簽發請求
[root@k8s-master01 ~]#mkdir $HOME/.certs [root@k8s-master01 ~]#(umask 077; openssl genrsa -out $HOME/.certs/admin-user.key 2048) [root@k8s-master01 ~]# openssl req -new -key $HOME/.certs/admin-user.key \ -out $HOME/.certs/admin-user.csr \ -subj "/CN=admin-user/O=dashboard"
2) kubernetes-ca證書簽發admin-user證書
[root@k8s-master01 ~]#openssl x509 -req -days 365 -CA /etc/kubernetes/pki/ca.crt \ -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial \ -in $HOME/.certs/admin-user.csr -out $HOME/.certs/admin-user.crt
3)添加集群配置,包括集群名稱、API Server URL和信任的CA的證書;clusters配置段中的各列表項名稱需要唯一。
[root@k8s-master01 ~]#kubectl config set-cluster kubernetes \ --certificate-authority=/etc/kubernetes/pki/ca.crt \ --embed-certs=true \ --server=https://192.168.32.248:6443 \ --kubeconfig=$HOME/.kube/admin-user.config
4) 設置客戶端認證
[root@k8s-master01 ~]#kubectl config set-credentials admin-user \ --client-key=$HOME/.certs/admin-user.key \ --client-certificate=$HOME/.certs/admin-user.crt \ --embed-certs=true \ --kubeconfig=$HOME/.kube/admin-user.config
5) 以用戶admin-user的身份憑據與Kubernetes集群建立映射關系。配置上下文
[root@k8s-master01 ~]#kubectl config set-context admin-user@kubernetes \ --cluster=kubernetes \ --user=admin-user \ --kubeconfig=$HOME/.kube/admin-user.config
6) 設置當前上下文為admin-user@kubernetes。
[root@k8s-master01 ~]#kubectl config use-context admin-user@kubernetes --kubeconfig=$HOME/.kube/admin-user.config
清單配置
[root@k8s-master01 ~]#vim admin-dashboard.yaml --- #配置訪問dashboard的sa apiVersion: v1 kind: ServiceAccount metadata: name: admin-user namespace: kubernetes-dashboard --- #把訪問dashboaed的sa與clusterrole的cluster-admin綁定 apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: admin-user subjects: - kind: ServiceAccount name: admin-user namespace: kubernetes-dashboard apiGroup: "" roleRef: kind: ClusterRole name: cluster-admin apiGroup: rbac.authorization.k8s.io
[root@k8s-master01 apps]# kubectl apply -f admin-dashboard.yaml serviceaccount/admin-user created clusterrolebinding.rbac.authorization.k8s.io/admin-user created
8) 獲取token名稱及token信息
[root@k8s-master01 apps]# kubectl get secrets -n kubernetes-dashboard|grep admin-user admin-user-token-v52gz kubernetes.io/service-account-token 3 7m9s [root@k8s-master01 apps]# kubectl describe secrets admin-user-token-v52gz -n kubernetes-dashboard Name: admin-user-token-v52gz Namespace: kubernetes-dashboard Labels: <none> Annotations: kubernetes.io/service-account.name: admin-user kubernetes.io/service-account.uid: b2dde30c-b6a7-479a-ba25-8503291ce91e Type: kubernetes.io/service-account-token Data ==== namespace: 20 bytes token: eyJhbGciOiJSUzI1NiIsImtpZCI6IkQ2cXNJdXJONzB2MmY1UDdadlFQZkxGSjFwUEI2TW0wVnIwdkhZWi1vakkifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlcm5ldGVzLWRhc2hib2FyZCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJhZG1pbi11c2VyLXRva2VuLXY1Mmd6Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQubmFtZSI6ImFkbWluLXVzZXIiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC51aWQiOiJiMmRkZTMwYy1iNmE3LTQ3OWEtYmEyNS04NTAzMjkxY2U5MWUiLCJzdWIiOiJzeXN0ZW06c2VydmljZWFjY291bnQ6a3ViZXJuZXRlcy1kYXNoYm9hcmQ6YWRtaW4tdXNlciJ9.B0RPQ9mUue3GLt7xAfYD9nwKidGVEmaITNHVe-9o_IGkMZJHF3P0JFBSc6JagaTtqlzJCYzebqwqqYrI7o3XuQXRJ_LCQyQ9BuWkGOe27Wh95gaEgqR7oWH50iLT-ClWxOQOjPrchHfEeZV4XlCbk8NhphibZECKG7V6ExK4X0pBu0sc8Gb8bgVz-rvM69ipWncJgP7CmNcHh_-U4VYLEtMh1NkgMKOmf0nB61UF7lBLxLav1cKDNSJAowLSwJgKttNyOBS1Z5OQJDCwZbS2lN2A4PapfRisVGsOuxLt-ZePfr-RlhVWPvd02HXZjnw7VGzrc-yC8nAHzChQ3CQ1cw ca.crt: 1066 bytes
9) 將token信息寫入/root/.kube/admin-user.config的最后面
[root@k8s-master01 apps]# vim /root/.kube/admin-user.config contexts: - context: cluster: kubernetes user: admin-user name: admin-user@kubernetes current-context: admin-user@kubernetes kind: Config preferences: {} users: - name: admin-user user: client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUN1akND...... client-key-data: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcFFJQkFB...... token: eyJhbGciOiJSUzI1NiIsImtpZCI6IkQ2cXNJdXJONzB2MmY1UDdadlFQZkxGSjFwUE......
將admin-user.config拷貝到其它電腦上,即可使用token登錄dashboard
說明:
dashboard是使用serviceaccount賬號。
若需要設定的用戶僅具有某個名稱空間的管理權限,或者僅擁有集群或名稱空間級別的資源讀取權限,都能夠通過RBAC權限管理模型來實現。這類用戶的設定過程與前述步驟中的關鍵不同之處僅在於角色分配步驟。例如,在名稱空間kubernetes-dashboard中創建服務賬戶monitor-user,並通過ClusterRoleBinding為其分配默認的集群角色view,便可創建一個僅具有全局讀取權限的Dashboard用戶,所需步驟如下。
[root@k8s-master01 apps]# kubectl create serviceaccount monitor -n kubernetes-dashboard [root@k8s-master01 apps]# kubectl create clusterrolebinding monitor --clusterrole=view \ --serviceaccount=kubernetes-dashboard:monitor
配置清單
[root@k8s-master01 ~]#vim monitor-dashboard.yaml --- #配置訪問dashboard的sa apiVersion: v1 kind: ServiceAccount metadata: name: monitor namespace: kubernetes-dashboard --- #把訪問dashboaed的sa與clusterrole的cluster-admin綁定 apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: monitor subjects: - kind: ServiceAccount name: monitor namespace: kubernetes-dashboard apiGroup: "" roleRef: kind: ClusterRole name: view apiGroup: rbac.authorization.k8s.io
或者在名稱空間dev中創建一個服務賬戶dev-ns-admin,並通過RoleBinding為其分配默認的集群角色admin,就能創建一個僅具有dev名稱空間管理權限的Dashboard用戶,所需要的步驟如下。
[root@k8s-master01 apps]# kubectl create serviceaccount ns-admin -n dev
[root@k8s-master01 apps]# kubectl create rolebinding ns-admin --clusterrole=admin --serviceaccount=dev:ns-admin
配置清單
[root@k8s-master01 ~]#vim monitor-dashboard.yaml --- #配置訪問dashboard的sa apiVersion: v1 kind: ServiceAccount metadata: name: ns-admin namespace: dev --- #把訪問dashboaed的sa與clusterrole的cluster-admin綁定 apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: ns-admin subjects: - kind: ServiceAccount name: ns-adminr namespace: dev apiGroup: "" roleRef: kind: ClusterRole name: admin apiGroup: rbac.authorization.k8s.io
最后找出token並寫入kubeconfig文件的最后即可。
七、准入控制器
1、准入控制器概述
准入控制器的相關代碼必須要由管理員編譯進kube-apiserver中才能使用,實現方式缺乏靈活性。於是,Kubernetes自1.7版本引入了Initializers和External Admission Webhooks來嘗試突破此限制,而且自1.9版本起,External Admission Webhooks又被分為MutatingAdmissionWebhook和ValidatingAdmissionWebhook兩種類型,分別用於在API中執行對象配置的“變異”和“驗證”操作,前一種類型的控制器會“改動”和“驗證”資源規范,而后一種類型僅“驗證”資源規范是否合規。
在具體的代碼實現上,一個准入控制器可以是驗證型、變異型或兼具此兩項功能。例如,LimitRanger准入控制器可以使用默認資源請求和限制(變異階段)來擴展Pod,也能夠校驗有着顯式資源需求定義的Pod是否超出LimitRange對象(驗證階段)的定義。而在具體運行時,准入控制也會根據准入控制器類型分階段運行,第一個階段串行運行各變異型控制器,第二階段則串行運行各驗證型控制器,如圖所示。在此過程中,任何控制器拒絕請求都將導致整個請求被即刻拒絕,並將錯誤信息返回給客戶端。
Kubernetes內置支持的所有准入控制器及其功能說明請參考官方文檔中的說明,具體地址為https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/。
2、LimitRange
盡管用戶可以為容器或Pod資源指定資源需求及資源限制,但這並非強制性要求,那些未明確定義資源限制的容器應用很可能會因程序Bug或真實需求而吞掉本地工作節點上的所有可用計算資源。因此妥當的做法是,使用LimitRange資源在每個名稱空間中限制每個容器的最小及最大計算資源用量,以及為未顯式定義計算資源使用區間的容器設置默認的計算資源需求和計算資源限制。一旦在名稱空間上定義了LimitRange對象,客戶端創建或修改資源對象的操作必將受到LimitRange控制器的“驗證”,任何違反LimitRange對象定義的資源最大用量的請求都將被直接拒絕。 LimitRange支持在Pod級別與容器級別分別設置CPU和內存兩種計算資源的可用范圍,它們對應的資源范圍限制類型分別為Pod和Container。一旦在名稱空間上啟用LimitRange,該名稱空間中的Pod或容器的requests和limits的各項屬性值必須在對應的可用資源范圍內,否則將會被拒絕,這是驗證型准入控制器的功能。以Pod級別的CPU資源為例,若某個LimitRange資源為其設定了[0.5,4]的區間,則相應名稱空間下任何Pod資源的requests.cpu的屬性值必須要大於等於500m,同時,limits.cpu的屬性值也必須要小於等於4。而未顯式指定request和limit屬性的容器,將會從LimitRange資源上分別自動繼承相應的默認設置,這是變異型准入控制器的功能。
LimitRange也支持在PersistentVolumeClaim資源級別設定存儲空間的范圍限制,它用於限制相應名稱空間中創建的PVC對象請求使用的存儲空間不能逾越指定的范圍。未指定requests和limits屬性的PVC規范,將在創建時自動繼承LimitRange上配置的默認值。
[root@k8s-master01 apps]# vim limitrange-demo.yaml apiVersion: v1 kind: LimitRange metadata: name: resource-limits namespace: dev spec: limits: - type: Pod max: cpu: "4" memory: "4Gi" min: cpu: "500m" memory: "16Mi" - type: Container max: cpu: "4" memory: "1Gi" min: cpu: "100m" memory: "4Mi" default: cpu: "2" memory: "512Mi" defaultRequest: cpu: "500m" memory: "64Mi" maxLimitRequestRatio: cpu: "4" - type: PersistentVolumeClaim max: storage: "10Gi" min: storage: "1Gi" default: storage: "5Gi" defaultRequest: storage: "1Gi" maxLimitRequestRatio: storage: "5"
LimitRange資源的詳細描述會以非常直觀、清晰的方式輸出相關的資源限制及默認值的定義,將如上配置清單中的LimitRange資源resource-limits創建到集群上,而后便可使用describe命令查看:
[root@k8s-master01 apps]# kubectl create ns dev namespace/dev created [root@k8s-master01 apps]# kubectl apply -f limitrange-demo.yaml limitrange/resource-limits created [root@k8s-master01 apps]# kubectl get limitrange No resources found in default namespace. [root@k8s-master01 apps]# kubectl get limitrange -n dev NAME CREATED AT resource-limits 2021-05-12T06:17:14Z [root@k8s-master01 apps]# kubectl describe limitrange resource-limits -n dev Name: resource-limits Namespace: dev Type Resource Min Max Default Request Default Limit Max Limit/Request Ratio ---- -------- --- --- --------------- ------------- ----------------------- Pod cpu 500m 4 - - - Pod memory 16Mi 4Gi - - - Container cpu 100m 4 500m 2 4 Container memory 4Mi 1Gi 64Mi 512Mi - PersistentVolumeClaim storage 1Gi 10Gi 1Gi 5Gi 5
通過在dev名稱空間中創建Pod對象與PVC對象對各限制的邊界和默認值的效果進行多維度測試。先創建一個僅包含一個容器且沒有默認系統資源需求和限制的Pod對象:
[root@k8s-master01 apps]# kubectl run testpod-1 --image="ikubernetes/demoapp:v1.0" -n dev pod/testpod-1 created
Pod對象testpod-1資源規范中被自動添加了CPU和內存資源的requests和limits屬性,它的值來自limitranges/resource-limits中的定義,如下面的命令及截取的結果片段所示。
[root@k8s-master01 apps]# kubectl get pod testpod-1 -n dev -o yaml apiVersion: v1 kind: Pod metadata: annotations: kubernetes.io/limit-ranger: 'LimitRanger plugin set: cpu, memory request for container testpod-1; cpu, memory limit for container testpod-1' ...... spec: containers: - image: ikubernetes/demoapp:v1.0 imagePullPolicy: IfNotPresent name: testpod-1 resources: limits: cpu: "2" memory: 512Mi requests: cpu: 500m memory: 64Mi ......
沒有給containers配置資源限制,則使用定義好的默認資源限制mincpu=0.5,minmen=64m;maxcpu=2,maxmem=512m。
若Pod對象設定的CPU或內存的requests屬性值小於LimitRange中相應資源的下限,或limits屬性值大於設定的相應資源的上限,就會觸發LimitRanger准入控制器拒絕相關的請求。例如下面創建Pod的命令中,僅requests.memory一個屬性值違反了limitrange/resource-limits中的定義,但請求同樣會被拒絕。
[root@k8s-master01 apps]# kubectl run testpod-2 --image="ikubernetes/demoapp:v1.0" -n dev \ > --limits='cpu=2,memory=1Gi' --requests='cpu=1,memory=8Mi' Error from server (Forbidden): pods "testpod-2" is forbidden: minimum memory usage per Pod is 16Mi, but request is 8388608 #配置pod的內存最少為8m,我們設置的pod資源位16m~4G,小於最下值,pod創建失敗。
在dev名稱空間中創建的PVC對象的可用存儲空間也將受到LimitRange資源中定義的限制。
3、ResourceQuota
盡管LimitRange資源能在名稱空間上限制單個容器、Pod或PVC相關的系統資源用量,但用戶依然可以創建出無數的資源對象,進而侵占集群上所有的可用系統資源。ResourceQuota資源能夠定義名稱空間級別的資源配額,從而在名稱空間上限制聚合資源消耗的邊界,它支持以資源類型來限制用戶可在本地名稱空間中創建的相關資源的對象數量,以及這些對象可消耗的計算資源總量等。 而同名的ResourceQuota准入控制器負責觀察傳入的請求,並確保它沒有違反相應名稱空間中ResourceQuota資源定義的任何約束。ResourceQuota准入控制器屬於“驗證”類型的控制器,用戶創建或更新資源的操作違反配額約束時將會被拒絕,API Server會響應以HTTP狀態代碼403 FORBIDDEN,並顯示一條消息以提示違反的約束條件。 ResourceQuota資源可限制名稱空間中處於非終止狀態的所有Pod對象的計算資源需求及計算資源限制總量。
▪cpu或requests.cpu:CPU資源相關請求的總量限額。
▪memory或requests.cpu:內存資源相關請求的總量限額。
▪limits.cpu:CPU資源相關限制的總量限額。
▪limits.memory:內存資源相關限制的總量限額。
ResourceQuota資源還支持為本地名稱空間中的PVC存儲資源的需求總量和限制總量設置限額,它能夠分別從名稱空間中的全部PVC、隸屬於特定存儲類的PVC以及基於本地臨時存儲的PVC分別進行定義。
▪requests.storage:所有PVC存儲需求的總量限額。 ▪persistentvolumeclaims:可以創建的PVC總數限額。 ▪<storage-class-name>.storageclass.storage.k8s.io/requests.storage:特定存儲類上可使用的所有PVC存儲需求的總量限額。 ▪<storage-class-name>.storageclass.storage.k8s.io/persistentvolumeclaims:特定存儲類上可使用的PVC總數限額。 ▪requests.ephemeral-storage:所有Pod可以使用的本地臨時存儲資源的requets總量。 ▪limits.ephemeral-storage:所有Pod可用的本地臨時存儲資源的limits總量。
下面的資源清單(resourcequota-demo.yaml)在dev名稱空間中定義了一個ResourceQuota資源對象,它定義了計算資源與存儲資源分別在requests和limits維度的限額,也定義了部署資源類型中的可用對象數量。
[root@k8s-master01 apps]# vim resourcequota-demo.yaml apiVersion: v1 kind: ResourceQuota metadata: name: resourcequota-demo namespace: dev spec: hard: pods: "5" count/services: "5" count/configmaps: "5" count/secrets: "5" count/cronjobs.batch: "2" requests.cpu: "2" requests.memory: "4Gi" limits.cpu: "4" limits.memory: "8Gi" count/deployments.apps: "2" count/statefulsets.apps: "2" persistentvolumeclaims: "6" requests.storage: "20Gi" fast-rbd.storageclass.storage.k8s.io/requests.storage: "20Gi" fast-rbd.storageclass.storage.k8s.io/persistentvolumeclaims: "6"
與LimitRange不同的是,ResourceQuota會計入指定范圍內,先前的資源對象對系統資源和資源對象的限額占用情況,因此將resourceqouta-demo創建到集群上之后,dev名稱空間中現有的資源會立即分去限額內的一部分可用空間,這在ResourceQuota資源的詳細描述中會有直觀展示。
[root@k8s-master01 apps]# kubectl apply -f resourcequota-demo.yaml resourcequota/resourcequota-demo created [root@k8s-master01 apps]# kubectl describe resourcequotas/resourcequota-demo -n dev Name: resourcequota-demo Namespace: dev Resource Used Hard -------- ---- ---- count/configmaps 1 5 count/cronjobs.batch 0 2 count/deployments.apps 0 2 count/secrets 1 5 count/services 0 5 count/statefulsets.apps 0 2 fast-rbd.storageclass.storage.k8s.io/persistentvolumeclaims 0 6 fast-rbd.storageclass.storage.k8s.io/requests.storage 0 20Gi limits.cpu 2 4 limits.memory 512Mi 8Gi persistentvolumeclaims 0 6 pods 1 5 requests.cpu 500m 2 requests.memory 64Mi 4Gi requests.storage 0 20Gi
dev名稱空間下的Pod資源限額已被先前的自主式Pod對象消耗了1/5,與此同時,計算資源請求和限制也各占用了一部分配額。隨后,在dev名稱空間中創建Pod資源時,requests.cpu、requests.memroy、limits.cpu、limits.memory和pods等任何一個限額的超出都將致使創建操作失敗,如下面的命令及結果所示。
[root@k8s-master01 apps]# kubectl run testpod-2 --image="ikubernetes/demoapp:v1.0" \ > --requests="cpu=2,memory=1Gi" --limits="cpu=2,memory=1Gi" -n dev Error from server (Forbidden): pods "testpod-2" is forbidden: exceeded quota: resourcequota-demo, requested: requests.cpu=2, used: requests.cpu=500m, limited: requests.cpu=2 #cpu數超過最大的限制數量
每個ResourceQuota資源對象上還支持定義一組作用域,用於定義資源上的配額僅生效於這組作用域交集范圍內的對象,目前適用范圍包括Terminating、NotTerminating、BestEffort和NotBestEffort。
▪Terminating:匹配.spec.activeDeadlineSeconds的屬性值大於等於0的所有Pod對象。
▪NotTerminating:匹配.spec.activeDeadlineSeconds的屬性值為空的所有Pod對象。
▪BestEffort:匹配所有位於BestEffort QoS類別的Pod對象。
▪NotBestEffort:匹配所有非BestEffort QoS類別的Pod對象。
4、PodSecurityPolicy
Pod和容器規范中允許用戶使用securityContext字段定義安全相關的配置,但允許任何用戶隨意以特權模式運行容器或者使用任意的Linux內核能力等,顯然存在着難以預料的安全風險。API Server提供了PodSecurityPolicy資源讓管理員在集群全局定義或限定用戶在Pod和容器上可用及禁用的安全配置,例如是否可使用特權容器和主機名稱空間,可使用的主機網絡端口范圍、卷類型和Linux Capabilities等。因此,本質上來說,PodSecurityPolicy資源就是集群全局范圍內定義的Pod資源可用的安全上下文策略。同名的PodSecurityPolicy准入控制器負責觀察集群范圍內的Pod資源的運行屬性,並確保它沒有違反PodSecurityPolicy資源定義的約束條件。
PSP准入控制器會根據顯式定義的PSP資源中的安全策略判定允許何種Pod資源的創建操作,若無任何可用的安全策略,它將阻止創建任何Pod資源。新部署的Kubernetes集群默認並不會自動生成任何PSP資源,因而該准入控制器默認處於禁用狀態。PSP資源的API接口(policy/v1beta1/podsecuritypolicy)獨立於PSP准入控制器,因此管理員可以先定義好必要的Pod安全策略,再設置kube-apiserver啟用PSP准入控制器。不當的Pod安全策略可能會產生難以預料的副作用,因此請確保添加的任何PSP對象都經過了充分測試。
PodSecurityPolicy是標准的API資源類型,它隸屬於policy群組,在spec字段中嵌套多種安全規則來定義期望的目標,資源規范及簡要的使用說明如下所示。
apiVersion: policy/v1beta1 # PSP資源所屬的API群組及版本 kind: PodSecurityPolicy # 資源類型標識 metadata: name <string> # 資源名稱 spec: allowPrivilegeEscalation <boolean> # 是否允許權限升級 allowedCSIDrivers <[]Object> #內聯CSI驅動程序列表,必須在Pod規范中顯式定義 allowedCapabilities <[]string> # 允許使用的內核能力列表,“*”表示all allowedFlexVolumes <[]Object> # 允許使用的Flexvolume列表,空值表示all allowedHostPaths <[]Object> # 允許使用的主機路徑列表,空值表示all allowedProcMountTypes <[]string> # 允許使用的ProcMountType列表,空值表示默認 allowedUnsafeSysctls <[]string> # 允許使用的非安全sysctl參數,空值表示不允許 defaultAddCapabilities <[]string> # 默認添加到Pod對象的內核能力,可被drop defaultAllowPrivilegeEscalation <boolean> # 是否默認允許內核權限升級 forbiddenSysctls <[]string> # 禁止使用的sysctl參數,空表示不禁用 fsGroup <Object> # 允許在SecurityContext中使用的fsgroup,必選字段 rule <string> # 允許使用的FSGroup規則,支持RunAsAny和MustRunAs ranges <[]Object> # 允許使用的組ID范圍,需要與MustRunAs規則一同使用 max <integer> # 最大組ID號 min <integer> # 最小組ID號 hostIPC <boolean> # 是否允許Pod使用hostIPC hostNetwork <boolean> # 是否允許Pod使用hostNetwork hostPID <boolean> # 是否允許Pod使用hostPID hostPorts <[]Object> # 允許Pod使用的主機端口暴露其服務的范圍 max <integer> # 最大端口號,必選字段 min <integer> # 最小端口號,必選字段 privileged <boolean> # 是否允許運行特權Pod readOnlyRootFilesystem <boolean> # 是否設定容器的根文件系統為“只讀” requiredDropCapabilities <[]string> # 必須要禁用的內核能力列表 runAsGroup <Object> # 允許Pod在runAsGroup中使用的值列表,未定義表示不限制 runAsUser <Object> # 允許Pod在runAsUser中使用的值列表,必選字段 rule <string> # 支持RunAsAny、MustRunAs和MustRunAsNonRoot ranges <[]Object> # 允許使用的組ID范圍,需要跟MustRunAs規則一同使用 max <integer> # 最大組ID號 min <integer> # 最小組ID號 runtimeClass <Object> # 允許Pod使用的運行類,未定義表示不限制 allowedRuntimeClassNames <[]string> # 可使用的runtimeClass列表,“*”表示all defaultRuntimeClassName <string> # 默認使用的runtimeClass seLinux <Object> # 允許Pod使用的selinux標簽,必選字段 rule <string> # MustRunAs表示使用seLinuxOptions定義的值;RunAsAny表示可使用任意值 seLinuxOptions <Object> # 自定義seLinux選項對象,與MustRunAs協作生效 supplementalGroups <Object> # 允許Pod在SecurityContext中使用附加組,必選字段 volumes <[]string> # 允許Pod使用的存儲卷插件列表,空表示禁用,“*”表示all
然而,即便在啟用了PSP准入控制器的情況下,PSP對象依然不會生效,管理員還需要借助授權插件(例如RBAC)將use權限授權給特定的Role或ClusterRole,再為相關的User Account或Service Account分配這些角色才能讓PSP策略真正生效。下面簡單說明為Kubernetes集群設定的能支撐集群自身運行的框架性的Pod安全策略,以及允許非管理員使用的Pod安全策略,而后啟用PSP准入控制器中使這些策略生效的方法。
4.1 設置特權及受限的PSP對象
通常,system:masters組內的管理員賬戶、system:node組內的kubelet賬戶,以及kube-system名稱空間中的所有服務賬戶需要擁有創建各類Pod對象的權限,包括創建特權Pod對象。因此,啟用PSP准入控制器之前需要先創建一個特權PSP資源,並將該資源的使用權賦予各類管理員賬戶以確保Kubernetes集群的基礎服務可以正常運行。一個示例性的特權PSP資源清單(psp-privileged.yaml)如下,它啟用了幾乎所有的安全配置。
[root@k8s-master01 apps]# vim psp-privileged.yaml apiVersion: policy/v1beta1 kind: PodSecurityPolicy metadata: name: privileged annotations: seccomp.security.alpha.kubernetes.io/allowedProfileNames: '*' spec: privileged: true allowPrivilegeEscalation: true allowedCapabilities: ['*'] allowedUnsafeSysctls: ['*'] volumes: ['*'] hostNetwork: true hostPorts: - min: 0 max: 65535 hostIPC: true hostPID: true runAsUser: rule: 'RunAsAny' runAsGroup: rule: 'RunAsAny' seLinux: rule: 'RunAsAny' supplementalGroups: rule: 'RunAsAny' fsGroup: rule: 'RunAsAny'
出於安全加強的需要,除了有特權需求的系統級應用程序及集群管理員賬戶之外,其他應用或普通賬戶默認不應該允許使用與安全上下文相關的任何配置。因而,系統內置的特殊組之外的其他普通賬戶或服務賬戶,絕大多數都不必使用安全配置,它們僅可使用受限的安全策略。下面的資源清單(psp-restrict.yaml)定義了一個完全受限的安全策略,它禁止了幾乎所有的特權操作。
[root@k8s-master01 apps]#vim psp-restrict.yaml apiVersion: policy/v1beta1 kind: PodSecurityPolicy metadata: name: restricted annotations: seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'docker/default' apparmor.security.beta.kubernetes.io/allowedProfileNames: 'runtime/default' seccomp.security.alpha.kubernetes.io/defaultProfileName: 'docker/default' apparmor.security.beta.kubernetes.io/defaultProfileName: 'runtime/default' spec: privileged: false allowPrivilegeEscalation: false allowedUnsafeSysctls: [] requiredDropCapabilities: - ALL # 允許使用的核心存儲卷類型 volumes: ['configMap', 'emptyDir', 'projected', 'secret', 'secret', 'persistentVolumeClaim'] hostNetwork: false hostIPC: false hostPID: false runAsUser: rule: 'MustRunAsNonRoot' seLinux: rule: 'RunAsAny' supplementalGroups: rule: 'MustRunAs' ranges: # Forbid adding the root group. - min: 1 max: 65535 fsGroup: rule: 'MustRunAs' ranges: # Forbid adding the root group. - min: 1 max: 65535 readOnlyRootFilesystem: false
將上面兩個資源清單中定義的PSP資源提交並創建到集群之上,隨后便可授權特定的Role或ClusterRole資源通過use調用它們。PSP資源創建完成后才能授權特定的Role或ClusterRole資源通過use進行調用。我們這里首先使用如下命令將上面配置清單中定義的資源創建到集群之上。
[root@k8s-master01 apps]# kubectl apply -f psp-privileged.yaml -f psp-restrict.yaml podsecuritypolicy.policy/privileged created podsecuritypolicy.policy/restricted created
啟用PodSecurityPolicy准入控制器后,僅被授權使用PSP資源的賬戶才能夠在該資源中定義的策略框架下行使賬戶權限范圍內的資源管理操作。因此,這里還需要顯式授予system:masters、system:nodes和system:serviceaccounts:kube-system組內的用戶可以使用podsecuritypolicy/privileged資源,其他成功認證后的用戶能夠使用podsecuritypolicy/restricted資源。RBAC權限模型中,任何Subject都不能直接獲得權限,它們需要借助分配到的角色獲得權限。因此,下面先創建兩個分別能使用podsecuritypolicy/privileged和podsecuritypolicy/restricted資源的ClusterRole。
下面的資源清單(clusterrole-with-psp.yaml)中創建了兩個ClusterRole資源,授權psp-privileged可以使用名為privileged的安全策略,psp-restricted可以使用名為restricted的安全策略。
[root@k8s-master01 apps]# clusterrole-with-psp.yaml kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: psp-restricted rules: - apiGroups: ['policy'] resources: ['podsecuritypolicies'] verbs: ['use'] resourceNames: - restricted --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: psp-privileged rules: - apiGroups: ['policy'] resources: ['podsecuritypolicies'] verbs: ['use'] resourceNames: - privileged
下面的資源清單(clusterrolebinding-with-psp.yaml)定義了兩個ClusterRoleBinding對象:前一個為system:masters、system:node和system:serviceaccounts:kube-system組的賬戶分配集群角色psp-privileged,從而能夠使用任何安全配置;后一個為system: authenticated組內的賬戶分配集群角色psp-restricted,以禁止它們在Pod和容器上使用任何安全配置。
[root@k8s-master01 apps]#vim clusterrolebinding-with-psp.yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: privileged-psp-user roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: psp-privileged subjects: - apiGroup: rbac.authorization.k8s.io kind: Group name: system:masters - apiGroup: rbac.authorization.k8s.io kind: Group name: system:node - apiGroup: rbac.authorization.k8s.io kind: Group name: system:serviceaccounts:kube-system --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: restricted-psp-user roleRef: kind: ClusterRole name: psp-restricted apiGroup: rbac.authorization.k8s.io subjects: - kind: Group apiGroup: rbac.authorization.k8s.io name: system:authenticated
將上面兩個資源清單中定義的ClusterRole和ClusterRoleBinding資源創建到集群上,即可為API Server啟用PodSecurityPolicy准入控制器。
[root@k8s-master01 apps]# kubectl apply -f clusterrole-with-psp.yaml -f clusterrolebinding-with-psp.yaml clusterrole.rbac.authorization.k8s.io/psp-restricted created clusterrole.rbac.authorization.k8s.io/psp-privileged created clusterrolebinding.rbac.authorization.k8s.io/privileged-psp-user created clusterrolebinding.rbac.authorization.k8s.io/restricted-psp-user created
API Server的應用程kube-apiserver使用--enable-admission-plugins選項顯式指定要加載的准入控制器列表,因此在該選項的列表中添加PodSecurityPolicy條目,並重啟kube-apiserver程序便能啟用PSP准入控制器。對於使用kubeadm部署的Kubernetes集群來說,編輯Master節點上的/etc/kubernetes/manifests/kube-apiserver.yaml配置清單,直接修改--enable-admission-plugins選項的值,並添加PodSecurityPolicy列表項即可,各列表項以逗號分隔。kubelet監控到/etc/kubernetes/manifests目錄下的任何資源清單的改變時都會自動重建相關的Pod對象,因此編輯並保存kube-apiserver.yaml資源清單后,kubelet會通過重建相關的靜態Pod而自動生效。
待kube-apiserver重啟完成后,可通過監測API Server程序的運行狀態及相關日志來判定PodSecurityPolicy准入控制器是否成功啟用。以靜態Pod運行kube-apiserver的日志同樣可使用kubectl logs命令獲取。如下面的命令及截取的結果所示,PodSecurityPolicy准入控制器已然成功加載。若Kubernetes的各系統類Pod資源運行狀態正常,即表示安全策略已然成功啟用。
[root@k8s-master01 apps]# kubectl logs kube-apiserver-k8s-master01.ilinux.io -n kube-system …… plugins.go:158] Loaded 13 mutating admission controller(s) successfully in the following order: NamespaceLifecycle,LimitRanger,…,PodSecurityPolicy,… plugins.go:161] Loaded 11 validating admission controller(s) successfully in the following order: LimitRanger,ServiceAccount,PodSecurityPolicy,… …… 注意: 盡管PSP對已處於運行狀態的Pod或容器沒有影響,但對於正常運行中的Kubernetes集群來說,中途啟用PodSecurityPolicy仍然可能會導致諸多難以預料的錯誤,尤其是沒有事先為用到安全配置的Pod資源准備好可用的PSP資源時,這些Pod資源一旦重啟便會因觸發PSP策略而被阻止。
接下來,我們可通過能成功認證的普通賬戶測試其創建Pod資源時是否受限於restricted安全策略,以驗證PodSecurityPolicy資源的生效狀態。下面的命令嘗試以dev名稱空間的管理員mason用戶創建一個使用了主機端口(hostPort)的Pod資源,但該操作被PodSecurityPolicy拒絕。
[root@k8s-master01 apps]# kubectl run pod-with-hostport --image="ikubernetes/demoapp:v1.0" \ --port=80 --hostport=32080 -n dev --context='mason@kubernetes' Error from server (Forbidden): pods "pod-with-hostport" is forbidden: unable to validate against any pod security policy: [spec.containers[0].hostPort: Invalid value: 32080: Host port 32080 is not allowed to be used. Allowed ports: []]