Controlling Access to the Kubernetes API
API Server 是存取和管理資源對象的入口,不管是 kube-controllermanager、kube-scheduler、kubelet 和kube-proxy 等都要透過 API Server 進行存取。而每一次的訪問請求都須進行合法的驗證,如身分、操作權限等,當這些流程都為正常才能將書據存入 etcd 中。當請求到 API 時,會經歷幾個階段,如下圖所示:
當收到一個用戶端的請求後,會調用 Authentication 來驗證用戶端身分,如果前者驗證通過接著會驗證 Authorization 是否有權限去操作用戶端發送的請求(建立、讀取、刪除等),如果授權(Authorization)通過驗證必須在通過 Admission Control 檢測像是 namespace 是否存在、使否違反資源限制等。
用戶端存取 API 可以透過 kubectl、函式庫或使用 REST 方式,然而可以操作的主體被分為人和 POD 物件,其分別對應 User Account
和 Service Account
。
- User Account
- 非 kubernetes 所使用的管理帳號,像是密鑰、Keystone 或是以檔案方式的使用者和密碼列表
- 名稱需是唯一值
- Service Account
- 是 Kubernetes API 所管理的帳號,使用在 POD 之中的服務行程訪問 Kubernetes API 時提供的身分標識
- 一般會綁定特定 namespace,會附帶 Secret 資源的憑證用於訪問 API Server
上面兩種類型都可隸屬一或多個用戶組,而用戶組本身沒有操作權限,其本身只是一個 User Account 的邏輯集合。Kubernetes 有以下特殊目的的組
system:unauthenticated
- 未能通過任何一個 Authentication 檢驗的帳號
system:authenticated
- 驗證工的用戶自動加入的一個組,用於快速引用所有通過認證的用戶帳號
system:serviceaccounts
- 當前系統尚所有 Service Account 物件
system:serviceaccounts:<namespace>
- 特定 namespace 下的 Service Account 帳號
Authentication、Authorization 和 Admission Control
Kubernetes 用認證方式對 API 請求進行身份驗證,支援的認證有以下
- 證書
- bearer tokens
- authenticating proxy
- HTTP basic
- 等
認證過程會驗證以下屬性
- Username
- UID
- Groups
- Extra
API Server 支援以下幾種認證方式
- X509
- 藉由
--client-ca-file=SOMEFILE
啟用 API Server 客戶端證書身份驗證選項
- 藉由
- Static Token File
- 使用
--token-auth-file=SOMEFILE
,API Server 從中讀取 bearer token
token,user,uid,"group1,group2,group3"
- 使用
- Bootstrap Tokens
- 靜態密碼檔案
- 使用
--basic-auth-file
加載,用戶名和密碼等令牌以明文格式儲存的 CSV 格式檔案
- 使用
- Service Account Token
- 使用
--service-account-key-file
加載
- 使用
- OpenID Connect Tokens
- OAuth2 風格
- Webhook Token
- Authentication Proxy
- Keystone
- 匿名請求
- 未被任何驗證機制明確拒絕的用戶即為匿名用戶,其會被自動標識為用戶名
system:anonymous
,並屬於system:unauthenticated
用戶組;在 API Server 啟用了除AlwaysAllow
以外的認證機 制時,匿名用戶處於啟用狀態,但管理員可通過 –anonymousauth=false` 選項將其禁用
- 未被任何驗證機制明確拒絕的用戶即為匿名用戶,其會被自動標識為用戶名
API Server 主要支援以下授權機制來定義用戶操作的權限
- Node
- 基於 POD 資源的目標調度節點來實現的對 kubelet 的訪問控制
- ABAC(attribute-based access control)
- RBAC(role-based access control)
- WebHook
- 基於 HTTP 回調機制由外部 REST 服務檢查確認用戶授權的訪問控制
最後 Admission Controller 用於在客戶端請求經過身份驗證和授權檢查之後,會在存入 etcd 時攔截該請求,進行語意驗證,其支援的方式有以下
- AlwaysAdmit
- AlwaysDeny
- AlwaysPullImages
- NamespaceLifecycle
- LimitRanger
- ServiceAccount
- PersistentVolumeLabel
- DefaultStorageClass
- ResourceQuota
- DefaultTolerationSeconds
- ValidatingAdmissionWebhook
- MutatingAdmissionWebhook
Service Account 管理與應用
當我們使用 kubectl run nginx --image=nginx:latest
運行一個 nginx 應用時,此 POD 會自動關連一個儲存卷,並讓該容器掛載。如下使用 kubectl describe
進行觀察
...
Containers:
nginx:
Container ID:
Image: nginx:latest
...
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from default-token-fmjkg (ro)
...
Volumes:
default-token-fmjkg:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-fmjkg
Optional: false
....
當我們使用 kubectl describe secrets
該 SecretName 對應的物件時,會發現其有三個資料分別是 ca.crt
、namespace
和 token
,而 token
儲存了 Service Account 的認證 token,容器中的行程使用它向 API Server 發送連接請求,接著進行 Authentication 驗證,在將其用戶名稱傳遞給 Authorization 進行下一階段驗證。每個 POD 對象都只有一個 Service Account,如果未明確指定,Admission Controller 會自動使用當前 namespace 的默認 Service Account,通常是 default,如下。
kubectl describe serviceaccounts default
Name: default
Namespace: default
Labels: <none>
Annotations: <none>
Image pull secrets: <none>
Mountable secrets: default-token-fmjkg
Tokens: default-token-fmjkg
Events: <none>
Kubernetes 透過 Service Account 准入控制器、令牌控制器(token controller)和 Service Account 帳戶控制器,實現自動化。
- Service Account 帳戶控制器負責為 namespace 管理相應資源,且確保每個 namespace 中都存在一個 default 的 Service Account 物件。
- Service Account 准入控制器是 API Server 的一部分,負責在建立或更新 POD 時對其按需進行 Service Account 物件相關資訊的修改,包括如下操作:
- POD 沒有明確定義使用哪個 Service Account 物件時,則將設定為 default
- 確定 POD 正確引用已存在的 Service Account
- POD 中未定義
ImagePullSecerts
,則將 Service Account 的ImagePullSecerts
添加上去 - 為帶有訪問 API 的 Token 的 POD 添加一個儲存卷
- 為 POD 中每個容器添加一個
volumeMounts
,掛載至/var/run/secrets/kubernetes.io/serviceaccount
- token controller 是 controller-manager 的子元件,負責以下
- 監控 Service Account 的建立操作,且為其新增用於訪問 API 的 Secret 物件
- 監控 Service Account 的刪除操作,且刪除其相關的所有 Service Account token
- 監控 Secret 物件的新增操作,確保其引用的 Service Account 已存在,且在必要時為 Secret 物件新增認證 token
- 監控 Secret 物件的刪除操作,確保刪除每個 Service Account 中對此 Secret 的引用
在 /etc/kubernetes/manifests/
中 kube-controller-manager.yaml
這個檔案有一個 --service-account-private-key-file=/etc/kubernetes/pki/sa.key
配置它用於對生成的 Service Account token 進行簽章,以確保完整性。同樣的在 kube-apiserver.yaml
中使用了 --service-account-signing-key-file=/etc/kubernetes/pki/sa.key
參數用於認證期間的檢驗。
Create Service Account
當我們使用 kubectl get serviceaccounts --all-namespaces
進行觀察時每個 namespace 下都會存在一個 default 物件,其讓 POD 有權限讀取同一 namespace 下的其它資源。因此要讓 POD 有更大權限時,使用者必須自定義 Service Account 資源。當然一個 POD 物件最多也只能存在一個 Service Account 資源,可以透過 spec.serviceAccountName
指定要使用的 Service Account 物件,否則就是 default。
如何建立呢 ?可以透過 kubectl create servic-eaccount
或是 yaml 檔案形式如下:
apiVersion: v1
kind: ServiceAccount
metadata:
name: emqx # 建立一個 emqx 的服務帳戶
namespace: default
建立一個 Deployment
物件,並指定 emqx 的服務帳戶
apiVersion: apps/v1
kind: Deployment
metadata:
name: emqx-cluster
namespace: emqx-ns
labels:
app: emqx
spec:
replicas: 3
selector:
matchLabels:
app: emqx-app
template:
metadata:
labels:
app: emqx-app
spec:
serviceAccountName: emqx # this
...
當 POD 向 API Server 發送請求時,其設定的 token 在通過認證後將由 Authorization 進行 Service Account 是否有權限訪問所請求資源的判定,而當前 RBAC
為主流。
配置 kubeconfig
透過 kubeconfig 配置可以提供 kubectl、kubelet 等元件提供集群相關的配置,並且能夠設定上下文環境,並在不同環境中進行切換。
-----> kubernetes cluster1 API Server
/
Kubectl -----> kubeconfig
\
-----> kubernetes cluster2 API Server
當使用 kubeadm init
初始化 Kubernetes
集群後 /etc/kubernetes/admin.conf
檔案是 kubeconfig
格式的配置檔案,藉由 kubectl
加載至預設路徑 $HOME/.kube/config
。
透過 kubectl config view
可以獲取當前使用的環境配置。
$ kubectl config view
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: DATA+OMITTED
server: https://172.17.205.10:6443
name: kubernetes
contexts:
- context:
cluster: kubernetes
user: kubernetes-admin
name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes
kind: Config
preferences: {}
users:
- name: kubernetes-admin
user:
client-certificate-data: REDACTED
client-key-data: REDACTED
上面結果訊息內容包含:
- clusters
- 包含訪問 API Server 的 URL 和所屬集群名稱
- users
- 包含訪問 API Server 時使用者名稱和認證訊息
- contexts
- kubelete 的可用上下文,從
users
中某個使用者和clusters
中某個集群名稱組合
- kubelete 的可用上下文,從
- current-context
- 當前使用的上下文名稱,
contexts
中某一個選項
- 當前使用的上下文名稱,
結構上可以參考此圖 from https://eliu.github.io/2020/03/28/manage-kubeconfig/
簡單的說使用 kubectl 後操作 current-context
然後對應 contexts
中某一個選項。
對我們來說也可自訂義相關配置訊息至 kubeconfig
檔案中,並實現不同帳號接入集群功能。這些操作可使用 kubectl config
方式來操作
kubectl config view # 列出 kubeconfig 內容
kubectl config set-cluster # 設定 kubeconfig 中 clusters
kubectl config set-credentials # 設定 kubeconfig 中 users
kubectl config set-context # 設定 kubeconfig 中 contexts
kubectl config use-context # 設定 kubeconfig 中 current-context
初始化集群後,/etc/kubernetes/admin.conf
是默認管理及群的權限。
官方可參考資源
實作地端操作 GKE
以下是實現從本地端的 K8s 群集操作 GKE。先從 GKE 上的群集 .kube/config
進行複製並貼上至本地端主機 master
上,這邊將其複製到 gke1
檔案。
$ KUBECONFIG=gke1:~/.kube/config kubectl config view
# gke1 是 GKE 的 key
# DATA+OMITTED 會導致資訊不完全因此需用以下方式攤平
$ KUBECONFIG=gke1:~/.kube/config kubectl config view --flatten
$ KUBECONFIG=gke1:~/.kube/config kubectl config view --flatten > new
$ KUBECONFIG=new kubectl config get-contexts
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
* gke_sunny-catwalk-286908_us-central1-c_cluster-1-test gke_sunny-catwalk-286908_us-central1-c_cluster-1-test gke_sunny-catwalk-286908_us-central1-c_cluster-1-test
gke_sunny-catwalk-286908_us-central1-c_cluster-2-test gke_sunny-catwalk-286908_us-central1-c_cluster-2-test gke_sunny-catwalk-286908_us-central1-c_cluster-2-test
kubernetes-admin@kubernetes kubernetes kubernetes-admin
$ KUBECONFIG=new kubectl config use-context kubernetes-admin@kubernetes # 切換
Switched to context "kubernetes-admin@kubernetes".
$ KUBECONFIG=new kubectl config get-contexts
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
gke_sunny-catwalk-286908_us-central1-c_cluster-1-test gke_sunny-catwalk-286908_us-central1-c_cluster-1-test gke_sunny-catwalk-286908_us-central1-c_cluster-1-test
gke_sunny-catwalk-286908_us-central1-c_cluster-2-test gke_sunny-catwalk-286908_us-central1-c_cluster-2-test gke_sunny-catwalk-286908_us-central1-c_cluster-2-test
* kubernetes-admin@kubernetes kubernetes kubernetes-admin
$ KUBECONFIG=new kubectl get nodes
NAME STATUS ROLES AGE VERSION
master Ready master 5d18h v1.18.8
node01 Ready <none> 5d17h v1.18.8
node02 Ready <none> 5d17h v1.18.8
切換至 GKE 叢集
cch@master:~/context$ KUBECONFIG=new kubectl config use-context gke_sunny-catwalk-286908_us-central1-c_cluster-1-test
Switched to context "gke_sunny-catwalk-286908_us-central1-c_cluster-1-test".
cch@master:~/context$ KUBECONFIG=new kubectl config current-context
gke_sunny-catwalk-286908_us-central1-c_cluster-1-test
cch@master:~/context$ KUBECONFIG=new kubectl get nodes
NAME STATUS ROLES AGE VERSION
gke-cluster-1-test-default-pool-255d7fb2-1f8l Ready <none> 4d12h v1.15.12-gke.2
gke-cluster-1-test-default-pool-255d7fb2-lbwc Ready <none> 4d12h v1.15.12-gke.2
gke-cluster-1-test-default-pool-255d7fb2-ppnm Ready <none> 4d12h v1.15.12-gke.2
切記 cloud sdk 需要安裝,之後才能操作
$ echo "deb [signed-by=/usr/share/keyrings/cloud.google.gpg] https://packages.cloud.google.com/apt cloud-sdk main" |
$ sudo tee -a /etc/apt/sources.list.d/google-cloud-sdk.list
$ sudo apt-get install apt-transport-https ca-certificates gnupg
$ curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key --keyring /usr/share/keyrings/cloud.google.gpg add -
$ sudo apt-get update && sudo apt-get install google-cloud-sdk
$ gcloud auth login # 它會引導認證