Kubernetes筆記(9) - 認證、授權與准入控制
訪問控制概述
API Server作為Kubernetes集群系統的閘道器,是訪問及管理資源物件的唯一入口,其餘所有需要訪問叢集資源的元件,包括kube-controller-manager、kube-scheduler、kubelet和kube-proxy等叢集基礎元件、CoreDNS等叢集的附加元件以及此前使用的kubectl命令等都要經由此閘道器進行叢集訪問和管理。
API Server會對每一次的訪問請求進行合法性檢驗,包括使用者身份鑑別、操作許可權驗證以及操作是否符合全域性規範的約束等。
Kubernetes以外掛的方式進行對請求的認證、授權:通過認證外掛來識別客戶端身份;然後通過一到多個授權外掛來檢查使用者是否有許可權執行其發出的資源操作請求;最後還要通過一到多個准入控制外掛的遍歷檢測,例如檢查目標Namespace資源物件是否存在、是否違反系統資源限制等等。
使用者賬號與服務賬號
認證外掛從客戶端請求中提取出的使用者及所屬組的資訊並不會被儲存,它們僅僅用於檢驗使用者是否有許可權執行其所請求的操作。客戶端訪問API服務的途徑通常有三種:kubectl、客戶端庫或者直接使用REST介面進行請求,而可以執行此類請求的主體也被Kubernetes分為兩類:
- Pod物件,對應Service Account(服務賬號),是指由Kubernetes API管理的賬號,用於為Pod之中的服務程序在訪問Kubernetes API時提供身份標識(identity)。Service Account通常要綁定於特定的名稱空間,它們由API Server建立,或者通過API呼叫手動建立,附帶著一組儲存為Secret的用於訪問API Server的憑據。
- 操作人員,對應User Account(使用者賬號),是指由獨立於Kubernetes之外的其他服務管理的使用者賬號,例如由管理員分發的金鑰。
使用者組
User Account用於人工操控,作用於系統全域性,其名稱必須全域性唯一;Service Account隸屬於名稱空間,僅用於實現某些特定的操作任務,更輕量。這兩類賬號都可以隸屬於一個或多個使用者組。使用者組只是使用者賬號的邏輯集合,它本身並沒有操作許可權,但附加於組上的許可權可由其內部的所有使用者繼承,以實現高效的授權管理機制。
Kubernetes內建了一些特殊用途的組:
- system:unauthenticated,未能通過任何一個授權外掛檢驗的賬號,即未通過認證測試的使用者所屬的組。
- system:authenticated,認證成功後的使用者自動加入的一個組,用於快捷引用所有正常通過認證的使用者賬號。
- system:serviceaccounts,當前系統上的所有ServiceAccount物件。
- system:serviceaccounts:(namespace) 特定名稱空間內所有的Service Account物件。
認證
身份驗證外掛負責對API請求的認證,支援的認證方式有:客戶端證書、承載令牌(bearer tokens)、 身份驗證代理(authenticating proxy)、HTTP basic認證等。
API Server接收到訪問請求時,它將呼叫認證外掛嘗試提取如下資訊:
- Username:使用者名稱,如kubernetes-admin等。
- UID:使用者的數字標籤符,用於確保使用者身份的唯一性。
- Groups:使用者所屬的組,用於許可權指派和繼承。
- Extra:鍵值資料型別的字串,用於提供認證時需要用到的額外資訊。
API Server支援同時啟用多種認證機制,但至少應該分別為ServiceAccount和User Account各自啟用一個認證外掛。同時啟用多種認證機制時,認證過程會以序列的方式進行,直到一種認證機制成功完成即結束。
授權
成功通過身份認證後的操作請求還需要轉交給授權外掛進行許可許可權檢查,以確保其擁有執行相應的操作的許可。主要支援使用四類內建的授權外掛:
- Node:基於Pod資源的目標排程節點來實現的對kubelet的訪問控制。
- ABAC:attribute-based access control,基於屬性的訪問控制。
- RBAC:role-based access control,基於角色的訪問控制。
- Webhook:基於HTTP回撥機制通過外部REST服務檢查確認使用者授權的訪問控制。
此外還有AlwaysDeny和AlwaysAllow兩個特殊的授權外掛,其中AlwaysDeny僅用於測試,而AlwaysAllow則用於關閉授權檢查。
Service Account的管理與應用
API Server是訪問及管理資源物件的唯一入口,它會對每一次訪問請求進行合法性檢驗。每個Pod物件都會對應一個Service Account,若建立Pod資源時未予以明確指定,則名為ServiceAccount的准入控制器會為其自動附加當前名稱空間中預設的服務賬戶,其名稱通常為default:
✗ kubectl describe serviceaccount default
Namespace: default
...
Mountable secrets: default-token-m6bz5
Tokens: default-token-m6bz5
Pod通過儲存卷掛載的方式使用這些資訊,位置在/var/run/secrets/kubernetes.io/serviceaccount
✗ kubectl describe pod configmap-vol
...
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from default-token-m6bz5 (ro)
...
Volumes:
default-token-m6bz5:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-m6bz5
Optional: false
掛載點目錄中存在三個檔案:ca.crt、namespace和token,其中,token檔案儲存了Service Account的認證token,容器中的程序使用它向API Server發起連線請求,進而由認證外掛完成使用者認證並將其使用者名稱傳遞給授權外掛。
Service Account自動化
Kubernetes系統通過三個獨立的元件間的相互協作來實現服務賬戶的自動化,三個元件具體為:Service Account准入控制器、令牌控制器(token controller)和Service Account賬戶控制器。
ServiceAccount賬戶控制器負責為名稱空間管理相應的資源,並確保每個名稱空間中都存在一個名為“default”的Service Account物件。
ServiceAccount准入控制器是API Server的一部分,負責在建立或更新Pod時對其按需進行Service Account物件相關資訊的修改,包括如下操作:
- 若Pod沒有明確定義使用的ServiceAccount物件,則將其設定為“default”
- 確保Pod明確引用的ServiceAccount已存在,否則請求將被拒絕
- 若Pod物件中不包含ImagePullSecerts,則把ServiceAccount的ImagePullSecrets添加於其上
- 為帶有訪問API的令牌的Pod新增一個儲存卷
- 為Pod物件中的每個容器新增一個volumeMounts,掛載至/var/run/secrets/kubernetes.io/serviceaccount
令牌控制器是controller-manager的子元件,工作於非同步模式。其負責的任務有:
- 監控Service Account的建立操作,併為其新增用於訪問API的Secret物件
- 監控Service Account的刪除操作,並刪除其相關的所有Service Account令牌金鑰
- 監控Secret物件的新增操作,確保其引用的Service Account已存在,並在必要時為Secret物件新增認證令牌
- 監控Secret物件的刪除操作,以確保刪除每個ServiceAccount中對此Secret的引用。
建立服務賬戶
Service Account是Kubernetes API上的一種資源型別,它隸屬於名稱空間,每個名稱空間都有一個名為default的預設資源物件,可通過kubectl get serviceaccounts --all-namespaces
檢視。
每個Pod物件只能附加其所屬名稱空間中的一個Service Account資源,但一個Service Account資源可由多個Pod物件共享使用。建立Pod資源時,可使用“spec.serviceAccountName”屬性直接指定要使用的Service Account物件,如果省略則使用預設資源物件。
下面的配置清單建立名為sa-demo的服務賬戶,其餘的資訊則交由系統自動生成,包括認證令牌的Secret物件:
apiVersion: v1
kind: ServiceAccount
metadata:
name: sa-demo
呼叫imagePullSecret資源物件
ServiceAccount資源還可以基於spec.imagePullSecret欄位附帶一組docker-registry資源列表,用於在進行容器建立時,從某私有映象倉庫下載映象檔案之前進行服務認證。比如下面的清單指定了在Kubernetes筆記(8) - ConfigMap和Secret中建立的local-registry:
apiVersion: v1
kind: ServiceAccount
metadata:
name: sa-demo
imagePullSecrets:
- name: local-registry
RBAC:基於角色的訪問控制
在RBAC訪問控制機制中,User是一個可以獨立訪問計算機系統中的資料或者用資料表示的其他資源的主體;Role是指一個組織或任務中的工作或者位置,它代表了一種權利、資格和責任;Permission是允許執行的操作。
K8s使用的RBAC授權外掛支援Role和ClusterRole兩類角色,其中Role作用於名稱空間級別,用於定義名稱空間內的資源許可權集合,而ClusterRole則用於組織叢集級別的資源許可權集合,它們都是標準的API資源型別。
Role和RoleBinding
Role僅是一組許可(permission)許可權的集合,它描述了對哪些資源可執行何種操作,資源配置清單中使用rules欄位巢狀授權規則,下面的Role物件的配置清單示例,設定了讀取、列出及監視Pod物件的許可權:
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pods-reader
rules:
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list", "watch"]
沒有指定namespace,預設建立到了default下,role需要“繫結”(Role-Binding)到主體(如user、group或service account)之上才能發生作用。
RoleBinding用於將Role中定義的許可權賦予一個或一組使用者,它由一組主體,以及一個要引用來賦予這組主體的Role或ClusterRole組成,比如下面的RoleBinding配置清單將pods-reader角色賦給了使用者kube-user1,於是kube-user1就有了對應的許可權:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: role-binding-1
subjects:
- kind: User
name: kube-user1
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pods-reader
apiGroup: rbac.authorization.k8s.io
ClusterRole和ClusterRoleBinding
ClusterRole的許可授權作用於整個叢集,因此常用於控制Role無法生效的資源型別,這包括叢集級別的資源(如Nodes)、非資源型別的端點(如/healthz)和作用於所有名稱空間的資源(例如,跨名稱空間獲取任何資源的許可權)。
叢集級別的角色資源ClusterRole資源除了能夠管理與Role資源一樣的許可許可權之外,還可以用於叢集級元件的授權,配置方式及其在rules欄位中可內嵌的欄位也與Role資源類似,kind為ClusterRole。
對於namespace級別的資源的訪問控制來說,RoleBinding也能夠將主體繫結至ClusterRole資源之上,但僅能賦予使用者訪問Role-Binding資源本身所在的名稱空間之內可由ClusterRole賦予的許可權,若藉助ClusterRoleBinding進行繫結,則使用者就具有了所有相關名稱空間中的資源的訪問許可權。
但對於非namespace級別的資源,如nodes、persistentvolumes等資源,以及非資源型的URL(/api、/apis、/healthz、/swaggerapi和/version),則無法通過RoleBinding繫結來授權給使用者。它們也只能定義在ClusterRole中,且需要基於ClusterRoleBinding進行授權。不過,對此類資源的讀取許可權已經由系統預設的名稱同為system:discovery的ClusterRole和ClusterRoleBinding兩個資源自動設定。
執行kubectl get clusterrole system:discovery -o yaml
可以檢視:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: system:discovery
...
rules:
- nonResourceURLs:
- /api
- /api/*
- /apis
- /apis/*
- /healthz
- /livez
- /openapi
- /openapi/*
- /readyz
- /version
- /version/
verbs:
- get
准入控制器
在完成身份認證和許可權檢查之後,將由准入控制器進一步驗證,比如執行物件的語義驗證、設定缺失欄位的預設值、限制所有容器使用的映象檔案必須來自某個特定的Registry、檢查Pod物件的資源需求是否超出了指定的限制範圍等。
准入控制器分為Mutating-AdmissionWebhook和ValidatingAdmissionWebhook兩種型別,分別用於在API中執行物件配置的“變異”和“驗證”操作。變異控制器可以修改他們許可的物件,而驗證控制器則一般不會。在具體的程式碼實現上,一個准入控制器可以兼具驗證、變異兩項功能。
在執行時准入控制可分為兩個階段,第一個階段序列執行各變異型控制器,第二個階段序列執行各驗證型控制器,一旦任一階段中的任何控制器拒絕請求,則立即拒絕整個請求,並向用戶返回錯誤提示。
LimitRanger准入控制器
對於沒有指定資源需求及資源限制的容器來說,其應用有因故障吞掉所在工作節點上的所有可用計算資源的風險,使用LimitRange資源可以為某個名稱空間中的每個容器指定計算資源用量,或者設定預設的計算資源需求和計算資源限制。定義了LimitRange物件後,客戶端提交建立或修改的資源物件將受到LimitRanger控制器的檢查。
LimitRange資源支援限制容器、Pod和PersistentVolumeClaim三種資源物件的系統資源用量,其中Pod和容器主要用於定義可用的CPU和記憶體資源範圍,而PersistentVolume-Claim則主要定義儲存空間的限制範圍。
下面的配置清單在default空間中定義了容器的資源限制:
apiVersion: v1
kind: LimitRange
metadata:
name: cpu-limit-range
spec:
limits:
- default:
cpu: 1
defaultRequest:
cpu: 1
max:
cpu: 2000m
maxLimitRequestRatio:
cpu: 4
type: Container
在建立Pod物件時,如果沒有指定資源限制,則LimitRange資源會自動為其設定,而且如果Request超出max的限制後會被block。
ResourceQuota資源與准入控制器
儘管LimitRange資源能限制單個容器、Pod及PVC等相關計算資源或儲存資源的用量,但多個物件佔用資源的總和仍然可能超出系統的限制。ResourceQuota資源用於定義名稱空間的物件數量或系統資源配額,它支援限制每種資源型別的物件總數,以及所有物件所能消耗的計算資源及儲存資源總量等。一個名稱空間可以建立一個ResourceQuota物件。
ResourceQuota物件可限制指定名稱空間中非終止狀態的所有Pod物件的計算資源需求及計算資源限制總量,比如
- requests.cpu:CPU資源需求的總量限額
- requests.cpu:記憶體資源需求的總量限額。
- limits.cpu:CPU資源限制的總量限額。
- limits.memory:記憶體資源限制的總量限額。
還可以限制特定名稱空間中可以使用的PVC數量和這些PVC資源的空間大小總量,一部分舉例:
- persistentvolumeclaims:可以建立的PVC總數
- requests.storage:所有PVC儲存需求的總量限額
在1.9版本起支援以count/<resource>.<group>
的格式對所有資源型別物件的計數配額,如count/deployments.apps
count/services
等。
apiVersion: v1
kind: ResourceQuota
metadata:
name: resource-quota-1
spec:
hard:
pods: 2
requests.cpu: 500m
requests.memory: 1Gi
limits.cpu: 2
limits.memory: 2Gi
count/deployments.apps: 1
persistentvolumeclaims: 2
apply上述資源清單後執行kubectl describe quota resource-quota-1
:
Name: resource-quota-1
Namespace: default
Resource Used Hard
-------- ---- ----
count/deployments.apps 0 1
limits.cpu 0 2
limits.memory 0 2Gi
persistentvolumeclaims 1 2
pods 4 2
requests.cpu 0 500m
requests.memory 0 1Gi
從列印結果可以看出在建立ResourceQuota前pod數量為4個,所以建立ResourceQuota後就無法新增新的pod了。
此外一旦啟用了計算資源需求和計算資源限制配額,那麼建立的任何Pod物件都必須設定此兩類屬性,否則會被block。
學習資料
《Kubernetes實戰進階》 馬永亮著