https://mp.weixin.qq.com/s/ZBsZHQtAz_vKS8fwvHKB3g圖文分析Kubernetes認證、授權和准入控制
序言:Kubernetes 為我們提供了多種而且複雜的認證和授權機制,絕大多數情況下,都可以完全滿足多種場景下顆粒度很細的訪問許可權控制。這篇文章將會給大家介紹Kubernetes中的認證和授權機制,並通過相關的例子來概述 kubeconfig配置檔案的生成以及原理,然後也參雜了常見的證書問題,比如過期、吊銷、租戶等。
兩種使用者
Kubernetes客戶端的訪問有兩類使用者:
-
普通使用者
-
一般是叢集外訪問,如 通過kubectl 使用的證書service account,再比如叢集內的 Pod
那到底存在哪些異同?
kubernetes不會對user直接進行管理,所以並不會儲存 user的資訊,你也不能通過呼叫k8s api來增刪查這個 user。你可以認為這個 user 指的就是公司內的人員,一般需要對接內部許可權系統,user 的增刪操作都是在 k8s 外部進行,k8s 只做認證不做管理。
k8s 會對serviceaccount 進行管理,他的作用是給叢集內執行的 pod 提供一種認證的方式,如果你這個 pod 想呼叫apiserver操作一些資源如獲取 node列表,就需要繫結一個serviceaccount賬戶給自己,併為這個serviceaccount賦予一定的許可權,這樣就做到了實體和許可權的分離,也就是後面會提到的 rbac 授權。
作用範圍:
User獨立在 K8S 之外,也就是說User是可以作用於全域性的,跨 namespace,並且需要在全域性唯一
ServiceAccount是K8S的一種資源,是存在於某個namespace之中的,在不同namespace中可以同名,代表了不同的資源。
舉例說明:
為 user 生成 kubeconfig
一個同事張三都想要一份叢集的 kubeconfig 用來日常 kubectl 操作叢集,但限定只能操作名為 test 的 namespace,公司有獨立的許可權系統,他的使用者 ID 是唯一的,叫zhangsan。接下來手動為這個使用者生成 kubeconfig
生成證書:
得到:
zhangsan.pem
zhangsan-key.pem
接下來為 zhangsan 授權,首先生成一份角色:test-role, 許可權為test 的名稱空間下的所有資源的所有操作許可權
然後將這個角色role繫結到zhangsan這個 user 上,代表 zhangsan 擁有了這個 role 的許可權
為張三生成專屬的 kubeconfig 檔案,名為zhangsan.conf
獲取 test 下所有的 pod
kubectl get pod -n test
如果獲取其他 namespace 下的pod,則報許可權
為 pod 建立 serviceaccount
以 metrics-server的 pod 為例,需要對 pod、node、ns 等資源進行 get list操作,因此許可權配置為:
pod 的 yaml 配置中宣告serviceAccountName為metrics-server
以上是 user使用者使用 kubeconfig、pod 使用 serviceaccount 的示例,pod 使用serviceaccount是比較好理解的,但其實 kubeconfig 也可以直接用serviceaccount來生成,不一定非得用 user,只是大多數情況下,kubeconfig 的管理是和使用者的宣告週期一致的,即子使用者可以申領一份自己的 kubeconfig,管理員可以隨時吊銷或者禁用子使用者 kubeconfig 的效力。這個在後面的”多租戶下的kubeconfig“中會提到。
三種機制
談 k8s 的認證和訪問控制,一般都會看到這張圖:
k8s 中所有的 api 請求都要通過一個 gateway 也就是 apiserver 元件來實現,是叢集唯一的訪問入口。既然是 gateway,最基礎的功能就是 api 的認證 + 鑑權了。對應了圖上的步驟1和2,而 k8s 中還提供了第 3 步的 admission Control(准入控制),可以更方便地攔截、校驗資源請求。
三種機制:
1、認證:Authentication,即身份認證。檢查使用者是否為合法使用者,如客戶端證書、密碼、bootstrap tookens和JWT tokens等方式。
2、鑑權:Authorization,即許可權判斷。判斷該使用者是否具有該操作的許可權,k8s 中支援 Node、RBAC(Role-Based Access Control)、ABAC、webhook等機制,RBAC 為主流方式
3、准入控制:Admission Control。請求的最後一個步驟,一般用於拓展功能,如檢查 pod 的resource是否配置,yaml配置的安全是否合規等。一般使用admission webhooks來實現
1-2-3 全部通過後api 請求會被處理,在 k8s 中也就意味著資源變更可以落庫到 etcd。
K8S 中的認證
上面提到 k8s 並不儲存 user,只知道一個 user 名稱,因此 user 在訪問api 時怎麼做的認證?
kubernetes 支援很多種認證機制,包括:
-
X509 client certs
-
Static Token File
-
Bootstrap Tokens
-
Static Password File
-
Service Account Tokens
-
OpenId Connect Tokens
-
Webhook Token Authentication
-
Authticating Proxy
-
Anonymous requests
-
User impersonation
-
Client-go credential plugins
前面提到的 user 生成 kubeconfig就是X509 client certs方式, 而 metric-server 就是Service Account Tokens方式。
X509 client certs
即客戶端證書認證,X509 是一種數字證書的格式標準,是 kubernetes 中預設開啟使用最多的一種,也是最安全的一種,api-server 啟動時會指定 ca 證書以及 ca 私鑰,只要是通過同一個 ca 簽發的客戶端 x509 證書,則認為是可信的客戶端,kubeadm 安裝叢集時就是基於證書的認證方式。
客戶端證書認證叫作 TLS 雙向認證,也就是伺服器、客戶端互相驗證證書的正確性,在都正確的情況下協調通訊加密方案。apiserver 、controller-manager、scheduler、kubelet 等元件之間的互動,一般也是基於X509的客戶端認證方式,目前最常用的 X509 證書製作工具有 openssl、cfssl ,上面生成 kubeconfig 時用到就是 cfssl 工具簽發的證書。
還是舉個例子,在手動部署 k8s 叢集時需要做的證書操作,如果你已經熟悉這個過程或者用了 kubeadm 等部署工具,可以跳過這一段。
1、基礎證書生成
ca-config.json
建立用來生成 CA 檔案的 JSON 配置檔案,這個檔案後面會被各種元件使用,包括了證書過期時間的配置,expiry欄位
ca-csr.json
建立用來生成 CA 證書籤名請求(CSR)的 JSON 配置檔案
生成基礎 ca 證書
建立自簽名 CA 證書:這裡只需要ca-csr.json檔案,執行後會生成三個檔案:
ca.csr:證書籤名請求,一般用於提供給證書頒發機構,自籤的就不需要了
ca.pem:證書,公共證書
ca-key.pem:CA金鑰
2、生成 apiserver 證書
apiserver-csr.json
hosts列表不僅包含了三臺 master 機器的ip,還包括了 對應的負載均衡的 ip和外網 ip(如果有的話),以及 kubernetes 的 svc IP:172.18.0.1
這個 ip 是 svc ip range 中的第一個 ip,如果沒有這個 ip,叢集內的 pod 將無法通過 serviceaccount 的形式訪問 apiserver 並鑑權,會報證書錯誤。
建立apiserver 的 CA 證書:這裡需要4 個檔案
-
apiserver-csr.json:apiserver的證書配置
-
ca.pem:基礎公鑰
-
ca-key.pem:基礎私鑰
-
ca-config.json:配置檔案,如過期時間
執行後會生成三個檔案:
-
apiserver.csr
-
apiserver.pem
-
apiserver-key.pem
** 使用證書**
Service Account Tokens
也就是 service account 使用的認證方式。
你會發現x509的認證方式比較複雜,需要做很多證書,如果我在叢集中部署了一個 pod,比如一些 operator,想訪問apiserver獲取叢集的一些資訊,甚至對叢集的資源進行改動,這種認證屬於 k8s 管理範疇,因此 k8s 定義了一種資源serviceaccounts來定義一個 pod 應該擁有什麼許可權。
serviceaccounts 是面向 namespace 的,每個 namespace 建立的時候,kubernetes 會自動在這個 namespace 下面建立一個預設的 serviceaccounts,並且這個 serviceaccounts 只能訪問該 namespace 的資源。serviceaccounts 和 pod、service、deployment 一樣是 kubernetes 叢集中的一種資源,使用者可以建立自己的serviceaccounts。
service account 主要包含了三個內容:namespace、token 和 ca
-
namespace: 指定了 pod 所在的 namespace
-
token: token 用作身份驗證
-
ca: ca 用於驗證 apiserver 的證書
每個 service account 都對應一個 secret,這三個資訊就存放在這個 secret 裡,以 base64 編碼。service account 通過 mount 的方式儲存在 pod 的檔案系統中,其三者都是儲存在 /var/run/secrets/kubernetes.io/serviceaccount/目錄下。
kubeconfig 的生成與含義
kubeconfig 是用來訪問 k8s 叢集的憑證,生成 kubeconfig 的步驟很簡單但引數很多,這裡以生成 admin 的 kubeconfig 為例,解釋各引數的含義。
生成最高許可權的 kubeconfig。
一般情況下叢集建立之後,會先生成一份最高許可權的 kubeconfig,即管理員角色,可以操作叢集的所有資源,併為其他使用者建立或刪除許可權,可以稱之為 admin 證書,生成方式是:
admin-csr.json
admin 證書
生成 admin.conf,即最高許可權的 kubeconfig
叢集引數
本段設定了所需要訪問的叢集的資訊。
使用 set-cluster 設定了需要訪問的叢集,如上為 kubernetes 這只是個名稱,實際為 –server 指向的 apiserver 所在的叢集。
–certificate-authority 設定了該叢集的公鑰
–embed-certs 為 true 表示將 –certificate-authority 證書寫入到 kubeconfig 中
–server 則表示該叢集的 apiserver 地址
使用者引數
本段主要設定使用者的相關資訊,主要是使用者證書。
使用者名稱: zhangsan
證書: /etc/kubernetes/ssl/zhangsan.pem
私鑰: /etc/kubernetes/ssl/zhangsan-key.pem
這一步操作是指客戶端的證書首先要經過叢集 CA 的簽署,否則不會被叢集認可,認證就失敗。
此處使用的是 客戶端 x509 認證方式,也可以使用token認證,如kubelet的 TLS Boostrap機制下的bootstrapping 使用的就是 token 認證方式
上下文引數
叢集引數和使用者引數可以同時設定多對,而上下文引數就是叢集引數和使用者引數關聯起來。
上面的上下文名稱為 kubenetes,叢集為 kubenetes(apiserver 地址對應的叢集),使用者為zhangsan,表示使用 zhangsan 的使用者憑證來訪問
kubenetes 叢集
最後使用 kubectl config use-context kubernetes 來使用名為 kubenetes 的環境項來作為配置。
如果配置了多個環境項,可以通過切換不同的環境項名字來訪問到不同的叢集環境。
kubeconfig 的認證過程
正向生成 kubeconfig 我們已經做完了,apiserver 認證請求時,如何解析 kubeconfig 檔案的內容呢?
我們可以看下 kubeconfig 的內容:
除了 context,裡面有三個證書欄位,都是 base64 編碼後的內容
certificate-authority-data: server 端的證書,用於驗證 apiserver 的合法性
client-certificate-data: 客戶端證書
client-key-data: 客戶端私鑰
可以提取出來 certificate-authority-data 的內容放到一個檔案cert.txt,然後base64解碼
ertificate-authority-data:
得到的內容其實就是 ca.pem 即服務端證書,apiserver 的證書也是基於ca.pem簽發,因為 TLS 是雙向認證,apiserver 在認證 kubectl請求時,kubectl 也需要驗證 apiserver 的證書,防止中間人攻擊,驗證的欄位就是certificate-authority-data
client-certificate:
因為 k8s 沒有 user 這種資源,因此在使用 kubeconfig 訪問時,身份資訊就“隱藏”在client-certificate的資料中,我們來檢視一下。
將 kubeconfig 中的client-certificate-data的內容放在一個檔案 client.txt 中,然後解碼:
檢視證書內容:
輸出內容可以看到Subject: organization=system:masters, common_name=kubernetes-admin
apiserver 驗證、解析請求,得到 system:masters 的http上下文資訊,並傳給後續的authorizers來做許可權校驗。
O 和 CN 的含義
“O”:Organization, apiserver接到請求後從證書中提取該欄位作為請求使用者所屬的組 (Group)
“CN”:Common Name,apiserver從證書中提取該欄位作為請求的使用者名稱 (User Name)
在admin-csr.json中, admin使用了system:masters作為組 (Group)
k8s 預定義了 RoleBinding:cluster-admin 將 Group system:masters 與 Role cluster-admin 繫結,該 Role 授予了呼叫 k8s 相關 API 的許可權,許可權極高。
即:
-
Group: system:masters
-
ClusterRole: cluster-admin
-
ClusterRoleBinding: cluster-admin
k8s 核心元件的預設許可權
admin許可權:system:masters組,clusterrole 和 rolebinding 都叫 cluster-admin
kubelet: system:nodes組,clusterrole 和 rolebinding 都叫system:nodes,下同
kube-proxy: system:kube-proxy組
scheduler: system:kube-scheduler組
controller-manager: system:kube-controller-manager組
對應關係如圖所示
即 k8s 所有自身元件使用的許可權都是基於內建的 clusterrole,生成出來的 kubeconfig 被各元件程序使用,許可權都是預設已有 role,如果希望自己建立 role ,就要使用 rbac 授權了
至此,我們應該知道了kubeconfig 的生成流程、驗證方式,以及為什麼採用了 admin.conf 作為kubeconfig,kubectl就能擁有最高許可權。下面是一幅示意圖
K8S 中的授權
無論是 user的x509認證 還是 service account的 token認證,認證完後,都要到達第 2 步:授權,
K8S 目前支援瞭如下四種授權機制:
-
Node
-
ABAC
-
RBAC
-
Webhook
用的最多的就是 RBAC,即基於角色做許可權控制。
Node
僅 v1.7 版本以上支援 Node 授權,配合 NodeRestriction 准入控制來限制 kubelet,使其僅可訪問 node、endpoint、pod、service 以及 secret、configmap、pv、pvc 等相關的資源,在 apiserver 中使用以下配置來開啟
node 的鑑權機制:
RBAC
RBAC(Role-Based Access Control)是 kubernetes 中基於角色的訪問控制,通過自定義角色並將角色和特定的 user,group,serviceaccounts 關聯起來達到許可權控制的目的。
RBAC 中有三個比較重要的概念:
-
Role: 角色,它其實是一組規則,定義了一組對 Kubernetes API 物件的操作許可權;
-
Subject: 被作用者,包括 user、group、service account,通俗來講就是認證機制中所識別的使用者;
-
RoleBinding: 定義了“被作用者”和“角色”的繫結關係,也就是將使用者以及操作許可權進行繫結;
RBAC 其實就是通過建立角色(Role),通過 RoleBinding 將被作用者(subject)和角色(Role)進行繫結。下圖是 RBAC 中的幾種繫結關係:
示例:
role:
許可權宣告:
apiGroups為“”代表所有核心資源即 v1 group
apiGroups為“*”代表所有group
指定 pod 下的子物件如 kubectl logs xx,在resources中寫為pods/log
對於 CRD 的許可權可以定義為:
deployment、sts 等不在核心組,
以 prometheus pod 所需要的許可權為例:
K8S 中的准入控制
准入控制是請求的最後一個步驟,准入控制有許多內建的模組,可以作用於物件的 “CREATE”、”UPDATE”、”DELETE”、”CONNECT” 四個階段。在這一過程中,如果任一準入控制模組拒絕,那麼請求立刻被拒絕。一旦請求通過所有的准入控制器後就會寫入 etcd 中。
准入控制是在 apiserver 中進行配置啟用的:
kubectl api-versions |grep admission 來確認是否開啟“admissionregistration.k8s.io/v1beta1”
准入控制的配置是有序的,不同的順序會影響 kubernetes 的效能。若需要對 kubernetes 中的物件做一些擴充套件,可以使用准入控制,比如:建立 pod 時新增 initContainer 或者校驗欄位等。准入控制最常使用的擴充套件方式就是 admission webhooks,分兩種
-
MutatingAdmissionWebhook:在物件持久化之前進行修改
-
ValidatingAdmissionWebhook:在物件持久化之前進行
istio就是通過 mutating webhooks 來自動將Envoy這個 sidecar 容器注入到 Pod 中去的。
admission webhooks是同步呼叫,需要部署webhook server,並建立物件ValidatingWebhookConfiguration 或 MutatingWebhookConfiguration來指向自己的 server,以ValidatingAdmissionWebhook為例:
部署 webhook:
配置:ValidatingWebhookConfiguration
你需要等待幾秒,然後通過通過Deployment或者直接建立Pod,這時建立Pod的請求就會被apiserver攔住,呼叫ValidatingAdmissionWebhook進行檢查是否Admit通過。比如,上面的example-webhook是檢查容器映象是否以”gcr.io”為字首的。
示例中的 webhook邏輯比較簡單,只是檢查下 image name,然後啟動 http server。
AdmissionWebhook 與 Initializers 的區別:
二者都能實現動態可擴充套件載入admission controller, Initializers是序列執行,在高併發場景容易導致物件停留在uninitialized狀態,影響繼續排程。Alpha Initializers特性在k8s 1.14版本被移除了,官方更推薦AdmissionWebhook;MutatingAdmissionWebhook是序列執行,ValidatingAdmissionWebhook是並行執行,效能更好。
證書吊銷、過期更換
對於kubeconfig 這種 x509證書來說,只要證書不洩露,可以認為是很安全的。但是頒發證書容易,卻沒有很好的方案登出證書、續期證書
吊銷
想一下如果某個核心成員離職,該如何回收他的admin kubeconfig證書?或者不小心把 kubeconfig 洩露,如何讓這個 kubeconfig 無效呢?
先看下封禁手段:
如果是離職,且 k8s 叢集在內網環境,就算他把證書帶出公司也沒關係,畢竟有內網訪問限制。但如果是雲上 k8s 叢集,且開放了公網的入口,那麼安全風險就很高了,kubeconfig 只是一個檔案,你無法通過限制 ip 來源來封禁。
如果是轉崗,封禁就比較困難了,仍然在內網環境,且 kubeconfig 一般在辦公電腦上使用,即辦公網路到服務內網,通過來源封禁是不可能的。
再看下證書吊銷:
kubeconfig 證書不支援吊銷,參考 ISSUE。準確的說 k8s 沒有實現CRL(證書吊銷列表)或 OCSP(線上證書狀態協議),並沒有一個統一的地方管理這些證書,因此如果您的金鑰被盜用,Kubernetes也無法在身份驗證層知道。
如何解決這種問題:
-
重新簽發證書,涉及到apiserver 等元件的重啟
-
如果用了 rbac,封禁這個角色的許可權
如何預防這種問題:
重要:不要使用最高許可權的證書,不要使用自帶許可權的證書如 system:master,這種只適合kubelet 等元件使用,不適合用來簽發 kubeconfig
無論是用 user 還是 service account 來生成kubeconfig,都使用 rbac 來授權,這樣就算 kubeconfig 丟失,也可以解除 clusterrolebinding來吊銷許可權。即管理給使用者的授權,就變成管理clusterrolebinding了
證書的過期時間設定的短一點,如 1 個月就失效,將影響範圍降低
合理規劃主賬號和子賬戶,如主賬戶只允許有一份 admin 證書,其他子使用者全部基於 rbac 來操作,甚至主賬戶也可以用 rbac 來操作,只是許可權特別大而已
整合外部認證系統
證書之所以無法吊銷,是因為證書沒有統一的認證中心,換句話,K8S只是定義了一些角色,並沒有實現使用者管理、許可權管理,因此專業的人做專業的事,接入認證系統才是生產環境嚴肅認真的解決方案。
Kubernetes支援整合第三方Id Provider(IdP),主流的如AD、LADP以及OpenStack Keystone等。一般都是基於 OpenID Connect(OIDC)Token 的認證和授權。
當前支援OpenID Connect的產品有很多,如:
-
Keycloak
-
UAA
-
Dex
-
OpenUnison
-
雲廠商
證書續簽
證書配置中有一個欄位:”expiry”: “87600h”代表了證書的過期時間,到期後證書認證會失敗。
kubeadm 建立的 Kubernetes 叢集, apiserver、controller-manager、kubelete 等元件的證書預設有效期只有一年。
因為 k8s 版本迭代很快,官方推薦一年之內至少用 kubeadm 更新一次 Kubernetes 版本,同時會自動更新證書。
檢視根 CA 證書的有效期,預設為 10 年:
檢視當前證書有效期
重新簽發證書:續簽全部證書
也可以區域性進行續簽
如apiserver-etcd-client 、apiserver-kubelet-client、apiserver、etcd-healthcheck-client、etcd-peer、etcd-server、front-proxy-client
kubeadm alpha certs renew apiserver-etcd-client
如果不是 kubeadm 建立的叢集,需要手動重新生成所有元件的證書
kubelet 從 v1.8.0 開始支援證書輪換,當證書過期時,可以自動生成新的金鑰,並從 Kubernetes API 申請新的證書。
更換 apiserver 的 ip 或接入 nginx
如果因為機器故障,需要更換 apiserver 機器,則最好保持 ip 不變,否則證書需要重籤,因為證書檔案中聲明瞭機器 ip和負載均衡 ip,如果訪問時只用到了負載均衡 ip,那麼機器 ip 可以不用宣告,也不用擔心機器更換問題。
apiserver 接入 nginx
使用 nginx 的 passthrough,即 4 層轉發 ssl 請求(配置簡單,不需要apiserver證書)
方法二:使用七層正常的 http 轉發
多租戶叢集中的的使用者訪問控制
rbac 許可權控制是多租戶叢集中最基礎的隔離手段,如基於 namespace 做租戶隔離:
企業內部叢集:也就是公司內的叢集,是很多 k8s 客戶的使用模式,因為叢集在公司內網環境,網路風險可控,因此一般通過 namespace 對部門或產品線做隔離,如:
-
叢集管理員:admin 角色,最高許可權,可以擴、縮節點、升級叢集,負責分配PV、租戶等全域性資源,一般是叢集負責人。
-
租戶管理員:op 角色,租戶內(namespace)的最高許可權,管理租戶內的 rbac 許可權、儲存、計算資源等
-
普通使用者:rd 角色,使用許可權,根據開發測試角色不同,功能不同,可能會限制get/list/edit 等許可權。
namespace 名一般和部門 id 是一致的,方便對接內部許可權系統如 SSO,使用者登入後只能看到自己的 ns 下的業務 pod,同時可以下載自己的 kubeconfig 檔案。
因為是通過 namespace 做 rbac 許可權上的隔離,因此網路層面也要隔離 namespace,禁止互訪,如果是跨租戶的訪問需要開放白名單。而儲存和主機特權的隔離需要根據業務來決定,如seccomp/AppArmor/SELinux等是否允許業務使用。
一般情況下,namespace 的許可權隔離能滿足大多數企業 k8s 的需求,也是很多 paas 平臺的租戶實現方式。
saas & serveless 平臺:一般出現在公有云上,該場景下使用者沒有 k8s 的概念,且不同的服務可以混布在不同的 namespace,如函式計算、AI 離線計算等,你只需要在 saas 控制檯上點選部署 wordpress,選擇軟體版本,就能得到一個完整的部落格平臺,不需要關心後面的排程邏輯。
這種混布的業務如果有較高的安全需求,k8s 原生是無法滿足的,還需要使用安全容器如 kata在容器執行時來強化租戶安全。
其他場景下的認證需求
ingress
除了 k8s 叢集的認證,ingress 也需要 https 證書,而 ssl 證書的續期管理都是一件麻煩事,如果你用的是雲廠商,可以直接在雲上購買 ssl 證書並繫結域名,然後通過 ingress-controller 實現 ingress 的功能,續簽和證書管理都在雲上進行,不需要自己關心。
如果你覺得正規的 ca證書太貴,想用 Let’s Encrypt 等簽發方式,又覺得續簽麻煩,可以使用Cert manager來管理你的證書實現證書自動續簽。
helm
在 helm2 中,helm 操作需要配合叢集內部署 tiller 的 pod 來負責資源的建立,因此 tiller 需要賦予一定的許可權,一般為了簡單會為 tiller 的 pod 賦予 cluster-admin 的最高許可權
tiller pod 執行在kube-system 下,擁有叢集所有ns、所有資源的操作許可權,不過你也可以限定tiller的使用範圍,比如只能在特定的 namespace 下工作,如:
在特定 namespace 中部署 tiller,並僅限於在該 namespace 中部署資源
執行 helm init 來在 tiller-world namespace 中安裝 Tiller
而在 helm3 中已經不需要 tiller 這個元件,只需要一個 helm 二進位制,因此helm 命令使用的 kubeconfig 的許可權,也就決定了能夠在哪個 namespace 操作什麼資源,helm 許可權和 kubectl 統一,更加方便。