使用kubectl訪問Kubernetes叢集時的身份驗證和授權
當我們使用kubeadm成功引導啟動(init)一個Kubernetes叢集的控制平面後,kubeadm會在init的輸出結果中給予我們下面這樣的“指示”:
`... ... Your Kubernetes master has initialized successfully! To start using your cluster, you need to run the following as a regular user: mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config ... ... `
kubeadm init在結尾處輸出的這些資訊是在告知我們如何配置kubeconfig檔案。按照上述命令配置後,master節點上的kubectl就可以直接使用$HOME/.kube/config的資訊訪問k8s cluster了。並且,通過這種配置方式,kubectl也擁有了整個叢集的管理員(root)許可權。
很多K8s初學者在這裡都會有疑問:當kubectl使用這種kubeconfig方式訪問叢集時,Kubernetes的kube-apiserver是如何對來自kubectl的訪問進行身份驗證(authentication)和授權(authorization)的呢?為什麼來自kubectl的請求擁有最高的管理員許可權呢?在本文中,我們就來分析說明一下這個過程。
一. Kubernetes API的訪問控制原理回顧
在《Kubernetes的安全設定》一文中我曾介紹過Kubernetes叢集的訪問許可權控制由kube-apiserver負責,kube-apiserver的訪問許可權控制由身份驗證(authentication)、授權(authorization)和准入控制(admission control)三步驟組成,這三步驟是按序進行的:
要想搞明白kubectl訪問Kubernetes叢集時的身份驗證和授權,就是要弄清kube-apiserver在進行身份驗證和授權兩個環節都做了什麼:
-
Authentication:即身份驗證,這個環節它面對的輸入是整個http request,它負責對來自client的請求進行身份校驗,支援的方法包括:client證書驗證(https雙向驗證)、basic auth、普通token以及jwt token(用於serviceaccount)。APIServer啟動時,可以指定一種Authentication方法,也可以指定多種方法。如果指定了多種方法,那麼APIServer將會逐個使用這些方法對客戶端請求進行驗證,只要請求資料通過其中一種方法的驗證,APIServer就會認為Authentication成功;在較新版本kubeadm引導啟動的k8s叢集的apiserver初始配置中,預設支援client證書驗證和serviceaccount兩種身份驗證方式。在這個環節,apiserver會通過client證書或http header中的欄位(比如serviceaccount的jwt token)來識別出請求的“使用者身份”,包括”user”、”group”等,這些資訊將在後面的authorization環節用到。
-
Authorization:授權。這個環節面對的輸入是http request context中的各種屬性,包括:user、group、request path(比如:/api/v1、/healthz、/version等)、request verb(比如:get、list、create等)。APIServer會將這些屬性值與事先配置好的訪問策略(access policy)相比較。APIServer支援多種authorization mode,包括Node、RBAC、Webhook等。APIServer啟動時,可以指定一種authorization mode,也可以指定多種authorization mode,如果是後者,只要Request通過了其中一種mode的授權,那麼該環節的最終結果就是授權成功。在較新版本kubeadm引導啟動的k8s叢集的apiserver初始配置中,authorization-mode的預設配置是”Node,RBAC”。Node授權器主要用於各個node上的kubelet訪問apiserver時使用的,其他一般均由RBAC授權器來授權。
RBAC,Role-Based Access Control即Role-Based Access Control,它使用”rbac.authorization.k8s.io”實現授權決策,允許管理員通過Kubernetes API動態配置策略。在RBAC API中,一個角色(Role)包含了一組許可權規則。Role有兩種:Role和ClusterRole。一個Role物件只能用於授予對某一單一名稱空間(namespace)中資源的訪問許可權。ClusterRole物件可以授予與Role物件相同的許可權,但由於它們屬於叢集範圍物件, 也可以使用它們授予對以下幾種資源的訪問許可權:
- 叢集範圍資源(例如節點,即node)
- 非資源型別endpoint(例如”/healthz”)
- 跨所有名稱空間的名稱空間範圍資源(例如所有名稱空間下的pod資源)
rolebinding,角色繫結則是定義了將一個角色的各種許可權授予一個或者一組使用者。 角色繫結包含了一組相關主體(即subject, 包括使用者——User、使用者組——Group、或者服務賬戶——Service Account)以及對被授予角色的引用。 在名稱空間中可以通過RoleBinding物件進行使用者授權,而叢集範圍的使用者授權則可以通過ClusterRoleBinding物件完成。
好了,有了上面這些知識基礎,要搞清楚kubectl訪問叢集的身份驗證和授權過程,我們只需要逐一解決下面的一些問題即可:
1、authencation中識別出了哪些http request context中的資訊? 2、authorization中RBAC authorizer找到的對應的rolebinding或clusterrolebinding是什麼? 3、對應的role或clusterrole的許可權規則?
二. 在身份驗證(authentication)識別出Group
我們先從kubectl使用的kubeconfig入手。kubectl使用的kubeconfig檔案實質上就是kubeadm init過程中生成的/etc/kubernetes/admin.conf,我們檢視一下該kubeconfig檔案的內容:
環境k8s 1.10.3:
# kubectl config view
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: REDACTED
server: https://172.16.66.101:6443
name: kubernetes
contexts:
- context:
cluster: kubernetes
user: kubernetes-admin
name: [email protected]
current-context: [email protected]
kind: Config
preferences: {}
users:
- name: kubernetes-admin
user:
client-certificate-data: REDACTED
client-key-data: REDACTED
關於kubeconfig檔案的解釋,可以在這裡自行腦補。在這些輸出資訊中,我們著重提取到兩個資訊:
user name: kubernetes-admin
client-certificate-date: XXXX
前面提到過apiserver的authentication支援通過tls client certificate、basic auth、token等方式對客戶端發起的請求進行身份校驗,從kubeconfig資訊來看,kubectl顯然在請求中使用了tls client certificate的方式,即客戶端的證書。另外我們知道Kubernetes是沒有user這種資源的,通過k8s API也無法建立user。那麼kubectl的身份資訊就應該“隱藏”在client-certificate的資料中,我們來檢視一下。
首先我們將 /etc/kubernetes/admin.conf中client-certificate-data的資料內容儲存到一個臨時檔案admin-client-certificate.txt中:
// admin-client-certificate.txt
LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUM4akNDQWRxZ0F3SUJBZ0lJZjJkVlJqbThFTFF3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB4T0RBMU1UUXdPREUzTVROYUZ3MHhPVEExTVRRd09ERTNNVGRhTURReApGekFWQmdOVkJBb1REbk41YzNSbGJUcHRZWE4wWlhKek1Sa3dGd1lEVlFRREV4QnJkV0psY201bGRHVnpMV0ZrCmJXbHVNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQXhCbjNqZHc4MGIxR2ZiNnMKdzJOcnFwTG90TVQ0bnlBZjJIaHFNclhqbk8rd25hSzFBSVRPdy8yMm1EajByd0l1SndkUUlqNS9CYUY2M3BQRQoxcFUwdmhJUFZLNG42Skk0ZG1Nem8vbFIzalpwR2VaVzF6ZFhhQ292dzljN2NsYmlIby9tRkc0eHF5dFZMZlg0Ci9TOG1GcDJBOVFjaWVKR0lvNVMwQlIzRlpsVTFQTTdEUmJMRFZWcTFQZHlOWTJHZnNiR3JIbEdnWHZXQUtDZC8KSDc5Z0FxVm9UWGpTSVdDVll1WWNvTHZkdlZYUVNJaVlscFhGUDFqQlFMdmNVN3ZycXRiMTJSbXJ4bnBrVzRwbApkR0VPWDJzTG1mWVo1VGlGcGtSd3oyR3hzbVd5UmJ0Nk91SVNKRkk2UlowcitSbjR5TURLUHJZbEVuZ0RWYzVLClBaNXptd0lEQVFBQm95Y3dKVEFPQmdOVkhROEJBZjhFQkFNQ0JhQXdFd1lEVlIwbEJBd3dDZ1lJS3dZQkJRVUgKQXdJd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dFQkFFWk5UdlR6Mk9nekNVZHZNRmJyaFBzcCttRDJ2UGpNUkN4aQozQmtBMTB2SUNPU2ZkeW1NbjhhdzBJYktZejJnUWJYcVVmcXpRbVFmYTNpZitRWUJrQis3N3pmc3Y5YW00RVAvCmU2VGc1MnRxVjJQN3MyZUY3dE5BZTIwR3lWNnlGbFExUVVXNS9NNE0rSk1sVitCVWJsOXlFeVFsRU51Y0tmK3UKVFB5S0tUVXR6dlVZcjVFM0VKa3Q4NEVRSU52dzJuUjJqTnZlWjFYV09saVVyS2ZqSEh0ZnZPL241NlVTdUk0dwp1MkxUbElDUmNqNGcrWldsSWplTUZrR3lQYkp5SkFRNjVQMnNHclptMWtsR0dIM216d081Q1AxeVpXdm9VampQCmp6U2pNQ0lhSy9mUjhlUkFKNnExdFQ2YkcyNkwrbmprS0NRRFdLcGpBV09hcHVST2Niaz0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
然後針對該檔案資料做base64解碼,得到client certificate檔案:
cat admin-client-certificate.txt | base64 -d > admin-client.crt
# cat admin-client.crt
-----BEGIN CERTIFICATE-----
MIIC8jCCAdqgAwIBAgIIf2dVRjm8ELQwDQYJKoZIhvcNAQELBQAwFTETMBEGA1UE
AxMKa3ViZXJuZXRlczAeFw0xODA1MTQwODE3MTNaFw0xOTA1MTQwODE3MTdaMDQx
FzAVBgNVBAoTDnN5c3RlbTptYXN0ZXJzMRkwFwYDVQQDExBrdWJlcm5ldGVzLWFk
bWluMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxBn3jdw80b1Gfb6s
w2NrqpLotMT4nyAf2HhqMrXjnO+wnaK1AITOw/22mDj0rwIuJwdQIj5/BaF63pPE
1pU0vhIPVK4n6JI4dmMzo/lR3jZpGeZW1zdXaCovw9c7clbiHo/mFG4xqytVLfX4
/S8mFp2A9QcieJGIo5S0BR3FZlU1PM7DRbLDVVq1PdyNY2GfsbGrHlGgXvWAKCd/
H79gAqVoTXjSIWCVYuYcoLvdvVXQSIiYlpXFP1jBQLvcU7vrqtb12RmrxnpkW4pl
dGEOX2sLmfYZ5TiFpkRwz2GxsmWyRbt6OuISJFI6RZ0r+Rn4yMDKPrYlEngDVc5K
PZ5zmwIDAQABoycwJTAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYBBQUH
AwIwDQYJKoZIhvcNAQELBQADggEBAEZNTvTz2OgzCUdvMFbrhPsp+mD2vPjMRCxi
3BkA10vICOSfdymMn8aw0IbKYz2gQbXqUfqzQmQfa3if+QYBkB+77zfsv9am4EP/
e6Tg52tqV2P7s2eF7tNAe20GyV6yFlQ1QUW5/M4M+JMlV+BUbl9yEyQlENucKf+u
TPyKKTUtzvUYr5E3EJkt84EQINvw2nR2jNveZ1XWOliUrKfjHHtfvO/n56USuI4w
u2LTlICRcj4g+ZWlIjeMFkGyPbJyJAQ65P2sGrZm1klGGH3mzwO5CP1yZWvoUjjP
jzSjMCIaK/fR8eRAJ6q1tT6bG26L+njkKCQDWKpjAWOapuROcbk=
-----END CERTIFICATE-----
檢視證書內容:
# openssl x509 -in ./admin-client.crt -text
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 9180400125522743476 (0x7f67554639bc10b4)
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN=kubernetes
Validity
Not Before: May 14 08:17:13 2018 GMT
Not After : May 14 08:17:17 2019 GMT
Subject: O=system:masters, CN=kubernetes-admin
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
... ...
從證書輸出的資訊中,我們看到了下面這行:
Subject: O=system:masters, CN=kubernetes-admin
k8s apiserver對kubectl的請求進行client certificate驗證(通過ca證書–client-ca-file=/etc/kubernetes/pki/ca.crt對其進行校驗),驗證通過後kube-apiserver會得到:group = system:masters的http上下文資訊,並傳給後續的authorizers。
三. 在授權(authorization)時根據Group確定所繫結的角色(Role)
kubeadm在init初始引導叢集啟動過程中,建立了許多default的role、clusterrole、rolebinding和clusterrolebinding,在k8s有關RBAC的官方文件中,我們看到下面一些default clusterrole列表:
其中第一個cluster-admin這個cluster role binding綁定了system:masters group,這和authentication環節傳遞過來的身份資訊不謀而合。沿著system:masters group對應的cluster-admin clusterrolebinding“追查”下去,真相就會浮出水面。
我們檢視一下這一binding:
# kubectl get clusterrolebinding/cluster-admin -n kube-system -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
annotations:
rbac.authorization.kubernetes.io/autoupdate: "true"
creationTimestamp: 2018-06-07T06:14:55Z
labels:
kubernetes.io/bootstrapping: rbac-defaults
name: cluster-admin
resourceVersion: "103"
selfLink: /apis/rbac.authorization.k8s.io/v1/clusterrolebindings/cluster-admin
uid: 18c89690-6a1a-11e8-a0e8-00163e0cd764
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: Group
name: system:masters
我們看到在kube-system名字空間中,一個名為cluster-admin的clusterrolebinding將cluster-admin cluster role與system:masters Group繫結到了一起,賦予了所有歸屬於system:masters Group中使用者cluster-admin角色所擁有的許可權。
我們再來檢視一下cluster-admin這個role的具體許可權資訊:
# kubectl get clusterrole/cluster-admin -n kube-system -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
annotations:
rbac.authorization.kubernetes.io/autoupdate: "true"
creationTimestamp: 2018-06-07T06:14:55Z
labels:
kubernetes.io/bootstrapping: rbac-defaults
name: cluster-admin
resourceVersion: "52"
selfLink: /apis/rbac.authorization.k8s.io/v1/clusterroles/cluster-admin
uid: 18abe535-6a1a-11e8-a0e8-00163e0cd764
rules:
- apiGroups:
- '*'
resources:
- '*'
verbs:
- '*'
- nonResourceURLs:
- '*'
verbs:
- '*'
從rules列表中來看,cluster-admin這個角色對所有resources、verbs、apiGroups均有無限制的操作許可權,即整個叢集的root許可權。於是kubectl的請求就可以操控和管理整個叢集了。
四. 小結
至此,我們應該明確了為什麼採用了admin.conf kubeconfig的kubectrl擁有root許可權了。下面是一幅示意圖,簡要總結了對kubectl訪問請求的身份驗證和授權過程: