解析k8s中節(jié)點(diǎn)組件和集群通信原理
本文會(huì)分析node節(jié)點(diǎn)的兩個(gè)組件(kube-proxy和kubelet)同集群如何建立通信的過(guò)程。這兩個(gè)組件其實(shí)已經(jīng)覆蓋了從集群外(非POD形式)和集群進(jìn)行通信的所有可能,為什么這么說(shuō),我們可以看一下這兩個(gè)組件的特點(diǎn):
kube-proxy:只是做client和集群進(jìn)行通信 kubelet:即作為客戶端去監(jiān)聽(tīng)和獲取集群中的信息,又要作為服務(wù)端讓集群獲取這個(gè)node節(jié)點(diǎn)上的pod信息
前兩部分我們會(huì)簡(jiǎn)單的科普一下SSL的過(guò)程和k8s中通過(guò)RBAC進(jìn)行認(rèn)證的內(nèi)容,因?yàn)檫@兩部分內(nèi)容屬于看懂后續(xù)兩部分的基礎(chǔ),如果對(duì)這兩部分內(nèi)容比較熟悉的同學(xué)可以跳過(guò)。
為了增加閱讀體驗(yàn),我把有過(guò)多代碼的放到另一個(gè)文件里面,文章中相應(yīng)的部分通過(guò)鏈接的方式。
一、密不透風(fēng):SSL的過(guò)程

首先如果要想有一個(gè)CA機(jī)構(gòu)簽發(fā)的證書的話,會(huì)自己生成私鑰,然后通過(guò)私鑰證書簽名請(qǐng)求(CSR)讓CA去簽名,這個(gè)證書簽名中會(huì)包含服務(wù)端的一些信息(比如common name、organization等)還有公鑰。
CA收到證書簽名請(qǐng)求(CSR)之后,會(huì)生成一個(gè)證書,證書內(nèi)容包括申請(qǐng)者的信息,CA的信息以及申請(qǐng)者公鑰,然后會(huì)用CA的私鑰進(jìn)行加密,然后把證書給服務(wù)端。
當(dāng)客戶端想要和服務(wù)端進(jìn)行SSL連接的時(shí)候要先要發(fā)個(gè)申請(qǐng)連接的請(qǐng)求。
然后服務(wù)端就會(huì)把通過(guò)CA簽發(fā)的證書給客戶端。
客戶端用CA的證書去驗(yàn)證服務(wù)端的證書是否正確(一般CA證書會(huì)內(nèi)置在操作系統(tǒng)中,這也是為什么第0步驟會(huì)用虛線表示)
客戶端驗(yàn)證通過(guò)后會(huì)用證書中的公鑰對(duì)數(shù)據(jù)進(jìn)行加密保證安全。
注:
這里只是說(shuō)的單向SSL過(guò)程,如果是雙向的話客戶端也要有自己的證書 驗(yàn)證證書通過(guò)后之后,客戶端和服務(wù)端會(huì)協(xié)商一個(gè)對(duì)稱加密進(jìn)行通信,因?yàn)榉菍?duì)稱加密太慢了。(因?yàn)閷?duì)稱加密的過(guò)程對(duì)本文沒(méi)影響,所以有興趣的讀者可以自行查找資料或者和我探討交流) 這里為了照顧大多數(shù)讀者,沒(méi)有細(xì)致的深入,比如CA怎么簽發(fā)的服務(wù)端證書。考慮到本文主要講k8s的,不是講加密的,這里沒(méi)有重點(diǎn)寫出
附加幾個(gè)生成密鑰和查看的命令:
生成私鑰:openssl genrsa -out helios.key 1024 通過(guò)私鑰生成公鑰:openssl rsa -in helios.key -pubout -out helios.pem 生成證書簽名請(qǐng)求:openssl req -key helios.key -new -out helios.req CA簽發(fā)證書:openssl x509 -req -in helios.req -CA cacertificate.pem -CAkey caprivate.key -out helioscertificate.pem 查看證書簽名請(qǐng)求文件內(nèi)容:openssl req -in helios.req -noout -text
二、各司其職:RBAC是什么
RBAC的本質(zhì)就是給不同的用戶不同的角色,角色代表的權(quán)限,是由k8s本身定義的,用戶代表的訪問(wèn)集群的“人”。
所以要理解RBAC就要理解k8s中有幾種用戶,角色怎么控制權(quán)限,以及用戶和角色之間如何綁定的,下面我們就來(lái)一個(gè)個(gè)的看。
2.1 k8s中的用戶
在k8s中,用戶從宏觀上就可以分為兩種,集群內(nèi)的用戶以及集群外的用戶:
集群內(nèi)的用戶:serviceAccount 集群外的用戶:User
集群外的user就是能通過(guò)HTTP請(qǐng)求體中拿到, 對(duì)于集群內(nèi)的用戶認(rèn)證信息怎么拿到呢,我們來(lái)看個(gè)kube-system命名空間下面的coredns這個(gè)serviceAccount的定義,我們能看到它有一個(gè)secrets字段,這個(gè)字段的name字段就是指定的secrets的名字,也就是說(shuō)如果某個(gè)POD聲明使用了這個(gè)serviceAccount,就會(huì)把這個(gè)serviceAccount對(duì)應(yīng)的secrets掛載到POD里面,這個(gè)secrets對(duì)應(yīng)的定義在這里。在POD中掛載的目錄為:/var/run/secrets/kubernetes.io/serviceaccount/,我們可以使用下面命令查看這個(gè)pod有沒(méi)有訪問(wèn)某個(gè)api的權(quán)限:
kubectl exec -ti centosb -n helios-ns bash
CA_CERT=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
NAMESPACE=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)
curl --cacert $CA_CERT -H "Authorization: Bearer $TOKEN" "https://kubernetes.default.svc/api/v1/namespaces/$NAMESPACE/pods/"
在k8s中為了簡(jiǎn)化一系列用戶有相同權(quán)限的操作,提出了Group的的概念,就是能給一個(gè)組的成員綁定角色,所以Group是個(gè)邏輯的概念,是對(duì)一組用戶的抽象。這里我們先有個(gè)印象后面還會(huì)提及到。
2.2 RBAC中角色
角色就是一組權(quán)限的集合,我們可以看一下system:coredns這個(gè)角色的例子:yaml文件地址對(duì)于system:coredns這個(gè)集群級(jí)用戶來(lái)說(shuō),有對(duì)apiGroups為""下面endpoints、services、pod、namespaces的list和watch的權(quán)限。
2.3 RBAC的用法
k8s通過(guò)RBAC將權(quán)限的使用者和角色分離,提供四個(gè)新的資源,分為兩組,分別為:
rolebindings/roles:針對(duì)單個(gè)namespace下面的資源,比如說(shuō)endpoints、services等 clusterrolebindings/clusterroles:除了針對(duì)rolebindings/roles的功能外,還有集群級(jí)別的資源,比如說(shuō)namespace、pvc等
我們可以看一下system:coredns這個(gè)clusterrolebinding的yaml定義:yaml文件地址上述的ClusterRoleBinding就是將kube-system下面的coredns用戶(ServiceAccount)和system:coredns進(jìn)行綁定使之有對(duì)應(yīng)的權(quán)限。
現(xiàn)在對(duì)于RBAC的基本概念就解釋完了,其實(shí)RBAC還是很容易理解的,這里提出兩個(gè)問(wèn)題供讀者思考:
rolebindings能和clusterroles綁定么 clusterrolebindings能和roles綁定么
三、拋磚引玉:kube-proxy和集群通信的過(guò)程

3.1 啟動(dòng)前需要的手動(dòng)配置
創(chuàng)建證書簽名請(qǐng)求kube-proxy-csr.json 通過(guò)ca的證書、私鑰以及上一步的證書簽名請(qǐng)求生成kube-proxy的私鑰和證書
cfssl gencert -ca=/opt/k8s/work/ca.pem \
-ca-key=/opt/k8s/work/ca-key.pem \
-config=/opt/k8s/work/ca-config.json \
-profile=kubernetes kube-proxy-csr.json | cfssljson -bare kube-proxy
通過(guò)set-cluster設(shè)置集群信息(比如設(shè)置為kubernetes),放在kube-proxy.kubeconfig文件中(這個(gè)時(shí)候的kube-proxy.kubeconfig的內(nèi)容:kube-proxy1.kubeconfig) 設(shè)置訪問(wèn)集群的用戶為kube-proxy,放在kube-proxy.kubeconfig文件中(這個(gè)時(shí)候的kube-proxy.kubeconfig的內(nèi)容:kube-proxy2.kubeconfig) 創(chuàng)建上下文(將第4步和第5部的進(jìn)行綁定即,用kube-proxy去訪問(wèn)kubernetes集群),使用kube-proxy.kubeconfig文件中。(這個(gè)時(shí)候的kube-proxy.kubeconfig的內(nèi)容:kube-proxy3.kubeconfig) 在kube-proxy的配置文件中,配置訪問(wèn)apiserver的客戶端(clientConnection.kubeconfig) 使用第5步創(chuàng)建的上下文,這個(gè)時(shí)候的kube-proxy.kubeconfig文件內(nèi)容為最終狀態(tài)kube-proxy.kubeconfig
在集群中針對(duì)kube-proxy會(huì)有如下的RBAC規(guī)則:通過(guò)system:node-proxier這個(gè)CRB將用戶system:kube-proxy和ClusterRole進(jìn)行綁定.
3.2 啟動(dòng)之后
kube-proxy通過(guò)啟動(dòng)前生成的kube-proxy.kubeconfig和apiserver通信 apiserver通過(guò)內(nèi)置的RBAC判斷用戶權(quán)限 認(rèn)證和授權(quán)結(jié)束,可以通信
四、千呼萬(wàn)喚始出來(lái):kubele和集群通信的過(guò)程
kubelet和kube-proxy的區(qū)別就是,kube-proxy僅僅是作為和集群通信的庫(kù)戶端,但是kubelet既要做客戶端(和集群通信)又要做服務(wù)端(供apiserver收集pod的日志等)。
kube-proxy生成一份客戶端證書之后在各個(gè)node上是能通用的。kubelet的服務(wù)端證書中必須能表示這個(gè)node的身份(所以在kubelet證書里面有節(jié)點(diǎn)相關(guān)的CN信息)。
kubelet和kube-proxy有一些不相同的地方就是kubelet代表的是一個(gè)node節(jié)點(diǎn),所以他的證書要能唯一標(biāo)識(shí)(證書中的CN字段system:nodes:172.27.xxx.xxx)。
為了避免手動(dòng)為每個(gè)節(jié)點(diǎn)手動(dòng)創(chuàng)建個(gè)證書,所以k8s的1.4版本中引入了bootstrap(Add proposal for kubelet TLS bootstrap),bootstrap的目標(biāo)就是省去上述這么多手動(dòng)搞證書的步驟,把這個(gè)過(guò)程內(nèi)置在k8s里面,基于bootstrap的過(guò)程如下:
4.1 啟動(dòng)前的配置

4.1.1 生成token
因?yàn)樽铋_(kāi)始的kubelet是沒(méi)有證書的,這時(shí)候就要通過(guò)一個(gè)唯一的token的去和api-server通信,這個(gè)token的權(quán)限是比較低的。
token的格式為*[a-z0-9]{6}.[a-z0-9]{16}*(例如abcdef.0123456789abcdef)token的格式分為兩個(gè)部分tokenID和(.)secret
tokenID是public信息,比如是會(huì)作為用戶名system:bootstrap: secret只能給信任的第三方,作為認(rèn)證信息 可以通過(guò)kubeadm來(lái)簡(jiǎn)化這個(gè)流程
# /opt/k8s/bin/kubeadm token create \
> --description helios-test \
> --groups system:bootstrappers:k8s01 \
> --kubeconfig ~/.kube/config
379jgz.xc7l8qnzwrw10obr
4.1.2 創(chuàng)建secret
secrets有下面幾種類型:
Opaque:?jiǎn)渭兊耐ㄟ^(guò)把base64去encode密碼,安全性不高 kubernetes.io/dockerconfigjson:用來(lái)存儲(chǔ)私有docker registry的認(rèn)證信息,詳情見(jiàn)registry-secret-existing-credentials。 kubernetes.io/service-account-token:用來(lái)存儲(chǔ)注入給pod的認(rèn)證信息 bootstrap.kubernetes.io/token:專門用于bootstrap的
secret的格式:bootstrap-token-secret-format當(dāng)然這個(gè)secret有幾個(gè)要求:
必須在kube-system的命名空間下 比如以bootstrap-token- 開(kāi)頭 secret的類型必須是bootstrap.kubernetes.io/token 我們來(lái)驗(yàn)證一下:
# kubectl get secrets -n kube-system | grep bootstrap-token-
bootstrap-token-379jgz bootstrap.kubernetes.io/token 7 4h12m
bootstrap-token-379jgz這個(gè)secret的內(nèi)容為:bootstrap-token-secret其中usage-bootstrap-*用于說(shuō)明secret的目的:
usage-bootstrap-authentication:這個(gè)token能作為認(rèn)證token
auth-extra-groups:為該token的擴(kuò)展認(rèn)證:
echo -n "c3lzdGVtOmJvb3RzdHJhcHBlcnM6azhzMDE=" | base64 --decode
system:bootstrappers:k8s01
4.1.3 設(shè)置RBAC規(guī)則
授予kubelet創(chuàng)建CSR的權(quán)限: kubelet-bootstrap-crb、kubelet-bootstrap-cr
授予過(guò)期輪換client證書的權(quán)限:node-client-cert-renewal
授予過(guò)期輪換server證書的權(quán)限: node-server-cert-renewal
4.1.4 設(shè)置集群參數(shù)
kubectl config set-cluster kubernetes \
--certificate-authority=/etc/kubernetes/cert/ca.pem \
--embed-certs=true \
--server=${KUBE_APISERVER} \
--kubeconfig=kubelet-bootstrap-${node_name}.kubeconfig
4.1.5 根據(jù)第一步的token添加用戶
kubectl config set-credentials kubelet-bootstrap \
--token=${BOOTSTRAP_TOKEN} \
--kubeconfig=kubelet-bootstrap-${node_name}.kubeconfig
4.1.6 設(shè)置上下文參數(shù)
kubectl config set-context default \
--cluster=kubernetes \
--user=kubelet-bootstrap \
--kubeconfig=kubelet-bootstrap-${node_name}.kubeconfig
4.1.7 設(shè)置默認(rèn)上下文
kubectl config use-context default --kubeconfig=kubelet-bootstrap-${node_name}.kubeconfig
4.1.8 配置各個(gè)組件參數(shù)
設(shè)置kubelet的啟動(dòng)參數(shù):
--bootstrap-kubeconfig=/etc/kubernetes/kubelet-bootstrap.kubeconfig
--cert-dir=/etc/kubernetes/cert
--kubeconfig=/etc/kubernetes/kubelet.kubeconfig
因?yàn)閏omtroller-manager負(fù)責(zé)簽發(fā)證書,所有comtroller-manager的啟動(dòng)文件中要有CA的相關(guān)信息
--cluster-signing-cert-file="/var/lib/kubernetes/ca.pem"
--cluster-signing-key-file="/var/lib/kubernetes/ca-key.pem"
--experimental-cluster-signing-duration=8760h
4.2 啟動(dòng)過(guò)程

其實(shí)是先去找kubeconfg文件,如果找不到就會(huì)使用bootstrap-kubeconfig去開(kāi)始bootstrsp的過(guò)程:
4.2.1 第一次CSR的過(guò)程(申請(qǐng)client證書)
kubelet在node節(jié)點(diǎn)上生成clinet.key,然后把公鑰和bootstrap-kubeconfig發(fā)送出去 api-server對(duì)請(qǐng)求進(jìn)行認(rèn)證,通過(guò)后創(chuàng)建CSR api-server從bootstrap-kubeconfig文件中提取出token。 kube-system的ns下,尋找bootstrap-token- 的secret 用戶為system:bootstrappers: ,因?yàn)閷儆趕ystem:bootstrappers組下面,有創(chuàng)建CSR的權(quán)限 創(chuàng)建CSR controller-manager監(jiān)聽(tīng)到了有CSR,并且這個(gè)用戶有自動(dòng)approve的權(quán)限,就頒發(fā)證書 kubelet通過(guò)watch看到創(chuàng)建的CSR變?yōu)榱薸ssued狀態(tài),就通過(guò)拿CSR中status.certificate的字段,然后base64解碼變?yōu)楸镜匚募?/section>
第一次CSR之后,就已經(jīng)在kubelet的啟動(dòng)參數(shù)的--kubeconfig=路徑中生成了訪問(wèn)apiserver的kube-config文件,為kube-config我們可以看看證書中,用戶的名字(CN):
# cfssl certinfo -cert /etc/kubernetes/cert/kubelet-client-current.pem
{
"subject": {
"common_name": "system:node:k8s01",
"organization": "system:nodes",
"names": [
"system:nodes",
"system:node:k8s01"
]
},
...
}
現(xiàn)在的用戶就變?yōu)閟ystem:node:k8s01,組變?yōu)閟ystem:nodes了
4.2.2 第二次CSR的過(guò)程(申請(qǐng)server端證書)
用上述生成的kube-config文件去訪問(wèn)apiserver apiserver通過(guò)RBAC查看該用戶是否有創(chuàng)建CSR的權(quán)限(crb為:node-client-cert-renewal, approve-node-server-renewal-csr都給綁了) 出于安全問(wèn)題,因?yàn)檫@一步就相當(dāng)于注冊(cè)node的過(guò)程,所以要手動(dòng)apporve 真正給頒發(fā)證書的還是controller-manager kubelet通過(guò)watch看到創(chuàng)建的CSR變?yōu)榱薸ssued狀態(tài),就通過(guò)拿CSR中status.certificate的字段,然后base64解碼變?yōu)楸镜匚募?/section>
原文鏈接:https://github.com/helios741/myblog/tree/new/learn_go/src/2020/0104_k8s_component_communication
K8S 進(jìn)階訓(xùn)練營(yíng)
點(diǎn)擊屏末 | 閱讀原文 | 即刻學(xué)習(xí)

掃描二維碼獲取
更多云原生知識(shí)
k8s 技術(shù)圈

