前言
在進行二進制搭建K8S集羣前,我們需要梳理通最磨人的一個點,就是各種各樣的證書機制。這一步是在安裝配置kubernetes的所有步驟中最容易出錯也是最難排查問題的一步,而這卻剛好是第一步,萬事開頭難,不要因爲這點困難就望而卻步。
一共有多少證書?
官方文檔參考:https://kubernetes.io/docs/setup/certificates/
先從Etcd算起:
1、Etcd對外提供服務,要有一套etcd server證書
2、Etcd各節點之間進行通信,要有一套etcd peer證書
3、Kube-APIserver訪問Etcd,要有一套etcd client證書
再算kubernetes:
4、Kube-APIserver對外提供服務,要有一套kube-apiserver server證書
5、kube-scheduler、kube-controller-manager、kube-proxy、kubelet和其他可能用到的組件,需要訪問kube-APIserver,要有一套kube-APIserver client證書
6、kube-controller-manager要生成服務的service account,要有一對用來簽署service account的證書(CA證書)
7、kubelet對外提供服務,要有一套kubelet server證書
8、kube-APIserver需要訪問kubelet,要有一套kubelet client證書
加起來共8套,但是這裏的“套”的含義我們需要理解。
同一個套內的證書必須是用同一個CA籤署的,簽署不同套裏的證書的CA可以相同,也可以不同。例如,所有etcd server證書需要是同一個CA簽署的,所有的etcd peer證書也需要是同一個CA簽署的,而一個etcd server證書和一個etcd peer證書,完全可以是兩個CA機構簽署的,彼此沒有任何關係。這算兩套證書。
爲什麼同一個“套”內的證書必須是同一個CA簽署的
原因在驗證這些證書的一端。因爲在要驗證這些證書的一端,通常只能指定一個Root CA。這樣一來,被驗證的證書自然都需要是被這同一個Root CA對應的私鑰簽署,不然不能通過認證。
其實實際上,使用一套證書(都使用一套CA來簽署)一樣可以搭建出K8S,一樣可以上生產,但是理清這些證書的關係,在遇到因爲證書錯誤,請求被拒絕的現象的時候,不至於無從下手,而且如果沒有搞清證書之間的關係,在維護或者解決問題的時候,貿然更換了證書,弄不好會把整個系統搞癱。
TLS bootstrapping 簡化kubelet證書製作
Kubernetes1.4版本引入了一組簽署證書用的API。這組API的引入,使我們可以不用提前準備kubelet用到的證書。
每個kubelet用到的證書都是獨一無二的,因爲它要綁定各自的IP地址,於是需要給每個kubelet單獨製作證書,如果業務量很大的情況下,node節點會很多,這樣一來kubelet的數量也隨之增加,而且還會經常變動(增減Node)kubelet的證書製作就成爲一件很麻煩的事情。使用TLS bootstrapping就可以省事兒很多。
工作原理:Kubelet第一次啓動的時候,先用同一個bootstrap token作爲憑證。這個token已經被提前設置爲隸屬於用戶組system:bootstrappers,並且這個用戶組的權限也被限定爲只能用來申請證書。 用這個bootstrap token通過認證後,kubelet申請到屬於自己的兩套證書(kubelet server、kube-apiserver client for kubelet),申請成功後,再用屬於自己的證書做認證,從而擁有了kubelet應有的權限。這樣一來,就去掉了手動爲每個kubelet準備證書的過程,並且kubelet的證書還可以自動輪替更新
官方文檔參考:https://kubernetes.io/docs/tasks/tls/certificate-rotation/
kubelet證書爲何不同
這樣做是一個爲了審計,另一個爲了安全。 每個kubelet既是服務端(kube-apiserver需要訪問kubelet),也是客戶端(kubelet需要訪問kube-apiserver),所以要有服務端和客戶端兩組證書。
服務端證書需要與服務器地址綁定,每個kubelet的地址都不相同,即使綁定域名也是綁定不同的域名,故服務端地址不同
客戶端證書也不應相同,每個kubelet的認證證書與所在機器的IP綁定後,可以防止一個kubelet的認證證書泄露以後,使從另外的機器上僞造的請求通過驗證。
安全方面,如果每個node上保留了用於簽署證書的bootstrap token,那麼bootstrap token泄漏以後,是不是可以隨意簽署證書了?安全隱患非常大。所以,kubelet啓動成功以後,本地的bootstrap token需要被刪除。
正式製作證書
雖然可以用多套證書,但是維護多套CA實在過於繁雜,這裏還是用一個CA簽署所有證書。
需要準備的證書:
admin-key.pem
admin.pem
ca-key.pem
ca.pem
kube-proxy-key.pem
kube-proxy.pem
kubernetes-key.pem
kubernetes.pem
使用證書的組件如下:
etcd:使用 ca.pem、kubernetes-key.pem、kubernetes.pem
kube-apiserver:使用 ca.pem、kubernetes-key.pem、kubernetes.pem
kubelet:使用 ca.pem
kube-proxy:使用 ca.pem、kube-proxy-key.pem、kube-proxy.pem
kubectl:使用 ca.pem、admin-key.pem、admin.pem
kube-controller-manager:使用 ca-key.pem、ca.pem
我們使用CFSSL來製作證書,它是cloudflare開發的一個開源的PKI工具,是一個完備的CA服務系統,可以簽署、撤銷證書等,覆蓋了一個證書的整個生命週期,後面只用到了它的命令行工具。
注:一般情況下,K8S中證書只需要創建一次,以後在向集羣中添加新節點時只要將/etc/kubernetes/ssl目錄下的證書拷貝到新節點上即可。
下載安裝cfssl命令行工具
[root@dong bin]# mkdir -p /usr/local/bin/cfssl [root@dong bin]# wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64 [root@dong bin]# wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64 [root@dong bin]# wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64 [root@dong bin]# chmod +x cfssl* [root@dong bin]# mv cfssl_linux-amd64 /usr/local/bin/cfssl [root@dong bin]# mv cfssljson_linux-amd64 /usr/local/bin/cfssljson [root@dong bin]# mv cfssl-certinfo_linux-amd64 /usr/local/bin/cfssl-certinfo [root@dong bin]# export PATH=/usr/local/bin:$PATH
創建CA證書
創建存放證書目錄
[root@dong bin]# mkdir -p /opt/kubernetes/ssl/ [root@dong bin]# cd /opt/kubernetes/ssl/
創建證書配置文件
[root@dong ssl]# vim ca-config.json { "signing": { "default": { "expiry": "87600h" }, "profiles": { "kubernetes": { "usages": [ "signing", "key encipherment", "server auth", "client auth" ], "expiry": "87600h" } } }
字段說明:
ca-config.json:可以定義多個 profiles,分別指定不同的過期時間、使用場景等參數;後續在簽名證書時使用某個 profile;
signing:表示該證書可以簽名其他證書;生成的ca.pem證書中 CA=TRUE;
server auth:表示client可以用該 CA 對server提供的證書進行驗證;
client auth:表示server可以用該CA對client提供的證書進行驗證;
expiry:過期時間
創建CA證書籤名請求文件
[root@dong ssl]# vim ca-csr.json { "CN": "kubernetes", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "ST": "BeiJing", "L": "BeiJing", "O": "k8s", "OU": "System" } ], "ca": { "expiry": "87600h" } }
字段說明:
“CN”:Common Name,kube-apiserver 從證書中提取該字段作爲請求的用戶名 (User Name);瀏覽器使用該字段驗證網站是否合法;
“O”:Organization,kube-apiserver 從證書中提取該字段作爲請求用戶所屬的組 (Group);
生成CA證書和私鑰
[root@dong ssl]# cfssl gencert -initca ca-csr.json | cfssljson -bare ca [root@dong ssl]# ls | grep ca ca-config.json ca.csr ca-csr.json ca-key.pem ca.pem
其中ca-key.pem是ca的私鑰,ca.csr是一個簽署請求,ca.pem是CA證書,是後面kubernetes組件會用到的RootCA。
創建kubernetes證書
創建kubernetes證書籤名請求文件 kubernetes-csr.json
[root@dong ssl]# vim kubernetes-csr.json { "CN": "kubernetes", "hosts": [ "127.0.0.1", "192.168.214.88", "192.168.214.89", "192.168.214.200", "192.168.214.201", "10.254.0.1", "192.168.214.210", "kubernetes", "kube-api.wangdong.com", "kubernetes.default", "kubernetes.default.svc", "kubernetes.default.svc.cluster", "kubernetes.default.svc.cluster.local" ], "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "ST": "BeiJing", "L": "BeiJing", "O": "k8s", "OU": "System" } ] }
字段說明:
如果 hosts 字段不爲空則需要指定授權使用該證書的 IP 或域名列表。
由於該證書後續被 etcd 集羣和 kubernetes master使用,將etcd、master節點的IP都填上,同時還有service網絡的首IP。(一般是 kube-apiserver 指定的 service-cluster-ip-range 網段的第一個IP,如 10.254.0.1)
我這裏的設置包括一個私有鏡像倉庫,兩個etcd,兩個master,以上物理節點的IP也可以更換爲主機名。
生成kubernetes證書和私鑰
[root@dong ssl]# cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kubernetes-csr.json | cfssljson -bare kubernetes [root@dong ssl]# ls |grep kubernetes kubernetes.csr kubernetes-csr.json kubernetes-key.pem kubernetes.pem
創建admin證書
創建admin證書籤名請求文件admin-csr.json
[root@dong ssl]# admin-csr.json { "CN": "admin", "hosts": [], "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "ST": "BeiJing", "L": "BeiJing", "O": "system:masters", "OU": "System" } ] }
說明:
後續 kube-apiserver 使用 RBAC 對客戶端(如 kubelet、kube-proxy、Pod)請求進行授權;
kube-apiserver 預定義了一些 RBAC 使用的 RoleBindings,如 cluster-admin 將 Group system:masters 與 Role cluster-admin 綁定,該 Role 授予了調用kube-apiserver 的所有 API的權限;
O指定該證書的 Group 爲 system:masters,kubelet 使用該證書訪問 kube-apiserver 時 ,由於證書被 CA 簽名,所以認證通過,同時由於證書用戶組爲經過預授權的 system:masters,所以被授予訪問所有 API 的權限;
注:這個admin 證書,是將來生成管理員用的kube config 配置文件用的,現在我們一般建議使用RBAC 來對kubernetes 進行角色權限控制, kubernetes 將證書中的CN 字段 作爲User, O 字段作爲 Group
生成admin證書和私鑰
[root@dong ssl]# cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes admin-csr.json | cfssljson -bare admin [root@dong ssl]# ls | grep admin admin.csr admin-csr.json admin-key.pem admin.pem
創建kube-proxy證書
創建 kube-proxy 證書籤名請求文件 kube-proxy-csr.json
[root@dong ssl]# vim kube-proxy-csr.json { "CN": "system:kube-proxy", "hosts": [], "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "CN", "ST": "BeiJing", "L": "BeiJing", "O": "k8s", "OU": "System" } ] }
說明:
CN 指定該證書的 User 爲 system:kube-proxy;
kube-apiserver 預定義的 RoleBinding system:node-proxier 將User system:kube-proxy 與 Role system:node-proxier 綁定,該 Role 授予了調用 kube-apiserver Proxy 相關 API 的權限;
生成kube-proxy證書和私鑰
[root@dong ssl]# cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes kube-proxy-csr.json | cfssljson -bare kube-proxy [root@dong ssl]# ls |grep kube-proxy kube-proxy.csr kube-proxy-csr.json kube-proxy-key.pem kube-proxy.pem
經過上述操作,我們會用到如下文件:
[root@dong ssl]# ls | grep pem admin-key.pem admin.pem ca-key.pem ca.pem kube-proxy-key.pem kube-proxy.pem kubernetes-key.pem kubernetes.pem
在搭建k8s集羣的時候,將這些文件分發到至此集羣中其他節點機器中即可。至此,TLS證書創建完畢。