K8S 提供了豐富的認證和授權機制,可以滿足各種場景細粒度的訪問控制。本文會介紹 k8s 中的用戶認證、授權機制,並通過例子闡述 kubeconfig 的生成和原理,最後會列舉常見的證書問題,如過期、吊銷、租戶控制等。
兩種用戶
k8s中的客戶端訪問有兩類用戶:
普通用戶(Human User),一般是集群外訪問,如 kubectl 使用的證書
service account: 如集群內的 Pod
有什麼區別呢?
k8s 不會對 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
如果獲取其他 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 中去的:https://istio.io/docs/setup/kubernetes/sidecar-injection/。
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來管理你的證書實現證書自動續簽。
cert-manager + nginx-ingress-controller 結合可以參考這個文章:https://cert-manager.io/docs/tutorials/acme/ingress/
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 統一,更加方便。