1. 程式人生 > 其它 >十五、Kubernetes之安全配置

十五、Kubernetes之安全配置

本章實驗為kubeadm安裝k8s叢集

在任何將資源或服務提供給有限使用者的系統上,認證和授權是兩個必不可少的功能,前者用於身份鑑別,負責驗證“來者是誰”,而後者則實現許可權分派,負責鑑證“他有權做哪些事”。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物件是否違反系統資源限制等,而其中任何的檢查失敗都可能會導致寫入操作失敗。

Kubernetes對整個系統的認證,授權,訪問控制做了精密的設定;對於Kubernetes叢集來說,apiserver是整個就叢集訪問控制的唯一入口,在Kubernetes叢集之上部署應用程式的時候,也可以通過宿主機的NodePort暴露的埠訪問裡面的程式,使用者訪問kubernetes叢集需要經歷如下認證過程:認證->授權->准入控制(adminationcontroller)

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物件。

對API Server來說,來自客戶端的請求要麼與使用者賬戶繫結,要麼以某個服務賬戶的身份進行,要麼被視為匿名請求。這意味著群集內部或外部的每個程序,包括由人類使用者使用kubectl,以及各節點上執行的kubelet程序,再到控制平面的成員元件,必須在向API Server發出請求時進行身份驗證,否則即被視為匿名使用者。

3、認證、授權與准入控制基礎知識

Kubernetes使用身份驗證外掛對API請求進行身份驗證,它允許管理員自定義服務賬戶和使用者賬戶要啟用或禁用的外掛,並支援各自同時啟用多種認證機制。具體設定時,至少應該為服務賬戶和使用者賬戶各自啟用一個認證外掛。 如果啟用了多種認證機制,賬號認證過程由認證外掛以序列方式進行,直到其中一種認證機制成功完成即結束。若認證失敗,伺服器則響應以401狀態碼,反之,請求者就會被Kubernetes識別為某個具體的使用者(以其使用者名稱進行標識),並且該連線上隨後的操作都會以此使用者身份進行。API Server對於接收到的每個訪問請求會呼叫認證外掛,嘗試將以下屬性與訪問請求相關聯。

▪Username:使用者名稱,例如kubernetes-admin等。
▪UID:使用者的數字標籤符,用於確保使用者身份的唯一性。
▪Groups:使用者所屬的組,用於許可權指派和繼承。
▪Extra:鍵值資料型別的字串,用於提供認證需要用到的額外資訊。

3.1 Kubernetes的認證方式

Kubernetes支援的認證方式包括X.509數字證書、承載令牌(bearer token,也稱為不記名令牌)、身份驗證代理(authenticating proxy)和HTTP Basic認證等。其中所有的令牌認證機制通常被統稱為“承載令牌認證”。

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的授權

為了核驗使用者的操作許可,成功通過身份認證後的操作請求還需要轉交給授權外掛進行許可許可權檢查,以確保其擁有執行相應操作的許可。API Server主要支援使用4類內建的授權外掛來定義使用者的操作許可權。

▪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](admission controller)則用於在客戶端請求經過身份驗證和授權檢查之後,將物件持久化儲存到etcd之前攔截請求,從而實現在資源的建立、更新和刪除操作期間強制執行物件的語義驗證等功能,而讀取資源資訊的操作請求則不會經由准入控制器檢查。API Server內建了許多准入控制器,常用的包含下面列出的幾種。不過,其中的個別控制器僅在較新版本的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,每個呼叫都可能會更改物件。

檢查期間,僅那些順利通過所有準入控制器檢查的資源操作請求的結果才能儲存到etcd中,而任何一個准入控制器的拒絕都將導致寫入請求失敗。

二、ServiceAccount及認證

Kubernetes原生的應用程式意味著專為運行於Kubernetes系統之上而開發的應用程式,這些程式託管執行在Kubernetes之上,能夠直接與API Server進行互動,並進行資源狀態的查詢或更新,例如Flannel和CoreDNS等。API Server同樣需要對這類來自Pod資源中的客戶端程式進行身份驗證,服務賬戶也是專用於這類場景的賬號。ServiceAccount資源一般由使用者身份資訊及儲存了認證資訊的Secret物件組成。

1、ServiceAccount自動化

建立的每個Pod資源都自動關聯了一個Secret儲存卷,並由其容器掛載至/var/run/secrets/kubernetes.io/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的引用。

需要注意的是,為確保完整性等,必須為kube-controller-manager使用--service-account-private-key-file選項指定一個私鑰檔案,用於對生成的ServiceAccount令牌進行簽名,該私鑰檔案必須是PEM格式。同時,要使用--service-account-key-file為kube-apiserver指定與前面的私鑰配對的公鑰檔案,實現在認證期間對認證令牌進行校驗。

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資源清單

以資源規範形式建立Secret物件時,以類似如上命令結果的形式,為Secret物件使用資源註解kubernetes.io/service-account.name引用一個現存的ServiceAccount物件,並指定資源型別為特定的kubernetes.io/service-account-token,我們便可以將指定的ServiceAccount物件引用的Secret物件予以置換,該Secret物件同樣會自動生成固定的3個數據項。

完善地建立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例項

僅指定了資源名稱,以及允許Pod物件將其自動掛載為儲存卷,引用的Secret物件則交由系統自動生成。

[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

其中,local-harbor-secret是docker-registry型別的Secret物件,包含目標Docker Registry的服務入口、使用者名稱、密碼及使用者的電子郵件等資訊,它必須要由使用者提前手動建立。該Pod資源所在節點上的kubelet程序使用Secret物件中的令牌認證到目標Docker Registry,以下載執行容器所需要的映象檔案。

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進行驗證,許可權校驗則由授權外掛完成。

可在pods/pod-with-sa的互動式介面中進行訪問測試。

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

接下來,單獨向serviceaccount/namespace-admin授予default名稱空間的管理許可權,pods/pod-with-sa中的程序便能借助該ServiceAccount的身份管理相應名稱空間下的資源.

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下的資源

default名稱空間引用了serviceaccounts/default資源中Pod的容器程序卻不具有如上許可權,因為它們並未獲得相應的授權。事實上,kube-system名稱空間中的許多應用都使用了專用的ServiceAccount資源,例如Flannel、CoreDNS、kube-proxy以及多種控制器等

三、 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叢集中存在3個需要獨立完成X509數字證書認證和HTTPS通訊的體系:一是etcd叢集成員、伺服器及其客戶端;二是API Server及其客戶端,以及kubelet API及其客戶端;三是Kubernetes認證代理體系中的伺服器和客戶端。這3個獨立的體系各自需要一個獨立證書頒發機構為體系內的伺服器和客戶端頒發證書,完成體系內的元件身份認證同時又彼此隔離。

kubernetes叢集中的CA:

(1)etcd叢集CA及相關的數字證書 Kubernetes的API Server將叢集的狀態資料儲存到叢集儲存服務etcd中,包括含有敏感資料的Secret資源物件。出於提升服務可用性、資料冗餘及安全性等目的,生產環境通常應該配置有3、5或7個節點的etcd叢集,叢集內各節點間基於HTTPS協議進行通訊,它們使用Peer型別的數字證書進行通訊時的身份認證。而且,各etcd節點提供Server型別的數字證書與客戶端建立安全連線,並驗證其客戶端Client型別的數字證書。Kubernetes叢集各元件中,kube-apiserver是唯一一個可直接與叢集儲存通訊的元件,它是etcd服務的客戶端。

(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>

proxy-client-cert-file和proxy-client-key-file包含kube-aggregator執行客戶端證書身份驗證的證書/金鑰對,它使用requestheader-client-ca-file中指定的CA檔案對聚合器證書進行簽名。requestheader-allowed-names包含允許充當偽裝前端代理的身份/名稱列表(客戶端證書中使用的CN),而requestheader-username-headers、requestheader-group-headers和requestheader-extraheaders-prefix攜帶一個HTTP頭的列表,用於攜帶遠端使用者資訊。

1.2 Kubernetes叢集需要的數字證書

完整執行的Kubernetes系統需要為etcd、API Server及前端代理(front proxy)生成多個數字證書

另外,其他叢集上執行的應用(Pod)同其客戶端的通訊經由不可信的網路傳輸時也可能需要用到TLS/SSL協議,例如Nginx Pod與其客戶端間的通訊,客戶端來自於網際網路時,此處通常需配置一個公信的服務端證書。

普通使用者使用這種認證方式的前提是,它們各自擁有自己的數字證書,證書中的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的任意憑據或身份,因而叢集管理員必須自行集中管理證書,管理員的工作量比較大。

對於大型組織來說,Kubernetes系統使用者量大且變動頻繁,靜態密碼檔案和靜態令牌檔案認證方式動輒需要重啟API Server,而X509認證中的證書維護開銷較高且無法靈活變動憑據生效期限,因此這些認證方式都非理想選擇。實踐中,人們通常使用ID Token進行Kubernetes的普通使用者身份認證,API Server的OpenID Connect令牌認證外掛即用於該場景。

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命令成功後即完成。

Kubelet會把簽署後的證書及配對的私鑰儲存到--cert-dir選項指定的目錄下,並以之生成kubeconfig格式的配置檔案,該檔案的儲存路徑以--kubeconfig選項指定,它儲存有API Server的地址以及認證憑據。若指定的kubeocnfig配置檔案不存在,kubelet會轉而使用Bootstrap Token,從API Server自動請求完成TLS Bootstrapping過程。 kube-controller-manager內部有一個用於證書頒發的控制迴圈,它採用了類似於cfssl簽證器格式的自動簽證器,頒發的所有證書預設具有一年有效期限。正常使用中的Kubernetes叢集需要在證書過期之前完成更新,以免叢集服務不可用。較新版本的kubeadm部署工具已經能夠自動完成更新,如下第一條命令用於檢測證書有效期限,在接近過期時間的情況下,即可使用第二條命令進行更新。

~# kubeadm alpha certs check-expiration
~# kubeadm alpha certs renew all

Kubernetes 1.8之後的版本中使用的csrapproving審批控制器內置於kube-controller-manager,並且預設為啟用狀態。此審批控制器使用SubjectAccessview API確認給定的使用者是否有許可權請求CSR(證書籤署請求),而後根據授權結果判定是否予以簽署。不過,為了避免同其他審批器衝突,內建的審批器並不顯式拒絕CSR,而只是忽略它們。

四、kubeconfig配置檔案

基於無狀態協議HTTP/HTTPS的API Server需要驗證每次連線請求中的使用者身份,因而kube-controller-manager、kube-scheduler和kube-proxy等各類客戶端元件必須能自動完成身份認證資訊的提交,但通過程式選項來提供這些資訊會導致敏感資訊洩露。另外,管理員還面臨著使用kubectl工具分別接入不同叢集時的認證及認證資訊對映難題。為此,Kubernetes設計了一種稱為kubeconfig的配置檔案,它儲存有接入一到多個Kubernetes叢集的相關配置資訊,並允許管理員按需在各配置間靈活切換。

客戶端程式可通過預設路徑、--kubeconfig選項或者KUBECONFIG環境變數自定義要載入的kubeconfig檔案,從而能夠在每次的訪問請求中可認證到目標API Server。

1、kubeconfig檔案格式

kubeconfig檔案中,各叢集的接入端點以列表形式定義在clusters配置段中,每個列表項代表一個Kubernetes叢集,並擁有名稱標識;各身份認證資訊(credentials)定義在users配置段中,每個列表項代表一個能夠認證到某Kubernetes叢集的憑據。將身份憑據與叢集分開定義以便複用,具體使用時還要以context(上下文)在二者之間按需建立對映關係,各context以列表形式定義在contexts配置段中,而當前使用的對映關係則定義在current-context配置段中。

使用kubeadm初始化Kubernetes叢集過程中,在Master節點上生成的/etc/kubernetes/admin.conf檔案就是一個kubeconfig格式的檔案,它由kubeadm init命令自動生成,可由kubectl載入後接入當前叢集的API Server。kubectl載入kubeconfig檔案的預設路徑為$HOME/.kube/config,在kubeadm init命令初始化叢集過程中有一個步驟便是將/etc/kubernetes/admin.conf複製為該預設搜尋路徑上的檔案。當然,我們也可以通過--kubeconfig選項或KUBECONFIG環境變數將其修改為其他路徑。

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

使用者也可以在kubeconfig配置檔案中按需自定義相關的配置資訊,以實現使用不同的使用者賬戶接入叢集等功能。kubeconfig是一個文字檔案,儘管可以使用文字處理工具直接編輯它,但強烈建議使用者使用kubectl config及其子命令進行該檔案的設定,以便利用其他自動進行語法檢測等額外功能。

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中定義的上下文列表。

kubectl config命令的相關操作將針對載入的單個kubeconfig檔案進行,它根據其優先順序由高到低,依次搜尋--kubeconfig選項指定的檔案、KUBECONFIG環境變數指定的檔案和預設的.HOME/.kube/config檔案,以其中任何一種方式載入到配置檔案後即可終止搜尋過程。不過,kubectl config命令支援同時使用多個kubeconfig檔案,以及將多個配置檔案合併為一個。

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

上面的第6步確認了自定義配置中的ilinux使用者有效可用,它被API Server藉助靜態密碼檔案認證外掛完成認證並標識為ilinux使用者,從而擁有該使用者的資源操作許可權。為了進一步測試並瞭解kubeconfig的使用格式,下面把基於令牌檔案認證的ik8s使用者新增進同一個kubeconfig檔案中。ik8s使用者同ilinux使用者位於同一叢集上,因此,我們可省略新增叢集的步驟而直接複用它。

步驟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

實踐中,API Server支援的X509數字證書認證和OpenID Connect令牌認證才是客戶端使用最多的認證方式.

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"

步驟2:以kubernetes-ca的身份簽署ikubernetes的證書請求,這裡直接讀取相關的CSR檔案,並將簽署後的證書仍然儲存在當前系統使用者主目錄下的.certs中。

① 基於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

步驟3:以ikubernetes的身份憑據生成kubeconfig配置,將其儲存在kubectl預設搜尋路徑指向的$HOME/.kube/config檔案中。另外,因指向當前叢集的配置項已經存在,即位於clusters配置段中的kubernetes,這裡直接複用該叢集定義。

① 根據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檔案中。還沒有給該使用者授權。

3、多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、為指定使用者配置apiserver訪問認證

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"

4.2 以kubernetes-ca的身份簽署請求證書

基於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

以ikubernetes的身份憑據生成kubeconfig配置,將其儲存在kubectl預設搜尋路徑指向的$HOME/.kube/mason-kube.config檔案中。另外,因指向當前叢集的配置項已經存在,即位於clusters配置段中的kubernetes,這裡直接複用該叢集定義。

① 生成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
I have a dream so I study hard!!!