Kubernetes 集群安全機(jī)制詳解
本文主要介紹 Kubernetes 的安全機(jī)制,如何使用一系列概念、技術(shù)點(diǎn)、機(jī)制確保集群的訪問是安全的,涉及到的關(guān)鍵詞有:api-server,認(rèn)證,授權(quán),準(zhǔn)入控制,RBAC,Service Account,客戶端證書認(rèn)證,Kubernetes 用戶,Token 認(rèn)證等等。雖然涉及到的技術(shù)點(diǎn)比較瑣碎,比較多,但是了解整個(gè)機(jī)制后就很容易將其串起來,從而能很好地理解 Kubernetes 集群安全機(jī)制。
Kubernetes api-server 安全訪問機(jī)制
kube-apiserver 是 Kubernetes 整個(gè)集群的入口,是一個(gè) REST API 服務(wù),提供的 API 實(shí)現(xiàn)了 Kubernetes 各類資源對象(如 Pod,RC,Service 等)的增、刪、改、查,API Server 也是集群內(nèi)各個(gè)功能模塊之間交互和通信的樞紐,是整個(gè)集群的總線和數(shù)據(jù)中心。

由此可見 API Server 的重要性了,我們用 kubectl、各種語言提供的客戶端庫或者發(fā)送 REST 請求和集群交互,其實(shí)底層都是以 HTTP REST 請求的方式同 API Server 交互,那么訪問的安全機(jī)制是如何保證的呢,總不能隨便來一個(gè)請求都能接受并響應(yīng)吧。API Server 為此提供了一套特有的、靈活的安全機(jī)制,每個(gè)請求到達(dá) API Server 后都會經(jīng)過:認(rèn)證(Authentication)–>授權(quán)(Authorization)–>準(zhǔn)入控制(Admission Control)三道安全關(guān)卡,通過這三道安全關(guān)卡的請求才給予響應(yīng):

認(rèn)證(Authentication)
認(rèn)證階段的工作是識別用戶身份,支持的認(rèn)證方式有很多,比如:HTTP Base,HTTP token,TLS,Service Account,OpenID Connect 等,API Server 啟動時(shí)可以同時(shí)指定多種認(rèn)證方式,會逐個(gè)使用這些方法對客戶請求認(rèn)證,只要通過任意一種認(rèn)證方式,API Server 就會認(rèn)為 Authentication 成功。高版本的 Kubernetes 默認(rèn)認(rèn)證方式是 TLS。在 TLS 認(rèn)證方案中,每個(gè)用戶都擁有自己的 X.509 客戶端證書,API 服務(wù)器通過配置的證書頒發(fā)機(jī)構(gòu)(CA)驗(yàn)證客戶端證書。
授權(quán)(Authorization)
授權(quán)階段判斷請求是否有相應(yīng)的權(quán)限,授權(quán)方式有多種:AlwaysDeny,AlwaysAllow,ABAC,RBAC,Node 等。API Server 啟動時(shí)如果多種授權(quán)模式同時(shí)被啟用,Kubernetes 將檢查所有模塊,如果其中一種通過授權(quán),則請求授權(quán)通過。 如果所有的模塊全部拒絕,則請求被拒絕(HTTP狀態(tài)碼403)。高版本 Kubernetes 默認(rèn)開啟的授權(quán)方式是 RBAC 和 Node。
準(zhǔn)入控制(Admission Control)
準(zhǔn)入控制判斷操作是否符合集群要求,準(zhǔn)入控制配備有一個(gè)“準(zhǔn)入控制器”的列表,發(fā)送給 API Server 的每個(gè)請求都需要通過每個(gè)準(zhǔn)入控制器的檢查,檢查不通過,則 API Server 拒絕調(diào)用請求,有點(diǎn)像 Web 編程的攔截器的意思。具體細(xì)節(jié)在這里不進(jìn)行展開了,如想進(jìn)一步了解見這里: Using Admission Controllers 。
Kubernetes 認(rèn)證方式之客戶端證書(TLS)
通過上一節(jié)介紹我們知道 Kubernetes 認(rèn)證方式有多種,這里我們簡單介紹下客戶端證書(TLS)認(rèn)證方式,也叫 HTTPS 雙向認(rèn)證。一般我們訪問一個(gè) https 網(wǎng)站,認(rèn)證是單向的,只有客戶端會驗(yàn)證服務(wù)端的身份,服務(wù)端不會管客戶端身份如何。我們來大概看下 HTTPS 握手過程(單向認(rèn)證):
- 客戶端發(fā)送 Client Hello 消息給服務(wù)端;
- 服務(wù)端回復(fù) Server Hello 消息和自身證書給客戶端;
- 客戶端檢查服務(wù)端證書的合法性,證書檢查通過后根據(jù)雙方發(fā)送的消息生成 Premaster Key,然后用服務(wù)端的證書里面的公鑰加密 Premaster Key 并發(fā)送給服務(wù)端 ;
- 服務(wù)端通過自己的私鑰解密得到 Premaster Key,然后通過雙方協(xié)商的算法和交換的消息生成 Session Key(后續(xù)雙方數(shù)據(jù)加密用的對稱密鑰,客戶端也能通過同樣的方法生成同樣的 Key),然后回復(fù)客戶端一個(gè)消息表明握手結(jié)束,后續(xù)發(fā)送的消息會以協(xié)商的對稱密鑰加密。
HTTPS 雙向認(rèn)證的過程就是在上述第 3 步的時(shí)候同時(shí)回復(fù)自己的證書給服務(wù)端,然后第 4 步服務(wù)端驗(yàn)證收到客戶端證書的合法性,從而達(dá)到了驗(yàn)證客戶端的目的。在 Kubernetes 中就是用了這樣的機(jī)制,只不過相關(guān)的 CA 證書是自簽名的:

Kubernetes 授權(quán)方式之 RBAC 介紹
基于角色的訪問控制(Role-Based Access Control,即 RBAC),是 Kubernetes 提供的一種授權(quán)策略,也是新版集群默認(rèn)啟用的方式。RBAC 將角色和角色綁定分開,角色指的是一組定義好的操作集群資源的權(quán)限,而角色綁定是將角色和用戶、組或者服務(wù)賬號實(shí)體綁定,從而賦予這些實(shí)體權(quán)限??梢钥闯?RBAC 這種授權(quán)方式很靈活,要賦予某個(gè)實(shí)體權(quán)限只需要綁定相應(yīng)的角色即可。針對 RBAC 機(jī)制,Kubernetes 提供了四種 API 資源:Role、ClusterRole、RoleBinding、ClusterRoleBinding。

Role:只能用于授予對某一單一命名空間中資源的訪問權(quán)限,因此在定義時(shí)必須指定 namespace,以下示例描述了 default 命名空間中的一個(gè) Role 對象的定義,用于授予對 Pod 的讀訪問權(quán)限:
- kind: Role
- apiVersion: rbac.authorization.k8s.io/v1beta1
- metadata:
- namespace: default
- name: pod-reader
- rules:
- - apiGroups: [""] # 空字符串""表明使用core API group
- resources: ["pods"]
- verbs: ["get", "watch", "list"]
ClusterRole:針對集群范圍的角色,能訪問整個(gè)集群的資源,下面示例中的 ClusterRole 定義可用于授予用戶對某一特定命名空間,或者所有命名空間中的 secret(取決于其綁定方式)的讀訪問權(quán)限:
- kind: ClusterRole
- apiVersion: rbac.authorization.k8s.io/v1beta1
- metadata:
- # 鑒于ClusterRole是集群范圍對象,所以這里不需要定義"namespace"字段
- name: secret-reader
- rules:
- - apiGroups: [""]
- resources: ["secrets"]
- verbs: ["get", "watch", "list"]
RoleBinding:將 Role 和用戶實(shí)體綁定,從而賦予用戶實(shí)體命名空間內(nèi)的權(quán)限,同時(shí)也支持 ClusterRole 和用戶實(shí)體的綁定,下面示例中定義的 RoleBinding 對象在 default 命名空間中將 pod-reader 角色授予用戶 jane。 這一授權(quán)將允許用戶 jane 從 default 命名空間中讀取 Pod:
- # 以下角色綁定定義將允許用戶"jane"從"default"命名空間中讀取 Pod。
- kind: RoleBinding
- apiVersion: rbac.authorization.k8s.io/v1beta1
- metadata:
- name: read-pods
- namespace: default
- subjects:
- - kind: User
- name: jane
- apiGroup: rbac.authorization.k8s.io
- roleRef:
- kind: Role
- name: pod-reader
- apiGroup: rbac.authorization.k8s.io
ClusterRoleBinding:將 ClusterRole 和用戶實(shí)體綁定,從而賦予用戶實(shí)體集群范圍的權(quán)限,下面示例中所定義的 ClusterRoleBinding 允許在用戶組 manager 中的任何用戶都可以讀取集群中任何命名空間中的 secret:
- # 以下`ClusterRoleBinding`對象允許在用戶組"manager"中的任何用戶都可以讀取集群中任何命名空間中的secret。
- kind: ClusterRoleBinding
- apiVersion: rbac.authorization.k8s.io/v1beta1
- metadata:
- name: read-secrets-global
- subjects:
- - kind: Group
- name: manager
- apiGroup: rbac.authorization.k8s.io
- roleRef:
- kind: ClusterRole
- name: secret-reader
- apiGroup: rbac.authorization.k8s.io
關(guān)于 RBAC 更詳細(xì)的講解見這里: https://jimmysong.io/kubernete ... .html
Kubernetes 中兩種賬號類型介紹
Kubernetes 中有兩種用戶(User):服務(wù)賬號(ServiceAccount)和普通的用戶(User)。 ServiceAccount 是由 Kubernetes 管理的,而 User 賬號是在外部管理,Kubernetes 不存儲用戶列表,也就是說針對用戶的增、刪、該、查都是在集群外部進(jìn)行,Kubernetes 本身不提供普通用戶的管理。
兩種賬號的區(qū)別:
- ServiceAccount 是 Kubernetes 內(nèi)部資源,而普通用戶是存在于 Kubernetes 之外的;
- ServiceAccount 是屬于某個(gè)命名空間的,不是全局的,而普通用戶是全局的,不歸某個(gè) namespace 特有;
- ServiceAccount 一般用于集群內(nèi)部 Pod 進(jìn)程使用,和 api-server 交互,而普通用戶一般用于 kubectl 或者 REST 請求使用;
ServiceAccount 的實(shí)際應(yīng)用
ServiceAccount 可以用于 Pod 訪問 api-server,其對應(yīng)的 Token 可以用于 kubectl 訪問集群,或者登陸 kubernetes dashboard。
普通用戶的實(shí)際應(yīng)用
- X509 客戶端證書,客戶端證書驗(yàn)證通過為 API Server 指定 –client-ca-file=xxx 選項(xiàng)啟用,API Server 通過此 ca 文件來驗(yàn)證 API 請求攜帶的客戶端證書的有效性,一旦驗(yàn)證成功,API Server 就會將客戶端證書 Subject 里的 CN 屬性作為此次請求的用戶名。關(guān)于客戶端證書方式的用戶后面會有專門的實(shí)踐介紹。
- 靜態(tài)token文件,通過指定 –token-auth-file=SOMEFILE 選項(xiàng)來啟用 bearer token 驗(yàn)證方式,引用的文件是一個(gè)包含了 token,用戶名,用戶 ID 的 csv 文件,請求時(shí),帶上 Authorization: Bearer xxx 頭信息即可通過 bearer token 驗(yàn)證;
- 靜態(tài)密碼文件,通過指定 --basic-auth-file=SOMEFILE 選項(xiàng)啟用密碼驗(yàn)證,引用的文件是一個(gè)包含密碼,用戶名,用戶 ID 的 csv 文件,請求時(shí)需要將 Authorization 頭設(shè)置為 Basic BASE64ENCODED(USER:PASSWORD);
實(shí)踐:基于客戶端證書認(rèn)證方式新建 Kubeconfig 訪問集群
Kubeconfig 文件詳解
我們知道在安裝完 Kubernetes 集群后會生成 $HOME/.kube/config 文件,這個(gè)文件就是 kubectl 命令行工具訪問集群時(shí)使用的認(rèn)證文件,也叫 Kubeconfig 文件。這個(gè) Kubeconfig 文件中有很多重要的信息,文件大概結(jié)構(gòu)是這樣,這里說明下每個(gè)字段的含義:
- apiVersion: v1
- clusters:
- - cluster:
- certificate-authority-data: ...
- server: https://192.168.26.10:6443
- name: kubernetes
- contexts:
- - context:
- cluster: kubernetes
- user: kubernetes-admin
- name: kubernetes-admin@kubernetes
- current-context: kubernetes-admin@kubernetes
- kind: Config
- preferences: {}
- users:
- - name: kubernetes-admin
- user:
- client-certificate-data: ...
- client-key-data: ...
可以看出文件分為三大部分:clusters、contexts、users。
clusters 部分
定義集群信息,包括 api-server 地址、certificate-authority-data: 用于服務(wù)端證書認(rèn)證的自簽名 CA 根證書(master 節(jié)點(diǎn) /etc/kubernetes/pki/ca.crt 文件內(nèi)容 )。
contexts 部分
集群信息和用戶的綁定,kubectl 通過上下文提供的信息連接集群。
users 部分
多種用戶類型,默認(rèn)是客戶端證書(x.509 標(biāo)準(zhǔn)的證書)和證書私鑰,也可以是 ServiceAccount Token。這里重點(diǎn)說下前者:
- client-certificate-data:base64 加密后的客戶端證書;
- client-key-data:base64 加密后的證書私鑰;
一個(gè)請求在通過 api-server 的認(rèn)證關(guān)卡后,api-server 會從收到客戶端證書中取用戶信息,然后用于后面的授權(quán)關(guān)卡,這里所說的用戶并不是服務(wù)賬號,而是客戶端證書里面的 Subject 信息:O 代表用戶組,CN 代表用戶名。為了證明,可以使用 openssl 手動獲取證書中的這個(gè)信息:
首先,將 Kubeconfig 證書的 user 部分 client-certificate-data 字段內(nèi)容進(jìn)行 base64 解密,保存文件為 client.crt,然后使用 openssl 解析證書信息即可看到 Subject 信息:
- openssl x509 -in client.crt -text
解析集群默認(rèn)的 Kubeconfig 客戶端證書得到的 Subject 信息是:
- Subject: O=system:masters, CN=kubernetes-admin
可以看出該證書綁定的用戶組是 system:masters,用戶名是 kubernetes-admin,而集群中默認(rèn)有個(gè) ClusterRoleBinding 叫 cluster-admin,它將名為 cluster-admin 的 ClusterRole 和用戶組 system:masters 進(jìn)行了綁定,而名為 cluster-admin 的 ClusterRole 有集群范圍的 Superadmin 權(quán)限,這也就理解了為什么默認(rèn)的 Kubeconfig 能擁有極高的權(quán)限來操作 Kubernetes 集群了。
新建具有只讀權(quán)限的 Kubeconfig 文件
上面我們已經(jīng)解釋了為什么默認(rèn)的 Kubeconfig 文件具有 Superadmin 權(quán)限,這個(gè)權(quán)限比較高,有點(diǎn)類型 Linux 系統(tǒng)的 Root 權(quán)限。有時(shí)我們會將集群訪問權(quán)限開放給其他人員,比如供研發(fā)人員查看 Pod 狀態(tài)、日志等信息,這個(gè)時(shí)候直接用系統(tǒng)默認(rèn)的 Kubeconfig 就不太合理了,權(quán)限太大,集群的安全性沒有了保障。更合理的做法是給研發(fā)人員一個(gè)只讀權(quán)限的賬號,避免對集群進(jìn)行一些誤操作導(dǎo)致故障。
我們以客戶端證書認(rèn)證方式創(chuàng)建 Kubeconfig 文件,所以需要向集群自簽名 CA 機(jī)構(gòu)(master 節(jié)點(diǎn))申請證書,然后通過 RBAC 授權(quán)方式給證書用戶授予集群只讀權(quán)限,具體方法如下:
假設(shè)我們設(shè)置證書的用戶名為:developer – 證書申請時(shí) -subj 選項(xiàng) CN 參數(shù)。
1、創(chuàng)建證書私鑰:
- openssl genrsa -out developer.key 2048
2、用上面私鑰創(chuàng)建一個(gè) csr(證書簽名請求)文件,其中我們需要在 subject 里帶上用戶信息(CN為用戶名,O為用戶組):
- openssl req -new -key developer.key -out developer.csr -subj "/CN=developer"
其中/O參數(shù)可以出現(xiàn)多次,即可以有多個(gè)用戶組
3、找到 Kubernetes 集群(API Server)的 CA 根證書文件,其位置取決于安裝集群的方式,通常會在 master 節(jié)點(diǎn)的 /etc/kubernetes/pki/ 路徑下,會有兩個(gè)文件,一個(gè)是 CA 根證書(ca.crt),一個(gè)是 CA 私鑰(ca.key)。
4、通過集群的 CA 根證書和第 2 步創(chuàng)建的 csr 文件,來為用戶頒發(fā)證書:
- openssl x509 -req -in developer.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out developer.crt -days 365
至此,客戶端證書頒發(fā)完成,我們后面會用到文件是 developer.key 和 developer.crt
基于 RBAC 授權(quán)方式授予用戶只讀權(quán)限
在 Kubernetes 集群中已經(jīng)有個(gè)默認(rèn)的名為 view 只讀 ClusterRole 了,我們只需要將該 ClusterRole 綁定到 developer 用戶即可:
- kubectl create clusterrolebinding kubernetes-viewer --clusterrole=view --user=developer
基于客戶端證書生成 Kubeconfig 文件
前面已經(jīng)生成了客戶端證書,并給證書里的用戶賦予了集群只讀權(quán)限,接下來基于客戶端證書生成 Kubeconfig 文件:
拷貝一份 $HOME/.kube.config,假設(shè)名為 developer-config,在其基礎(chǔ)上做修改:
1、contexts 部分 user 字段改為 developer,name 字段改為 developer@kubernetes。(這些改動隨意命名,只要前后統(tǒng)一即可);
2、users 部分 name 字段改為 developer,client-certificate-data 字段改為 developer.crt base64 加密后的內(nèi)容,client-key-data 改為 developer.key base64 加密后的內(nèi)容;
注意:證書 base64 加密時(shí)指定 –wrap=0 參數(shù)
- cat developer.crt | base64 –wrap=0
- cat developer.key | base64 –wrap=0
接下來測試使用新建的 Kubeconfig 文件:
- [root@master ~]# kubectl –kubeconfig developer-config –context=developer@kubernetes get pod
- NAME READY STATUS RESTARTS AGE
- nginx-deployment-5754944d6c-dqsdj 1/1 Running 0 5d9h
- nginx-deployment-5754944d6c-q675s 1/1 Running 0 5d9h
- [root@master ~]# kubectl –kubeconfig developer-config –context=developer@kubernetes delete pod nginx-deployment-5754944d6c-dqsdj
- Error from server (Forbidden): pods “nginx-deployment-5754944d6c-dqsdj” is forbidden: User “developer” cannot delete resource “pods” in API group “” in the namespace “default”
可以看出新建的 Kubeconfig 文件可以使用,寫權(quán)限是被 forbidden 的,說明前面配的 RBAC 權(quán)限機(jī)制是起作用的。
實(shí)踐:Kubeconfig 或 token 方式登陸 Kubernetes dashboard
我們打開 kubernetes dashboard 訪問地址首先看到的是登陸認(rèn)證頁面,有兩種登陸認(rèn)證方式可供選擇:Kubeconfig 和 Token 方式。

其實(shí)兩種方式都需要服務(wù)賬號的 Token,對于 Kubeconfig 方式直接使用集群默認(rèn)的 Kubeconfig: $HOME/.kube/config 文件不能登陸,因?yàn)槲募腥鄙?Token 字段,所以直接選擇本地的 Kubeconfig 文件登陸會報(bào)錯(cuò)。正確的使用方法是獲取某個(gè)服務(wù)賬號的 Token,然后將 Token 加入到 $HOME/.kube/config 文件。下面具體實(shí)踐下兩種登陸 dashboard 方式:
準(zhǔn)備工作
首先,兩種方式都需要服務(wù)賬號,所以我們先創(chuàng)建一個(gè)服務(wù)賬號,然后為了測試,給這個(gè)服務(wù)賬號一個(gè)查看權(quán)限(RBAC 授權(quán)),到時(shí)候登陸 dashboard 后只能查看,不能對集群中的資源做修改。
1、創(chuàng)建一個(gè)服務(wù)賬號(在 default 命名空間下):
- kubectl create serviceaccount kube-dashboard-reader
2、將系統(tǒng)自帶的 ClusterRole:view 角色綁定到上一步創(chuàng)建的服務(wù)賬號,授予集群范圍的資源只讀權(quán)限:
- kubectl create clusterrolebinding kube-dashboard-reader --clusterrole=view --serviceaccount=default:kube-dashboard-reader
3、獲取服務(wù)賬號的 token:
- kubectl get secret `kubectl get secret -n default | grep kube-dashboard-reader | awk '{print $1}'` -o jsonpath={.data.token} -n default | base64 -d
Kubeconfig 方式登陸 dashboard
拷貝一份 $HOME/.kube/config,修改內(nèi)容,將準(zhǔn)備工作中獲取的 Token 添加入到文件中:在 Kubeconfig 的 Users 下 User 部分添加,類型下面這樣:
- ...
- users:
- - name: kubernetes-admin
- user:
- client-certificate-data: ...
- client-key-data: ...
- token: <這里為上面獲取的 Token...>
然后登陸界面選擇 Kubeconfig 單選框,選擇該文件即可成功登陸 dashboard。
Token 方式登陸 dashboard
登陸界面選擇 Token 單選框,將準(zhǔn)備工作中獲取的 Token 粘貼進(jìn)去即可成功登陸。






























