為 Pod 配置服務賬號

Kubernetes 為在叢集中執行的客戶端,或者與叢集的控制平面有關係的客戶端,提供了兩種不同的方式來向 API 伺服器進行身份驗證。

一個_服務賬戶_為 Pod 中執行的程序提供身份,並對映到 ServiceAccount 物件。當你向 API 伺服器進行身份驗證時,你將自己標識為一個特定的_使用者_。Kubernetes 承認使用者的概念,但是 Kubernetes 本身**不**具有使用者 API。

本任務指南是關於 Kubernetes API 中確實存在的 ServiceAccount。本指南向你展示了一些為 Pod 配置 ServiceAccount 的方法。

準備工作

你需要一個 Kubernetes 叢集,並且 `kubectl` 命令列工具必須配置為與你的叢集通訊。建議在至少有兩個不充當控制平面主機的節點組成的叢集上執行本教程。如果你還沒有叢集,可以使用 minikube 建立一個,或者使用這些 Kubernetes 線上演練:

使用預設服務賬戶訪問 API 伺服器

當 Pod 訪問 API 伺服器時,Pod 會以特定的 ServiceAccount(例如 `default`)進行身份驗證。每個名稱空間中始終至少有一個 ServiceAccount。

每個 Kubernetes 名稱空間都包含至少一個 ServiceAccount:該名稱空間的預設 ServiceAccount,名為 `default`。如果你在建立 Pod 時沒有指定 ServiceAccount,Kubernetes 會自動分配該名稱空間中名為 `default` 的 ServiceAccount。

你可以獲取已建立 Pod 的詳細資訊。例如:

kubectl get pods/<podname> -o yaml

在輸出中,你會看到一個欄位 `spec.serviceAccountName`。如果你在建立 Pod 時沒有指定,Kubernetes 會自動設定該值。

在 Pod 中執行的應用程式可以使用自動掛載的服務賬戶憑據訪問 Kubernetes API。參閱訪問叢集以瞭解更多資訊。

當 Pod 以 ServiceAccount 進行身份驗證時,其訪問級別取決於正在使用的授權外掛和策略

Pod 刪除後,API 憑據會自動撤銷,即使存在 finalizer 也是如此。具體來說,API 憑據在 Pod 上設定的 `metadata.deletionTimestamp` (刪除時間戳通常是接受**刪除**請求的時間加上 Pod 的終止寬限期) 後 60 秒撤銷。

選擇不自動掛載 API 憑據

如果你不希望 kubelet 自動掛載 ServiceAccount 的 API 憑據,你可以選擇退出預設行為。你可以透過在 ServiceAccount 上設定 `automountServiceAccountToken: false` 來選擇不自動掛載 ServiceAccount 的 API 憑據到 `/var/run/secrets/kubernetes.io/serviceaccount/token`。

例如

apiVersion: v1
kind: ServiceAccount
metadata:
  name: build-robot
automountServiceAccountToken: false
...

你也可以選擇不為特定 Pod 自動掛載 API 憑據:

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  serviceAccountName: build-robot
  automountServiceAccountToken: false
  ...

如果 ServiceAccount 和 Pod 的 `spec` 都為 `automountServiceAccountToken` 指定了一個值,則 Pod 的規約優先。

使用多個 ServiceAccount

每個名稱空間至少有一個 ServiceAccount:預設的 ServiceAccount 資源,名為 `default`。你可以使用以下命令列出當前名稱空間中的所有 ServiceAccount 資源:

kubectl get serviceaccounts

輸出類似於:

NAME      SECRETS    AGE
default   1          1d

你可以像這樣建立額外的 ServiceAccount 物件:

kubectl apply -f - <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
  name: build-robot
EOF

ServiceAccount 物件的名稱必須是有效的 DNS 子域名

如果你獲取服務賬戶物件的完整轉儲,像這樣:

kubectl get serviceaccounts/build-robot -o yaml

輸出類似於:

apiVersion: v1
kind: ServiceAccount
metadata:
  creationTimestamp: 2019-06-16T00:12:34Z
  name: build-robot
  namespace: default
  resourceVersion: "272500"
  uid: 721ab723-13bc-11e5-aec2-42010af0021e

你可以使用授權外掛設定服務賬戶的許可權

要使用非預設服務賬戶,請將 Pod 的 `spec.serviceAccountName` 欄位設定為你要使用的 ServiceAccount 的名稱。

你只能在建立 Pod 時或在新的 Pod 模板中設定 `serviceAccountName` 欄位。你不能更新已存在的 Pod 的 `.spec.serviceAccountName` 欄位。

清理

如果你嘗試從上面的示例中建立 `build-robot` ServiceAccount,可以透過執行以下命令進行清理:

kubectl delete serviceaccount/build-robot

手動為 ServiceAccount 建立 API 令牌

假設你有一個名為“build-robot”的現有服務賬戶,如前所述。

你可以使用 `kubectl` 獲取該 ServiceAccount 的有時效性 API 令牌:

kubectl create token build-robot

該命令的輸出是一個令牌,你可以使用它作為該 ServiceAccount 進行身份驗證。你可以使用 `kubectl create token` 命令的 `--duration` 命令列引數請求特定的令牌持續時間(發行的令牌的實際持續時間可能更短,甚至可能更長)。

特性狀態: Kubernetes v1.33 [stable] (預設啟用:true)

使用 `kubectl` v1.31 或更高版本,可以建立直接繫結到節點的 ServiceAccount 令牌:

kubectl create token build-robot --bound-object-kind Node --bound-object-name node-001 --bound-object-uid 123...456

令牌將一直有效,直到過期或關聯的節點或服務賬戶被刪除。

手動建立 ServiceAccount 的長期 API 令牌

如果你想獲取 ServiceAccount 的 API 令牌,你可以建立一個帶有特殊註解 `kubernetes.io/service-account.name` 的新 Secret。

kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
  name: build-robot-secret
  annotations:
    kubernetes.io/service-account.name: build-robot
type: kubernetes.io/service-account-token
EOF

如果你使用以下命令檢視 Secret:

kubectl get secret/build-robot-secret -o yaml

你可以看到 Secret 現在包含“build-robot”ServiceAccount 的 API 令牌。

由於你設定的註解,控制平面會自動為該 ServiceAccount 生成一個令牌,並將其儲存在關聯的 Secret 中。控制平面還會清理已刪除 ServiceAccount 的令牌。

kubectl describe secrets/build-robot-secret

輸出類似於:

Name:           build-robot-secret
Namespace:      default
Labels:         <none>
Annotations:    kubernetes.io/service-account.name: build-robot
                kubernetes.io/service-account.uid: da68f9c6-9d26-11e7-b84e-002dc52800da

Type:   kubernetes.io/service-account-token

Data
====
ca.crt:         1338 bytes
namespace:      7 bytes
token:          ...

當你刪除一個關聯了 Secret 的 ServiceAccount 時,Kubernetes 控制平面會自動從該 Secret 中清理掉長期令牌。

向服務賬戶新增 ImagePullSecrets

首先,建立一個 imagePullSecret。然後,驗證它是否已建立。例如:

  • 建立一個 imagePullSecret,如在 Pod 上指定 ImagePullSecrets 中所述。

    kubectl create secret docker-registry myregistrykey --docker-server=<registry name> \
            --docker-username=DUMMY_USERNAME --docker-password=DUMMY_DOCKER_PASSWORD \
            --docker-email=DUMMY_DOCKER_EMAIL
    
  • 驗證它是否已建立。

    kubectl get secrets myregistrykey
    

    輸出類似於:

    NAME             TYPE                              DATA    AGE
    myregistrykey    kubernetes.io/.dockerconfigjson   1       1d
    

將映象拉取 Secret 新增到服務賬戶

接下來,修改名稱空間的預設服務賬戶,以使用此 Secret 作為 imagePullSecret。

kubectl patch serviceaccount default -p '{"imagePullSecrets": [{"name": "myregistrykey"}]}'

你可以透過手動編輯物件實現相同的效果:

kubectl edit serviceaccount/default

`sa.yaml` 檔案的輸出類似於:

你選擇的文字編輯器將開啟,配置看起來像這樣:

apiVersion: v1
kind: ServiceAccount
metadata:
  creationTimestamp: 2021-07-07T22:02:39Z
  name: default
  namespace: default
  resourceVersion: "243024"
  uid: 052fb0f4-3d50-11e5-b066-42010af0d7b6

使用你的編輯器,刪除帶有鍵 `resourceVersion` 的行,新增 `imagePullSecrets:` 的行並儲存。保持 `uid` 值與你找到的相同。

進行這些更改後,編輯後的 ServiceAccount 看起來像這樣:

apiVersion: v1
kind: ServiceAccount
metadata:
  creationTimestamp: 2021-07-07T22:02:39Z
  name: default
  namespace: default
  uid: 052fb0f4-3d50-11e5-b066-42010af0d7b6
imagePullSecrets:
  - name: myregistrykey

驗證新 Pod 是否已設定 imagePullSecrets

現在,當在當前名稱空間中使用預設 ServiceAccount 建立新 Pod 時,新 Pod 的 `spec.imagePullSecrets` 欄位會自動設定:

kubectl run nginx --image=<registry name>/nginx --restart=Never
kubectl get pod nginx -o=jsonpath='{.spec.imagePullSecrets[0].name}{"\n"}'

輸出為:

myregistrykey

服務賬戶令牌卷投影

特性狀態: Kubernetes v1.20 [stable]

kubelet 還可以將 ServiceAccount 令牌投影到 Pod 中。你可以指定令牌的所需屬性,例如受眾和有效期。這些屬性在預設 ServiceAccount 令牌上是**不可**配置的。當 Pod 或 ServiceAccount 被刪除時,令牌也將對 API 失效。

你可以使用名為 `ServiceAccountToken` 的投影卷型別,為 Pod 的 `spec` 配置此行為。

此投影卷中的令牌是一個 JSON Web Token (JWT)。此令牌的 JSON 有效負載遵循定義明確的架構——一個 Pod 繫結令牌的有效負載示例:

{
  "aud": [  # matches the requested audiences, or the API server's default audiences when none are explicitly requested
    "https://kubernetes.default.svc"
  ],
  "exp": 1731613413,
  "iat": 1700077413,
  "iss": "https://kubernetes.default.svc",  # matches the first value passed to the --service-account-issuer flag
  "jti": "ea28ed49-2e11-4280-9ec5-bc3d1d84661a", 
  "kubernetes.io": {
    "namespace": "kube-system",
    "node": {
      "name": "127.0.0.1",
      "uid": "58456cb0-dd00-45ed-b797-5578fdceaced"
    },
    "pod": {
      "name": "coredns-69cbfb9798-jv9gn",
      "uid": "778a530c-b3f4-47c0-9cd5-ab018fb64f33"
    },
    "serviceaccount": {
      "name": "coredns",
      "uid": "a087d5a0-e1dd-43ec-93ac-f13d89cd13af"
    },
    "warnafter": 1700081020
  },
  "nbf": 1700077413,
  "sub": "system:serviceaccount:kube-system:coredns"
}

使用服務賬戶令牌投影啟動 Pod

為了向 Pod 提供一個受眾為 `vault` 且有效期為兩小時的令牌,你可以定義一個類似於以下內容的 Pod 清單:

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - image: nginx
    name: nginx
    volumeMounts:
    - mountPath: /var/run/secrets/tokens
      name: vault-token
  serviceAccountName: build-robot
  volumes:
  - name: vault-token
    projected:
      sources:
      - serviceAccountToken:
          path: vault-token
          expirationSeconds: 7200
          audience: vault

建立 Pod

kubectl create -f https://k8s.io/examples/pods/pod-projected-svc-token.yaml

kubelet 將:代表 Pod 請求並存儲令牌;將令牌透過可配置的檔案路徑提供給 Pod;並在令牌接近過期時重新整理它。如果令牌的有效期超過其總生命週期 (TTL) 的 80%,或者令牌超過 24 小時,kubelet 會主動請求輪換令牌。

應用程式負責在令牌輪換時重新載入令牌。對於應用程式來說,按計劃載入令牌(例如:每 5 分鐘一次),而不跟蹤實際過期時間,通常就足夠了。

服務賬戶頒發者發現

特性狀態: Kubernetes v1.21 [stable]

如果已在叢集中為 ServiceAccount 啟用了令牌投影,那麼你還可以使用發現功能。Kubernetes 提供了一種客戶端作為_身份提供者_進行聯邦的方式,以便一個或多個外部系統可以充當_依賴方_。

啟用後,Kubernetes API 伺服器透過 HTTP 釋出 OpenID Provider Configuration 文件。該配置文件釋出在 `/.well-known/openid-configuration`。OpenID Provider Configuration 有時被稱為_發現文件_。Kubernetes API 伺服器還會透過 HTTP 在 `/openid/v1/jwks` 釋出相關的 JSON Web Key Set (JWKS)。

使用 RBAC 的叢集包含一個名為 `system:service-account-issuer-discovery` 的預設 ClusterRole。一個預設的 ClusterRoleBinding 將此角色分配給 `system:serviceaccounts` 組,所有 ServiceAccount 都隱式屬於該組。這允許在叢集上執行的 Pod 透過其掛載的服務賬戶令牌訪問服務賬戶發現文件。管理員還可以根據其安全要求以及他們打算與之聯邦的外部系統,選擇將該角色繫結到 `system:authenticated` 或 `system:unauthenticated`。

JWKS 響應包含依賴方可用於驗證 Kubernetes 服務賬戶令牌的公鑰。依賴方首先查詢 OpenID Provider Configuration,並使用響應中的 `jwks_uri` 欄位查詢 JWKS。

在許多情況下,Kubernetes API 伺服器無法在公共網際網路上訪問,但使用者或服務提供商可以提供從 API 伺服器快取響應的公共端點。在這些情況下,可以透過向 API 伺服器傳遞 `--service-account-jwks-uri` 標誌來覆蓋 OpenID Provider Configuration 中的 `jwks_uri`,使其指向公共端點,而不是 API 伺服器的地址。與頒發者 URL 一樣,JWKS URI 要求使用 `https` 方案。

下一步

另請參閱