《深刻淺出 Kubernetes 安全機制》最先發布在 blog.hdls.me/15432142284…html
在 Kubernetes 中,全部資源的訪問和變動都是圍繞 APIServer 展開的。好比說 kubectl 命令、客戶端 HTTP RESTFUL 請求,都是去 call APIServer 的 API 進行的,本文就重點解讀 k8s 爲了集羣安全,都作了些什麼。node
首先,Kubernetes 官方文檔給出了上面這張圖。描述了用戶在訪問或變動資源的以前,須要通過 APIServer 的認證機制、受權機制以及准入控制機制。這三個機制能夠這樣理解,先檢查是否合法用戶,再檢查該請求的行爲是否有權限,最後作進一步的驗證或添加默認參數。web
Kubernetes 中有兩種用戶,一種是內置「用戶」 ServiceAccount,另外一種我稱之爲天然人。json
所謂天然人就是指區別於 pod 等資源概念的「人」,能夠理解成實際操做 "kubectl" 命令的人。admin 能夠分發私鑰,但天然人能夠儲存相似 KeyStone 甚至包含帳號密碼的文件,因此 k8s 中沒有對天然人以 API 對象描述之。bootstrap
在典型的 Kubernetes 集羣中,API 一般服務在 443 端口,APIServer 提供自簽名證書。當你使用 kube-up.sh
建立集羣用戶時,證書會自動在 $USER/.kube/config
中建立出來,然後續用 kubectl
命令訪問 APIServer 時,都是用這個證書。api
與之相反,k8s 中以 API 對象的形式描述和管理 ServiceAccount。它們被綁定在某個具體的 namespace 中,能夠由 APIServer 自動建立出來或手動 call k8s API。安全
k8s 中的認證機制,是在用戶訪問 APIServer 的第一步。一般是一個完整的 HTTP 請求打過來,可是這一步每每只檢測請求頭或客戶端證書。bash
認證機制目前有客戶端證書、bearer tokens、authenticating proxy、HTTP basic auth 這幾種模式。使用方式一般有如下幾種:ide
X509 Client Certs: 客戶端證書模式須要在 kubectl
命令中加入 --client-ca-file=<SOMEFILE>
參數,指明證書所在位置。ui
Static Token File: --token-auth-file=<SOMEFILE>
參數指明 bearer tokens
所在位置。
bearer tokens: 在 HTTP 請求頭中加入 Authorization: Bearer <TOKEN>
。
Bootstrap Tokens: 與 bearer tokens
一致,但 TOKEN 格式爲 [a-z0-9]{6}.[a-z0-9]{16}
。該方式稱爲 dynamically-managed Bearer token,以 secret
的方式保存在 kube-system namespace
中,能夠被動態的建立和管理。同時,啓用這種方式還須要在 APIServer 中打開 --enable-bootstrap-token-auth
,這種方式還處於 alpha 階段。
Static Password File: 以參數 --basic-auth-file=<SOMEFILE>
指明 basic auth file 的位置。這個 basic auth file 以 csv 文件的形式存在,裏面至少包含三個信息:password、username、user id,同時該模式在使用時須要在請求頭中加入 Authorization: Basic BASE64ENCODED(USER:PASSWORD)
。
Service Account Tokens: 該方式一般被 pod 所使用,在 PodSpec
中指明 ServiceAccount 來訪問 ApiServer。
除了以上列出來的幾種方式外,還有一些比較特殊的訪問方式,這裏再也不詳細解讀。
當用戶經過認證後,k8s 的受權機制將對用戶的行爲等進行受權檢查。換句話說,就是對這個請求自己,是否對某資源、某 namespace、某操做有權限限制。
受權機制目前有 4 種模式:RBAC、ABAC、Node、Webhook。下面對這 4 種模式分別作分析。
Role-based access control (RBAC) 是基於角色的權限訪問控制,一般是對於「內置用戶」而言的。該模式是在 k8s v1.6 開發出來的。若要開啓該模式,須要在 APIServer 啓動時,設置參數 --authorization-mode=RBAC
。
RBAC 所使用的 API Group 是 rbac.authorization.k8s.io/v1beta1
,直到 Kubernetes v1.8 後,RBAC 模塊達到穩定水平,所使用的 API Group 爲 rbac.authorization.k8s.io/v1
。
所謂基於角色的權限訪問控制,就是對某個用戶賦予某個角色,而這個角色一般決定了對哪些資源擁有怎樣的權限。
首先來看看這個 「內置用戶」,在大多時候咱們都不使用 「天然人」 這個功能,而是使用 ServiceAccount,再對其餘資源授予某個 ServiceAccount,就使得其可以以 「內置用戶」 的身份去訪問 APIServer。
建立一個 ServiceAccount 很簡單,只須要指定其所在 namespace 和 name 便可。舉個例子:
apiVersion: v1
kind: ServiceAccount
metadata:
namespace: hdls
name: hdls-sa
複製代碼
RBAC 中最重要的概念就是 Role
和 RoleBinding
。Role
定義了一組對 Kubernetes API 對象的操做權限,而 RoleBinding
則定義的是具體的 ServiceAccount 和 Role 的對應關係。
舉個 Role 的例子以下:
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: hdls
name: hdls-role
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
複製代碼
其中: namespace: 在這裏僅限於邏輯上的「隔離」,並不會提供任何實際的隔離或者多租戶能力; rules:定義的是權限規則,容許「被做用者」,對 hdls 下面的 Pod 對象,進行 GET 和 LIST 操做; apiGroups:爲 "" 表明 core API Group; resources:指的是資源類型,對此還能夠進行詳細的劃分,指定能夠操做的資源的名字,好比:
rules:
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["my-config"]
verbs: ["get"]
複製代碼
verbs: 指的是具體的操做,當前 Kubernetes(v1.11)裏可以對 API 對象進行的全部操做有 "get", "list", "watch", "create", "update", "patch", "delete"。
再看 RoleBinding 的例子:
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: hdls-rolebinding
namespace: hdls
subjects:
- kind: ServiceAccount
name: hdls-sa
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: hdls-role
apiGroup: rbac.authorization.k8s.io
複製代碼
能夠看到,這個 RoleBinding 對象裏定義了一個 subjects 字段,即「被做用者」。它的類型是 ServiceAccount,就是上面建立的 sa。這個 subjects 還能夠是 User 和 Group,User 是指 k8s 裏的用戶,而 Group 是指 ServiceAccounts。
roleRef
字段是用來直接經過名字,引用咱們前面定義的 Role 對象(hdls-role),從而定義了 Subject 和 Role 之間的綁定關係。
此時,咱們再用 kubectl get sa -n hdls -o yaml
命令查看以前的 ServiceAccount
,就能夠看到 ServiceAccount.secret
,這是由於 k8s 會爲一個 ServiceAccount
自動建立並分配一個 Secret
對象,而這個 Secret
就是用來跟 APIServer 進行交互的受權文件: Token
。Token
文件的內容通常是證書或者密碼,以一個 Secret
對象的方式保存在 etcd 當中。
這個時候,咱們在咱們的 Pod 的 YAML 文件中定義字段 .spec.serviceAccountName
爲上面的 ServiceAccount name
便可聲明使用。
若是一個 Pod 沒有聲明 serviceAccountName
,Kubernetes 會自動在它的 Namespace 下建立一個名叫 default
的默認 ServiceAccount
,而後分配給這個 Pod。然而這個默認 ServiceAccount 並無關聯任何 Role。也就是說,此時它有訪問 APIServer 的絕大多數權限。
須要注意的是 Role 和 RoleBinding 對象都是 Namespaced 對象,它們只對本身的 Namespace 內的資源有效。
而某個 Role 須要對於非 Namespaced 對象(好比:Node),或者想要做用於全部的 Namespace 的時候,咱們須要使用 ClusterRole 和 ClusterRoleBinding 去作受權。
這兩個 API 對象的用法跟 Role 和 RoleBinding 徹底同樣。只不過,它們的定義裏,沒有了 Namespace 字段。
值得一提的是,Kubernetes 已經內置了不少個爲系統保留的 ClusterRole
,它們的名字都以 system:
開頭。通常來講,這些系統級別的 ClusterRole
,是綁定給 Kubernetes 系統組件對應的 ServiceAccount
使用的。
除此以外,Kubernetes 還提供了四個內置的 ClusterRole
來供用戶直接使用:
cluster-admin:整個集羣的最高權限。若是在 ClusterRoleBinding
中使用,意味着在這個集羣中的全部 namespace 中的全部資源都擁有最高權限,隨心所欲;若是在 RoleBinding
中使用,即在某個 namespace 中隨心所欲。
admin:管理員權限。若是在 RoleBinding
中使用,意味着在某個 namespace 中,對大部分資源擁有讀寫權限,包括建立 Role 和 RoleBinding 的權限,但沒有對資源 quota 和 namespace 自己的寫權限。
edit:寫權限。在某個 namespace 中,擁有對大部分資源的讀寫權限,但沒有對 Role 和 RoleBinding 的讀寫權限。
view:讀權限。在某個 namespace 中,僅擁有對大部分資源的讀權限,沒有對 Role 和 RoleBinding 的讀權限,也沒有對 seccrets 的讀權限。
在 Kubernetes v1.9 以後,ClusterRole
有一種新的定義方法,就是使用 aggregationRule
將多個 ClusterRole
合成一個新的 ClusterRole
。
首先看個 k8s 官網的例子:
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: monitoring
aggregationRule:
clusterRoleSelectors:
- matchLabels:
rbac.example.com/aggregate-to-monitoring: "true"
rules: []
複製代碼
其中 rules
字段沒必要定義,會被 controller manager
自動填充。
能夠看出 aggregationRule
就是將全部知足 label
條件的 ClusterRole
的合成一個 ClusterRole
,而這個新的 ClusterRole
權限爲其餘總和。
相對於 User 而言,k8s 還擁有「用戶組」(Group)的概念,也就是一組「用戶」的意思。而對於「內置用戶」 ServiceAccount 來講,「用戶組」的概念也一樣適用。
實際上,一個 ServiceAccount,在 Kubernetes 裏對應的「用戶」的名字是: system:serviceaccount:<ServiceAccount 名字 >
;而它對應的內置「用戶組」的名字,就是 system:serviceaccounts:<Namespace 名字 >
。
對於 Group 的運用,咱們舉個例子,在 RoleBinding 裏這樣定義 subjects:
subjects:
- kind: Group
name: system:serviceaccounts:hdls
apiGroup: rbac.authorization.k8s.io
複製代碼
這就意味着這個 Role 的權限規則,做用於 hdls 裏的全部 ServiceAccount。
而若是 Group 不指定 Namespace,即直接定義爲 system:serviceaccounts
,意味着做用於整個系統裏的全部 ServiceAccount。
Attribute-based access control (ABAC) 是基於屬性的權限訪問控制。若要開啓該模式,須要在 APIServer 啓動時,開啓 --authorization-policy-file=<SOME_FILENAME>
和 --authorization-mode=ABAC
兩個參數。
其 policy 文件用來指定權限規則,必須知足每行都是一個 json 對象的格式。能夠指定 user 或 group 爲某個特定的對象,並描述其擁有的權限。
與 Yaml 文件一致,必須描述的屬性有 apiVersion、kind、spec,而 spec 裏描述了具體的用戶、資源和行爲。看個例子:
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user": "bob", "namespace": "projectCaribou", "resource": "pods", "readonly": true}}
複製代碼
這就描述了用戶 bob 只有在 namespace projectCaribou 下對 pod 的讀權限。相似的,這個 User 能夠是某我的,也能夠是 kubelet 或者某個 ServiceAccount,這裏 ServiceAccount 須要寫全,好比:system:serviceaccount:kube-system:default
。
若是是描述某個 namespace 下的全部人,須要用到 group,好比:
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"group": "system:serviceaccounts:default", "readonly": true, "resource": "pods"}}
複製代碼
Node 受權機制是一種特殊的模式,是 kubelet
發起的請求受權。開啓該模式,須要開啓參數 --authorization-mode=Node
。
經過啓動 --enable-admission-plugins=...,NodeRestriction,...
,來限制 kubelet 訪問 node,endpoint、pod、service以及secret、configmap、PV 和 PVC 等相關的資源。
Webhook 模式是一種 HTTP 回調模式,是一種經過 HTTP POST
方式實現的簡單事件通知。該模式須要 APIServer 配置參數 –authorization-webhook-config-file=<SOME_FILENAME>
,HTTP 配置文件的格式跟 kubeconfig 的格式相似。
# Kubernetes API version
apiVersion: v1
# kind of the API object
kind: Config
# clusters refers to the remote service.
clusters:
- name: name-of-remote-authz-service
cluster:
# CA for verifying the remote service.
certificate-authority: /path/to/ca.pem
# URL of remote service to query. Must use 'https'. May not include parameters.
server: https://authz.example.com/authorize
# users refers to the API Server's webhook configuration.
users:
- name: name-of-api-server
user:
client-certificate: /path/to/cert.pem # cert for the webhook plugin to use
client-key: /path/to/key.pem # key matching the cert
# kubeconfig files require a context. Provide one for the API Server.
current-context: webhook
contexts:
- context:
cluster: name-of-remote-authz-service
user: name-of-api-server
name: webhook
複製代碼
其中,Cluster 指須要回調的地方的客戶端,指定其訪問證書和 URL;user 指回調處訪問的身份,指明其所需證書和 key;contexts 指回調的內容。
在一個請求經過了認證機制和受權認證後,須要通過最後一層篩查,即准入控制。這個准入控制模塊的代碼一般在 APIServer 中,並被編譯到二進制文件中被執行。這一層安全檢查的意義在於,檢查該請求是否達到系統的門檻,便是否知足系統的默認設置,並添加默認參數。
准入控制以插件的形式存在,開啓的方式爲: kube-apiserver --enable-admission-plugins=NamespaceLifecycle,LimitRanger ...
關閉的方式爲: kube-apiserver --disable-admission-plugins=PodNodeSelector,AlwaysDeny ...
經常使用的准入控制插件有:
PersistentVolumeClaim
建立默認的 PV;node.kubernetes.io/not-ready:NoExecute
和 node.alpha.kubernetes.io/unreachable:NoExecute
沒有容忍,爲其建立默認的 5 分鐘容忍 notready:NoExecute
和unreachable:NoExecute
;priorityClassName
來決定優先級;以上只列舉了部分,詳情請移步 Kubernetes 官方文檔。
官方建議:
--enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota
複製代碼
--admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota
複製代碼
--admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,DefaultStorageClass,ResourceQuota,DefaultTolerationSeconds
複製代碼
--admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota
複製代碼