管理服務帳戶
ServiceAccount 為 Pod 中執行的程序提供身分。
Pod 內部的程序可以使用其關聯的服務帳戶的身分,向叢集的 API 伺服器進行身分驗證。
如需服務帳戶的簡介,請閱讀設定服務帳戶。
本任務指南說明了 ServiceAccount 背後的一些概念。本指南還說明了如何取得或撤銷代表 ServiceAccount 的權杖,以及如何(可選地)將 ServiceAccount 的有效性綁定到 API 物件的生命週期。
開始之前
您需要有一個 Kubernetes 叢集,並且必須將 kubectl 命令列工具配置為與您的叢集進行通訊。建議在至少有兩個節點且這些節點不充當控制平面主機的叢集上執行本教學。如果您還沒有叢集,可以使用 minikube 建立一個,或者您可以使用以下 Kubernetes 實驗環境之一
為了能夠完全按照這些步驟操作,請確保您有一個名為 examplens
的命名空間。如果沒有,請執行以下命令建立一個
kubectl create namespace examplens
使用者帳戶與服務帳戶
Kubernetes 區分使用者帳戶和服務帳戶的概念,原因如下
- 使用者帳戶適用於人類。服務帳戶適用於應用程式程序,這些程序(對於 Kubernetes)在 Pod 的容器中執行。
- 使用者帳戶旨在成為全域性的:名稱在叢集的所有命名空間中必須是唯一的。無論您查看哪個命名空間,代表使用者的特定使用者名稱都代表同一個使用者。在 Kubernetes 中,服務帳戶是命名空間範圍的:兩個不同的命名空間可以包含具有相同名稱的 ServiceAccount。
- 通常,叢集的使用者帳戶可能從公司資料庫同步,在公司資料庫中,建立新的使用者帳戶需要特殊權限,並且與複雜的業務流程相關聯。相比之下,服務帳戶的建立旨在更輕量級,允許叢集使用者按需為特定任務建立服務帳戶。將 ServiceAccount 建立與使用者導入步驟分開,使工作負載更容易遵循最小權限原則。
- 人類和服務帳戶的稽核考量可能有所不同;這種分離使實現這一目標變得更容易。
- 複雜系統的組態套件可能包含該系統組件的各種服務帳戶的定義。由於服務帳戶可以在沒有太多約束的情況下建立,並且具有命名空間範圍的名稱,因此這種組態通常是可移植的。
綁定的服務帳戶權杖
ServiceAccount 權杖可以綁定到 kube-apiserver 中存在的 API 物件。這可以用於將權杖的有效性與另一個 API 物件的存在聯繫起來。支援的物件類型如下:
- Pod(用於投影卷掛載,請參閱下文)
- Secret(可用於透過刪除 Secret 來撤銷權杖)
- Node(在 v1.32 中,建立新的節點綁定權杖為 Beta 版,使用現有的節點綁定權杖為 GA 版)
當權杖綁定到物件時,物件的 metadata.name
和 metadata.uid
會作為額外的「私有宣告」儲存在發行的 JWT 中。
當綁定的權杖提交給 kube-apiserver 時,服務帳戶驗證器將提取並驗證這些宣告。如果引用的物件或 ServiceAccount 正在等待刪除(例如,由於 Finalizers),則對於 .metadata.deletionTimestamp
日期之後 60 秒(或更長時間)的任何時刻,使用該權杖的身分驗證都將失敗。如果引用的物件不再存在(或其 metadata.uid
不匹配),則請求將不會通過身分驗證。
Pod 綁定權杖中的其他 metadata
Kubernetes v1.32 [stable]
(預設啟用:true)當服務帳戶權杖綁定到 Pod 物件時,其他 metadata 也會嵌入到權杖中,指示綁定 Pod 的 spec.nodeName
欄位的值,以及該節點的 uid(如果可用)。
當權杖用於身分驗證時,kube-apiserver **不會**驗證此節點資訊。包含它是為了讓整合者在檢查 JWT 時不必取得 Pod 或節點 API 物件來檢查關聯的節點名稱和 uid。
驗證和檢查私有宣告
TokenReview
API 可用於驗證和從權杖中提取私有宣告
- 首先,假設您有一個名為
test-pod
的 Pod 和一個名為my-sa
的服務帳戶。 - 建立一個綁定到此 Pod 的權杖
kubectl create token my-sa --bound-object-kind="Pod" --bound-object-name="test-pod"
- 將此權杖複製到一個名為
tokenreview.yaml
的新檔案中
apiVersion: authentication.k8s.io/v1
kind: TokenReview
spec:
token: <token from step 2>
- 將此資源提交到 apiserver 進行審閱
kubectl create -o yaml -f tokenreview.yaml # we use '-o yaml' so we can inspect the output
您應該看到如下所示的輸出
apiVersion: authentication.k8s.io/v1
kind: TokenReview
metadata:
creationTimestamp: null
spec:
token: <token>
status:
audiences:
- https://kubernetes.default.svc.cluster.local
authenticated: true
user:
extra:
authentication.kubernetes.io/credential-id:
- JTI=7ee52be0-9045-4653-aa5e-0da57b8dccdc
authentication.kubernetes.io/node-name:
- kind-control-plane
authentication.kubernetes.io/node-uid:
- 497e9d9a-47aa-4930-b0f6-9f2fb574c8c6
authentication.kubernetes.io/pod-name:
- test-pod
authentication.kubernetes.io/pod-uid:
- e87dbbd6-3d7e-45db-aafb-72b24627dff5
groups:
- system:serviceaccounts
- system:serviceaccounts:default
- system:authenticated
uid: f8b4161b-2e2b-11e9-86b7-2afc33b31a7e
username: system:serviceaccount:default:my-sa
注意
儘管使用kubectl create -f
來建立此資源,並將其定義為 Kubernetes 中其他資源類型的方式,TokenReview 是一種特殊類型,且 kube-apiserver 實際上並不會將 TokenReview 物件持久儲存到 etcd 中。因此,kubectl get tokenreview
不是有效的指令。服務帳戶私有宣告的結構描述
JWT 權杖中 Kubernetes 特定宣告的結構描述目前尚未有文件記錄,不過相關的程式碼區域可以在 Kubernetes 程式碼庫的 serviceaccount 套件中找到。
您可以使用標準的 JWT 解碼工具來檢查 JWT。以下是 my-serviceaccount
ServiceAccount 的 JWT 範例,它綁定到名為 my-pod
的 Pod 物件,該 Pod 物件排程到 my-node
節點,位於 my-namespace
命名空間中
{
"aud": [
"https://my-audience.example.com"
],
"exp": 1729605240,
"iat": 1729601640,
"iss": "https://my-cluster.example.com",
"jti": "aed34954-b33a-4142-b1ec-389d6bbb4936",
"kubernetes.io": {
"namespace": "my-namespace",
"node": {
"name": "my-node",
"uid": "646e7c5e-32d6-4d42-9dbd-e504e6cbe6b1"
},
"pod": {
"name": "my-pod",
"uid": "5e0bd49b-f040-43b0-99b7-22765a53f7f3"
},
"serviceaccount": {
"name": "my-serviceaccount",
"uid": "14ee3fa4-a7e2-420f-9f9a-dbc4507c3798"
}
},
"nbf": 1729601640,
"sub": "system:serviceaccount:my-namespace:my-serviceaccount"
}
注意
此 JWT 中的 aud
和 iss
欄位可能會因不同的 Kubernetes 叢集而異,具體取決於您的配置。
pod
和 node
宣告的存在表示此權杖已綁定到 Pod 物件。當驗證 Pod 綁定的 ServiceAccount 權杖時,API 伺服器不會驗證所參考的 Node 物件是否存在。
在 Kubernetes 外部運行的服務若要執行 JWT 的離線驗證,可以使用此結構描述,以及配置了來自 API 伺服器的 OpenID Discovery 資訊的相容 JWT 驗證器,來驗證所提出的 JWT,而無需使用 TokenReview API。
以這種方式驗證 JWT 的服務不會驗證嵌入在 JWT 權杖中的宣告是否為最新且仍然有效。這表示如果權杖綁定到一個物件,而該物件已不存在,則該權杖仍將被視為有效(直到配置的權杖過期為止)。
需要確保權杖的綁定宣告仍然有效的客戶端必須使用 TokenReview API 將權杖呈現給 kube-apiserver
,以便它驗證和擴展嵌入的宣告,使用與上方 驗證和檢查私有宣告 區段類似的步驟,但使用 支援的客戶端程式庫。有關 JWT 及其結構的更多資訊,請參閱 JSON Web Token RFC。
綁定服務帳戶權杖卷機制
Kubernetes v1.22 [穩定]
(預設啟用:true)預設情況下,Kubernetes 控制平面(具體而言,ServiceAccount 准入控制器)會將 投影卷新增至 Pod,而此卷包含用於 Kubernetes API 存取的權杖。
以下是一個已啟動 Pod 的外觀範例
...
- name: kube-api-access-<random-suffix>
projected:
sources:
- serviceAccountToken:
path: token # must match the path the app expects
- configMap:
items:
- key: ca.crt
path: ca.crt
name: kube-root-ca.crt
- downwardAPI:
items:
- fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
path: namespace
該 manifest 片段定義了一個由三個來源組成的投影卷。在本例中,每個來源也代表該卷中的單一路徑。這三個來源是
serviceAccountToken
來源,其中包含 kubelet 從 kube-apiserver 取得的權杖。kubelet 使用 TokenRequest API 提取有時間限制的權杖。為 TokenRequest 提供的權杖會在 Pod 刪除時或在定義的生命週期後(預設為 1 小時)過期。kubelet 也會在權杖過期前刷新該權杖。權杖綁定到特定的 Pod,並以 kube-apiserver 作為其受眾。此機制取代了先前的機制,該機制根據 Secret 新增卷,其中 Secret 代表 Pod 的 ServiceAccount,但不會過期。configMap
來源。ConfigMap 包含憑證授權單位資料的捆綁包。Pod 可以使用這些憑證來確保它們連接到叢集的 kube-apiserver(而不是中間盒或意外錯誤配置的對等點)。downwardAPI
來源,它會查找包含 Pod 的命名空間的名稱,並使該名稱資訊可供 Pod 內運行的應用程式碼使用。
Pod 中掛載此特定卷的任何容器都可以存取上述資訊。
注意
沒有特定的機制可以使透過 TokenRequest 發出的權杖失效。如果您不再信任 Pod 的綁定服務帳戶權杖,您可以刪除該 Pod。刪除 Pod 會使其綁定的服務帳戶權杖過期。ServiceAccount 的手動 Secret 管理
v1.22 之前的 Kubernetes 版本會自動建立用於存取 Kubernetes API 的憑證。這種較舊的機制是基於建立權杖 Secret,然後可以將其掛載到運行的 Pod 中。
在更新的版本中,包括 Kubernetes v1.32,API 憑證是直接取得,使用 TokenRequest API,並使用投影卷掛載到 Pod 中。使用此方法取得的權杖具有有限的生命週期,並且在掛載它們的 Pod 被刪除時會自動失效。
您仍然可以手動建立 Secret 以保存服務帳戶權杖;例如,如果您需要永不過期的權杖。
一旦您手動建立 Secret 並將其連結到 ServiceAccount,Kubernetes 控制平面會自動將權杖填入該 Secret 中。
注意
儘管手動建立長期存在的 ServiceAccount 權杖的機制存在,但建議改為使用 TokenRequest 來取得短期 API 存取權杖。自動產生的舊版 ServiceAccount 權杖清理
在 1.24 版之前,Kubernetes 會自動為 ServiceAccount 產生基於 Secret 的權杖。為了區分自動產生的權杖和手動建立的權杖,Kubernetes 會檢查 ServiceAccount 的 secrets 欄位中的參考。如果 Secret 在 secrets
欄位中被參考,則它被視為自動產生的舊版權杖。否則,它被視為手動建立的舊版權杖。例如
apiVersion: v1
kind: ServiceAccount
metadata:
name: build-robot
namespace: default
secrets:
- name: build-robot-secret # usually NOT present for a manually generated token
從 1.29 版開始,如果自動產生的舊版 ServiceAccount 權杖在一段時間內(預設設定為一年)保持未使用狀態,則會將其標記為無效。對於持續此定義期間(再次,預設為一年)未使用的權杖,控制平面將隨後清除它們。
如果使用者使用無效的自動產生權杖,權杖驗證器將會
- 為鍵值對
authentication.k8s.io/legacy-token-invalidated: <secret name>/<namespace>
新增稽核註解, - 遞增
invalid_legacy_auto_token_uses_total
指標計數, - 使用新日期更新 Secret 標籤
kubernetes.io/legacy-token-last-used
, - 傳回錯誤,指示權杖已失效。
當收到此驗證錯誤時,使用者可以更新 Secret 以移除 kubernetes.io/legacy-token-invalid-since
標籤,以暫時允許使用此權杖。
以下是已標記 kubernetes.io/legacy-token-last-used
和 kubernetes.io/legacy-token-invalid-since
標籤的自動產生舊版權杖範例
apiVersion: v1
kind: Secret
metadata:
name: build-robot-secret
namespace: default
labels:
kubernetes.io/legacy-token-last-used: 2022-10-24
kubernetes.io/legacy-token-invalid-since: 2023-10-25
annotations:
kubernetes.io/service-account.name: build-robot
type: kubernetes.io/service-account-token
控制平面詳細資訊
ServiceAccount 控制器
ServiceAccount 控制器管理命名空間內的 ServiceAccount,並確保每個活動命名空間中都存在名為「default」的 ServiceAccount。
權杖控制器
服務帳戶權杖控制器作為 kube-controller-manager
的一部分運行。此控制器以非同步方式運作。它會
- 監看 ServiceAccount 刪除,並刪除所有對應的 ServiceAccount 權杖 Secret。
- 監看 ServiceAccount 權杖 Secret 新增,並確保參考的 ServiceAccount 存在,並在需要時將權杖新增至 Secret。
- 監看 Secret 刪除,並在需要時從對應的 ServiceAccount 中移除參考。
您必須使用 --service-account-private-key-file
旗標將服務帳戶私鑰檔案傳遞給 kube-controller-manager
中的權杖控制器。私鑰用於簽署產生的服務帳戶權杖。同樣地,您必須使用 --service-account-key-file
旗標將對應的公鑰傳遞給 kube-apiserver
。公鑰將用於在身份驗證期間驗證權杖。
Kubernetes v1.32 [Alpha]
(預設啟用:false)設定 --service-account-private-key-file
和 --service-account-key-file
旗標的替代設定是為 外部 ServiceAccount 權杖簽署和金鑰管理 配置外部 JWT 簽署者。請注意,這些設定是互斥的,不能一起配置。
ServiceAccount 准入控制器
Pod 的修改是透過稱為 准入控制器 的外掛程式來實作的。它是 API 伺服器的一部分。此准入控制器會同步運作以在 Pod 建立時修改 Pod。當此外掛程式處於活動狀態時(並且在大多數發行版中預設為活動狀態),那麼當 Pod 建立時,它會執行以下操作
- 如果 Pod 沒有設定
.spec.serviceAccountName
,則准入控制器會將此傳入 Pod 的 ServiceAccount 名稱設定為default
。 - 准入控制器確保傳入 Pod 參考的 ServiceAccount 存在。如果沒有名稱相符的 ServiceAccount,則准入控制器會拒絕傳入的 Pod。該檢查甚至適用於
default
ServiceAccount。 - 如果 ServiceAccount 的
automountServiceAccountToken
欄位和 Pod 的automountServiceAccountToken
欄位都未設定為false
- 准入控制器會變更傳入的 Pod,新增額外的 卷,其中包含用於 API 存取的權杖。
- 准入控制器會將
volumeMount
新增至 Pod 中的每個容器,跳過已為路徑/var/run/secrets/kubernetes.io/serviceaccount
定義卷掛載的任何容器。對於 Linux 容器,該卷掛載在/var/run/secrets/kubernetes.io/serviceaccount
;在 Windows 節點上,掛載在等效路徑。
- 如果傳入 Pod 的 spec 尚未包含任何
imagePullSecrets
,則准入控制器會新增imagePullSecrets
,從ServiceAccount
複製它們。
舊版 ServiceAccount 權杖追蹤控制器
Kubernetes v1.28 [穩定]
(預設啟用:true)此控制器在 kube-system
命名空間中產生一個名為 kube-system/kube-apiserver-legacy-service-account-token-tracking
的 ConfigMap。ConfigMap 記錄系統開始監控舊版服務帳戶權杖的時間戳記。
舊版 ServiceAccount 權杖清理器
Kubernetes v1.30 [穩定]
(預設啟用:true)舊版 ServiceAccount 權杖清理器作為 kube-controller-manager
的一部分運行,並且每 24 小時檢查一次,以查看是否有任何自動產生的舊版 ServiceAccount 權杖在指定的時間量內未使用過。如果是,則清理器會將這些權杖標記為無效。
清理器首先檢查控制平面建立的 ConfigMap(前提是 LegacyServiceAccountTokenTracking
已啟用)。如果目前時間在 ConfigMap 中的日期之後指定的時間量,則清理器會循環遍歷叢集中的 Secret 列表,並評估每個類型為 kubernetes.io/service-account-token
的 Secret。
如果 Secret 滿足以下所有條件,則清理器會將其標記為無效
- Secret 是自動產生的,這表示它由 ServiceAccount 雙向參考。
- Secret 目前未被任何 Pod 掛載。
- Secret 自建立以來或自上次使用以來,在指定的時間量內未使用過。
清理器透過將名為 kubernetes.io/legacy-token-invalid-since
的標籤新增至 Secret 來將 Secret 標記為無效,並以目前日期作為值。如果無效的 Secret 在指定的時間量內未使用,則清理器將刪除它。
注意
上述所有指定的時間量預設為一年。叢集管理員可以透過kube-controller-manager
元件的 --legacy-service-account-token-clean-up-period
命令列引數來配置此值。TokenRequest API
Kubernetes v1.22 [穩定]
您可以使用 ServiceAccount 的 TokenRequest 子資源來取得該 ServiceAccount 的有時間限制的權杖。您不需要呼叫此資源來取得在容器內使用的 API 權杖,因為 kubelet 會使用投影卷為您設定此資源。
如果您想從 kubectl
使用 TokenRequest API,請參閱 手動建立 ServiceAccount 的 API 權杖。
Kubernetes 控制平面(具體而言,ServiceAccount 准入控制器)會將投影卷新增至 Pod,而 kubelet 確保此卷包含一個權杖,使容器能夠以正確的 ServiceAccount 進行身份驗證。
(此機制取代了先前的機制,該機制根據 Secret 新增卷,其中 Secret 代表 Pod 的 ServiceAccount,但不會過期。)
以下是一個已啟動 Pod 的外觀範例
...
- name: kube-api-access-<random-suffix>
projected:
defaultMode: 420 # decimal equivalent of octal 0644
sources:
- serviceAccountToken:
expirationSeconds: 3607
path: token
- configMap:
items:
- key: ca.crt
path: ca.crt
name: kube-root-ca.crt
- downwardAPI:
items:
- fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
path: namespace
該 manifest 片段定義了一個投影卷,它結合了來自三個來源的資訊
serviceAccountToken
來源,其中包含 kubelet 從 kube-apiserver 取得的權杖。kubelet 使用 TokenRequest API 提取有時間限制的權杖。為 TokenRequest 提供的權杖會在 Pod 刪除時或在定義的生命週期後(預設為 1 小時)過期。權杖綁定到特定的 Pod,並以 kube-apiserver 作為其受眾。configMap
來源。ConfigMap 包含憑證授權單位資料的捆綁包。Pod 可以使用這些憑證來確保它們連接到叢集的 kube-apiserver(而不是中間盒或意外錯誤配置的對等點)。downwardAPI
來源。此downwardAPI
卷使包含 Pod 的命名空間的名稱可供 Pod 內運行的應用程式碼使用。
Pod 中掛載此卷的任何容器都可以存取上述資訊。
建立額外的 API 權杖
注意
僅當 權杖請求 機制不適用時,才建立長期存在的 API 權杖。權杖請求機制提供有時間限制的權杖;由於這些權杖會過期,因此它們代表較低的資訊安全風險。若要為 ServiceAccount 建立永不過期、持久儲存的 API 權杖,請建立類型為 kubernetes.io/service-account-token
的 Secret,並使用註解參考 ServiceAccount。然後控制平面會產生長期存在的權杖,並使用該產生的權杖資料更新該 Secret。
以下是此類 Secret 的範例 manifest
apiVersion: v1
kind: Secret
type: kubernetes.io/service-account-token
metadata:
name: mysecretname
annotations:
kubernetes.io/service-account.name: myserviceaccount
若要根據此範例建立 Secret,請執行
kubectl -n examplens create -f https://k8s.io/examples/secret/serviceaccount/mysecretname.yaml
若要查看該 Secret 的詳細資訊,請執行
kubectl -n examplens describe secret mysecretname
輸出類似於
Name: mysecretname
Namespace: examplens
Labels: <none>
Annotations: kubernetes.io/service-account.name=myserviceaccount
kubernetes.io/service-account.uid=8a85c4c4-8483-11e9-bc42-526af7764f64
Type: kubernetes.io/service-account-token
Data
====
ca.crt: 1362 bytes
namespace: 9 bytes
token: ...
如果您在 examplens
命名空間中啟動新的 Pod,它可以使用您剛建立的 myserviceaccount
服務帳戶憑證 Secret。
注意
請勿在 ServiceAccount 的secrets
欄位中參考手動建立的 Secret。否則,如果手動建立的 Secret 長期未使用,將會被清除。請參考自動產生的舊版 ServiceAccount 權杖清除。刪除/使 ServiceAccount 權杖失效
如果您知道包含要移除之權杖的 Secret 名稱
kubectl delete secret name-of-secret
否則,請先找出 ServiceAccount 的 Secret。
# This assumes that you already have a namespace named 'examplens'
kubectl -n examplens get serviceaccount/example-automated-thing -o yaml
輸出類似於
apiVersion: v1
kind: ServiceAccount
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"v1","kind":"ServiceAccount","metadata":{"annotations":{},"name":"example-automated-thing","namespace":"examplens"}}
creationTimestamp: "2019-07-21T07:07:07Z"
name: example-automated-thing
namespace: examplens
resourceVersion: "777"
selfLink: /api/v1/namespaces/examplens/serviceaccounts/example-automated-thing
uid: f23fd170-66f2-4697-b049-e1e266b7f835
secrets:
- name: example-automated-thing-token-zyxwv
然後,刪除您現在知道名稱的 Secret
kubectl -n examplens delete secret/example-automated-thing-token-zyxwv
外部 ServiceAccount 權杖簽署和金鑰管理
Kubernetes v1.32 [Alpha]
(預設啟用:false)可以將 kube-apiserver 配置為使用外部簽署者進行權杖簽署和權杖驗證金鑰管理。此功能使 Kubernetes 發行版能夠與他們選擇的金鑰管理解決方案(例如:HSM、雲端 KMS)整合,以進行服務帳戶憑證的簽署和驗證。若要配置 kube-apiserver 使用 external-jwt-signer,請將 --service-account-signing-endpoint
旗標設定為檔案系統上 Unix 網域 Socket (UDS) 的位置,或以 @ 符號作為前綴並命名抽象 Socket 命名空間中的 UDS。在配置的 UDS 上,應有一個 RPC 伺服器實作 ExternalJWTSigner。外部權杖簽署者必須是健康的,並且準備好為 kube-apiserver 提供支援的服務帳戶金鑰,kube-apiserver 才能啟動。
請查看 KEP-740 以取得有關 ExternalJWTSigner 的更多詳細資訊。
注意
除非設定了--service-account-signing-endpoint
,否則 kube-apiserver 旗標 --service-account-key-file
和 --service-account-signing-key-file
將繼續用於從檔案讀取;它們是支援 JWT 簽署和驗證的互斥方式。清除
如果您建立了命名空間 examplens
進行實驗,您可以移除它
kubectl delete namespace examplens
下一步
- 閱讀更多關於投射卷的詳細資訊。