身分驗證

本頁面概述身分驗證。

Kubernetes 中的使用者

所有 Kubernetes 叢集都有兩類使用者:由 Kubernetes 管理的服務帳戶和一般使用者。

假設獨立於叢集的服務以下列方式管理一般使用者

  • 管理員分發私鑰
  • 使用者儲存區,例如 Keystone 或 Google 帳戶
  • 包含使用者名稱和密碼清單的檔案

在這方面,Kubernetes 沒有代表一般使用者帳戶的物件。 一般使用者無法透過 API 呼叫新增至叢集。

即使無法透過 API 呼叫新增一般使用者,任何出示由叢集憑證授權機構 (CA) 簽署的有效憑證的使用者都會被視為已通過身分驗證。在此配置中,Kubernetes 從憑證「主旨」的通用名稱欄位中判斷使用者名稱(例如,「/CN=bob」)。從那裡,基於角色的存取控制 (RBAC) 子系統將判斷使用者是否被授權對資源執行特定操作。如需更多詳細資訊,請參閱憑證請求中的一般使用者主題,以取得更多相關詳細資訊。

相反地,服務帳戶是由 Kubernetes API 管理的使用者。它們繫結到特定的命名空間,並由 API 伺服器自動建立,或透過 API 呼叫手動建立。服務帳戶與一組儲存為 Secrets 的憑證相關聯,這些憑證會掛載到 Pod 中,允許叢集內程序與 Kubernetes API 通訊。

API 請求會繫結到一般使用者或服務帳戶,或者被視為匿名請求。這表示叢集內部或外部的每個程序,從在工作站上輸入 kubectl 的人類使用者,到節點上的 kubelets,再到控制平面的成員,都必須在向 API 伺服器發出請求時進行身分驗證,否則將被視為匿名使用者。

身分驗證策略

Kubernetes 使用用戶端憑證、持有者 Token 或身分驗證代理,透過身分驗證外掛程式來驗證 API 請求。當向 API 伺服器發出 HTTP 請求時,外掛程式會嘗試將以下屬性與請求關聯

  • 使用者名稱:識別最終使用者的字串。常見的值可能是 kube-adminjane@example.com
  • UID:識別最終使用者的字串,並嘗試比使用者名稱更一致和唯一。
  • 群組:一組字串,每個字串都表示使用者在已命名的使用者邏輯集合中的成員資格。常見的值可能是 system:mastersdevops-team
  • 額外欄位:字串到字串清單的對應,其中包含授權者可能認為有用的其他資訊。

所有值對身分驗證系統而言都是不透明的,並且僅在由授權者解譯時才具有意義。

您可以同時啟用多種身分驗證方法。您通常應至少使用兩種方法

  • 用於服務帳戶的服務帳戶 Token
  • 至少一種其他使用者身分驗證方法。

當啟用多個驗證器模組時,第一個成功驗證請求的模組會短路評估。API 伺服器不保證驗證器運行的順序。

system:authenticated 群組包含在所有已驗證使用者的群組列表中。

與其他身份驗證協定(LDAP、SAML、Kerberos、替代 x509 方案等)的整合可以使用驗證 Proxy身份驗證 Webhook 來完成。

X509 用戶端憑證

用戶端憑證驗證是透過將 --client-ca-file=SOMEFILE 選項傳遞給 API 伺服器來啟用的。引用的檔案必須包含一個或多個憑證授權單位,用於驗證提交給 API 伺服器的用戶端憑證。如果用戶端憑證被提交並驗證,則主體的通用名稱將用作請求的使用者名稱。從 Kubernetes 1.4 開始,用戶端憑證也可以使用憑證的組織欄位來指示使用者的群組成員資格。若要包含使用者的多個群組成員資格,請在憑證中包含多個組織欄位。

例如,使用 openssl 命令列工具產生憑證簽署請求

openssl req -new -key jbeda.pem -out jbeda-csr.pem -subj "/CN=jbeda/O=app1/O=app2"

這將為使用者名稱 "jbeda" 建立 CSR,該使用者屬於兩個群組 "app1" 和 "app2"。

請參閱管理憑證,了解如何產生用戶端憑證。

靜態權杖檔案

當在命令列上給定 --token-auth-file=SOMEFILE 選項時,API 伺服器會從檔案中讀取持有者權杖。目前,權杖無限期有效,並且在不重新啟動 API 伺服器的情況下,無法變更權杖列表。

權杖檔案是一個 csv 檔案,至少包含 3 個欄位:權杖、使用者名稱、使用者 uid,後跟可選的群組名稱。

在請求中放入持有者權杖

當從 http 用戶端使用持有者權杖驗證時,API 伺服器期望有一個 Authorization 標頭,其值為 Bearer <token>。持有者權杖必須是一個字元序列,可以使用 HTTP 的編碼和引號功能放入 HTTP 標頭值中。例如:如果持有者權杖是 31ada4fd-adec-460c-809a-9e56ceb75269,那麼它將在 HTTP 標頭中顯示如下。

Authorization: Bearer 31ada4fd-adec-460c-809a-9e56ceb75269

啟動引導權杖

功能狀態: Kubernetes v1.18 [stable]

為了允許新叢集進行簡化的啟動引導,Kubernetes 包含一種動態管理的持有者權杖類型,稱為啟動引導權杖。這些權杖作為 Secret 儲存在 kube-system 命名空間中,可以在其中動態管理和建立。控制器管理器包含一個 TokenCleaner 控制器,該控制器會在啟動引導權杖過期時將其刪除。

權杖的形式為 [a-z0-9]{6}.[a-z0-9]{16}。第一個組件是權杖 ID,第二個組件是權杖密碼。您可以在 HTTP 標頭中指定權杖,如下所示

Authorization: Bearer 781292.db7bc3a58fc5f07e

您必須在 API 伺服器上使用 --enable-bootstrap-token-auth 旗標啟用啟動引導權杖驗證器。您必須透過控制器管理器的 --controllers 旗標啟用 TokenCleaner 控制器。這是透過類似 --controllers=*,tokencleaner 的方式完成的。如果您使用 kubeadm 來啟動引導叢集,kubeadm 將為您執行此操作。

驗證器驗證為 system:bootstrap:<Token ID>。它包含在 system:bootstrappers 群組中。命名和群組刻意受到限制,以勸阻使用者在啟動引導後使用這些權杖。使用者名稱和群組可以用於(並且由 kubeadm 使用)製作適當的授權策略,以支援啟動引導叢集。

請參閱啟動引導權杖,以深入了解啟動引導權杖驗證器和控制器的文件,以及如何使用 kubeadm 管理這些權杖。

服務帳戶權杖

服務帳戶是一種自動啟用的驗證器,它使用簽署的持有者權杖來驗證請求。此外掛程式接受兩個可選旗標

  • --service-account-key-file 包含 PEM 編碼的 x509 RSA 或 ECDSA 私鑰或公鑰的檔案,用於驗證 ServiceAccount 權杖。指定的檔案可以包含多個金鑰,並且可以多次指定此旗標,使用不同的檔案。如果未指定,則使用 --tls-private-key-file。
  • --service-account-lookup 如果啟用,則從 API 刪除的權杖將被撤銷。

服務帳戶通常由 API 伺服器自動建立,並透過 ServiceAccount 准入控制器 與叢集中運行的 Pods 關聯。持有者權杖掛載到 Pods 中眾所周知的位置,並允許叢集內程序與 API 伺服器對話。可以使用 PodSpecserviceAccountName 欄位將帳戶明確地與 Pods 關聯。

apiVersion: apps/v1 # this apiVersion is relevant as of Kubernetes 1.9
kind: Deployment
metadata:
  name: nginx-deployment
  namespace: default
spec:
  replicas: 3
  template:
    metadata:
    # ...
    spec:
      serviceAccountName: bob-the-bot
      containers:
      - name: nginx
        image: nginx:1.14.2

服務帳戶持有者權杖在叢集外部使用完全有效,並且可用於為希望與 Kubernetes API 對話的長期運行的作業建立身分。若要手動建立服務帳戶,請使用 kubectl create serviceaccount (NAME) 命令。這會在目前的命名空間中建立服務帳戶。

kubectl create serviceaccount jenkins
serviceaccount/jenkins created

建立關聯的權杖

kubectl create token jenkins
eyJhbGciOiJSUzI1NiIsImtp...

建立的權杖是簽署的 JSON Web 權杖 (JWT)。

簽署的 JWT 可以用作持有者權杖,以服務帳戶的身分進行驗證。請參閱上方,了解權杖如何包含在請求中。通常,這些權杖會掛載到 Pods 中,以供叢集內存取 API 伺服器,但也可以從叢集外部使用。

服務帳戶使用使用者名稱 system:serviceaccount:(NAMESPACE):(SERVICEACCOUNT) 進行驗證,並被分配到群組 system:serviceaccountssystem:serviceaccounts:(NAMESPACE)

OpenID Connect 權杖

OpenID Connect 是 OAuth2 的一種變體,受到某些 OAuth2 提供者的支援,特別是 Microsoft Entra ID、Salesforce 和 Google。該協定對 OAuth2 的主要擴展是在存取權杖中傳回的附加欄位,稱為 ID 權杖。此權杖是一個 JSON Web 權杖 (JWT),具有眾所周知的欄位,例如使用者的電子郵件,並由伺服器簽署。

為了識別使用者,驗證器使用 OAuth2 權杖回應 中的 id_token(而不是 access_token)作為持有者權杖。請參閱上方,了解權杖如何包含在請求中。

sequenceDiagram participant user as User participant idp as Identity Provider participant kube as kubectl participant api as API Server user ->> idp: 1. 登入 IdP activate idp idp -->> user: 2. 提供 access_token,
id_token, 和 refresh_token deactivate idp activate user user ->> kube: 3. 呼叫 kubectl
使用 --token 作為 id_token
OR 將權杖新增至 .kube/config deactivate user activate kube kube ->> api: 4. Authorization: Bearer... deactivate kube activate api api ->> api: 5. JWT 簽名是否有效? api ->> api: 6. JWT 是否已過期? (iat+exp) api ->> api: 7. 使用者已授權? api -->> kube: 8. 已授權:執行
動作並傳回結果 deactivate api activate kube kube --x user: 9. 傳回結果 deactivate kube
  1. 登入您的身份提供者

  2. 您的身份提供者將為您提供 access_tokenid_tokenrefresh_token

  3. 當使用 kubectl 時,請使用您的 id_token--token 旗標,或直接將其新增至您的 kubeconfig

  4. kubectl 會在標頭中將您的 id_token 名為 Authorization 發送至 API 伺服器

  5. API 伺服器將確保 JWT 簽名有效

  6. 檢查以確保 id_token 尚未過期

    如果使用 AuthenticationConfiguration 組態了 CEL 表達式,則執行宣告和/或使用者驗證。

  7. 確保使用者已授權

  8. 一旦授權,API 伺服器會將回應傳回給 kubectl

  9. kubectl 向使用者提供回饋

由於驗證您身分所需的所有資料都在 id_token 中,因此 Kubernetes 不需要「連回家」到身份提供者。在每個請求都是無狀態的模型中,這為身份驗證提供了一個非常可擴展的解決方案。它確實提供了一些挑戰

  1. Kubernetes 沒有「網頁介面」來觸發身份驗證過程。沒有瀏覽器或介面來收集憑證,這就是為什麼您需要先向您的身份提供者驗證身份的原因。
  2. id_token 無法撤銷,它就像憑證一樣,因此應該是短效期的(只有幾分鐘),因此每隔幾分鐘就必須取得新的權杖可能會非常煩人。
  3. 若要向 Kubernetes 儀表板驗證身份,您必須使用 kubectl proxy 命令或注入 id_token 的反向 Proxy。

組態 API 伺服器

使用旗標

若要啟用此外掛程式,請在 API 伺服器上組態以下旗標

參數描述範例必要
--oidc-issuer-url提供者的 URL,允許 API 伺服器探索公開簽名金鑰。僅接受使用 https:// 方案的 URL。這通常是提供者的探索 URL,變更為具有空的路徑。如果發行者的 OIDC 探索 URL 是 https://accounts.provider.example/.well-known/openid-configuration,則該值應為 https://accounts.provider.example
--oidc-client-id所有權杖都必須為其發行的用戶端 ID。kubernetes
--oidc-username-claim要用作使用者名稱的 JWT 宣告。預設為 sub,預期它是最終使用者的唯一識別碼。管理員可以根據其提供者選擇其他宣告,例如 emailname。但是,除了 email 之外的宣告將以發行者 URL 作為前綴,以防止與其他外掛程式發生命名衝突。sub
--oidc-username-prefix前置於使用者名稱宣告的前綴,以防止與現有名稱(例如 system: 使用者)發生衝突。例如,值 oidc: 將建立類似 oidc:jane.doe 的使用者名稱。如果未提供此旗標,並且 --oidc-username-claimemail 以外的值,則前綴預設為 ( Issuer URL )#,其中 ( Issuer URL )--oidc-issuer-url 的值。值 - 可用於停用所有前綴。oidc
--oidc-groups-claim要用作使用者群組的 JWT 宣告。如果宣告存在,則它必須是字串陣列。groups
--oidc-groups-prefix前置於群組宣告的前綴,以防止與現有名稱(例如 system: 群組)發生衝突。例如,值 oidc: 將建立類似 oidc:engineeringoidc:infra 的群組名稱。oidc
--oidc-required-claim一個 key=value 對,描述 ID 權杖中必要的宣告。如果設定,則驗證宣告是否存在於 ID 權杖中,並具有相符的值。重複此旗標以指定多個宣告。claim=value
--oidc-ca-file簽署您的身份提供者的網站憑證的 CA 憑證路徑。預設為主機的根 CA。/etc/kubernetes/ssl/kc-ca.pem
--oidc-signing-algs接受的簽名演算法。預設為 "RS256"。RS512
從檔案進行身份驗證組態
功能狀態: Kubernetes v1.30 [beta] (預設啟用:true)

JWT 驗證器是一個驗證器,用於使用符合 JWT 規範的權杖來驗證 Kubernetes 使用者。驗證器將嘗試剖析原始 ID 權杖,驗證它是否已由組態的發行者簽署。用於驗證簽名的公開金鑰是從發行者的公開端點使用 OIDC 探索來發現的。

最小的有效 JWT 酬載必須包含以下宣告

{
  "iss": "https://example.com",   // must match the issuer.url
  "aud": ["my-app"],              // at least one of the entries in issuer.audiences must match the "aud" claim in presented JWTs.
  "exp": 1234567890,              // token expiration as Unix time (the number of seconds elapsed since January 1, 1970 UTC)
  "<username-claim>": "user"      // this is the username claim configured in the claimMappings.username.claim or claimMappings.username.expression
}

組態檔案方法允許您組態多個 JWT 驗證器,每個驗證器都具有唯一的 issuer.urlissuer.discoveryURL。組態檔案甚至允許您指定 CEL 表達式,以將宣告映射到使用者屬性,並驗證宣告和使用者資訊。當組態檔案被修改時,API 伺服器也會自動重新載入驗證器。您可以使用 apiserver_authentication_config_controller_automatic_reload_last_timestamp_seconds 指標來監控 API 伺服器上次重新載入組態的時間。

您必須使用 API 伺服器上的 --authentication-config 旗標指定身份驗證組態的路徑。如果您想使用命令列旗標而不是組態檔案,這些旗標將繼續按原樣工作。若要存取新功能,例如組態多個驗證器、為發行者設定多個受眾,請切換為使用組態檔案。

對於 Kubernetes v1.32,結構化身份驗證組態檔案格式為 Beta 級別,並且使用該組態的機制也為 Beta 級別。如果您沒有特別停用叢集的 StructuredAuthenticationConfiguration 功能閘道,則可以透過指定 kube-apiserver 的 --authentication-config 命令列引數來啟用結構化身份驗證。結構化身份驗證組態檔案的範例如下所示。

---
#
# CAUTION: this is an example configuration.
#          Do not use this for your own cluster!
#
apiVersion: apiserver.config.k8s.io/v1beta1
kind: AuthenticationConfiguration
# list of authenticators to authenticate Kubernetes users using JWT compliant tokens.
# the maximum number of allowed authenticators is 64.
jwt:
- issuer:
    # url must be unique across all authenticators.
    # url must not conflict with issuer configured in --service-account-issuer.
    url: https://example.com # Same as --oidc-issuer-url.
    # discoveryURL, if specified, overrides the URL used to fetch discovery
    # information instead of using "{url}/.well-known/openid-configuration".
    # The exact value specified is used, so "/.well-known/openid-configuration"
    # must be included in discoveryURL if needed.
    #
    # The "issuer" field in the fetched discovery information must match the "issuer.url" field
    # in the AuthenticationConfiguration and will be used to validate the "iss" claim in the presented JWT.
    # This is for scenarios where the well-known and jwks endpoints are hosted at a different
    # location than the issuer (such as locally in the cluster).
    # discoveryURL must be different from url if specified and must be unique across all authenticators.
    discoveryURL: https://discovery.example.com/.well-known/openid-configuration
    # PEM encoded CA certificates used to validate the connection when fetching
    # discovery information. If not set, the system verifier will be used.
    # Same value as the content of the file referenced by the --oidc-ca-file flag.
    certificateAuthority: <PEM encoded CA certificates>    
    # audiences is the set of acceptable audiences the JWT must be issued to.
    # At least one of the entries must match the "aud" claim in presented JWTs.
    audiences:
    - my-app # Same as --oidc-client-id.
    - my-other-app
    # this is required to be set to "MatchAny" when multiple audiences are specified.
    audienceMatchPolicy: MatchAny
  # rules applied to validate token claims to authenticate users.
  claimValidationRules:
    # Same as --oidc-required-claim key=value.
  - claim: hd
    requiredValue: example.com
    # Instead of claim and requiredValue, you can use expression to validate the claim.
    # expression is a CEL expression that evaluates to a boolean.
    # all the expressions must evaluate to true for validation to succeed.
  - expression: 'claims.hd == "example.com"'
    # Message customizes the error message seen in the API server logs when the validation fails.
    message: the hd claim must be set to example.com
  - expression: 'claims.exp - claims.nbf <= 86400'
    message: total token lifetime must not exceed 24 hours
  claimMappings:
    # username represents an option for the username attribute.
    # This is the only required attribute.
    username:
      # Same as --oidc-username-claim. Mutually exclusive with username.expression.
      claim: "sub"
      # Same as --oidc-username-prefix. Mutually exclusive with username.expression.
      # if username.claim is set, username.prefix is required.
      # Explicitly set it to "" if no prefix is desired.
      prefix: ""
      # Mutually exclusive with username.claim and username.prefix.
      # expression is a CEL expression that evaluates to a string.
      #
      # 1.  If username.expression uses 'claims.email', then 'claims.email_verified' must be used in
      #     username.expression or extra[*].valueExpression or claimValidationRules[*].expression.
      #     An example claim validation rule expression that matches the validation automatically
      #     applied when username.claim is set to 'email' is 'claims.?email_verified.orValue(true)'.
      # 2.  If the username asserted based on username.expression is the empty string, the authentication
      #     request will fail.
      expression: 'claims.username + ":external-user"'
    # groups represents an option for the groups attribute.
    groups:
      # Same as --oidc-groups-claim. Mutually exclusive with groups.expression.
      claim: "sub"
      # Same as --oidc-groups-prefix. Mutually exclusive with groups.expression.
      # if groups.claim is set, groups.prefix is required.
      # Explicitly set it to "" if no prefix is desired.
      prefix: ""
      # Mutually exclusive with groups.claim and groups.prefix.
      # expression is a CEL expression that evaluates to a string or a list of strings.
      expression: 'claims.roles.split(",")'
    # uid represents an option for the uid attribute.
    uid:
      # Mutually exclusive with uid.expression.
      claim: 'sub'
      # Mutually exclusive with uid.claim
      # expression is a CEL expression that evaluates to a string.
      expression: 'claims.sub'
    # extra attributes to be added to the UserInfo object. Keys must be domain-prefix path and must be unique.
    extra:
      # key is a string to use as the extra attribute key.
      # key must be a domain-prefix path (e.g. example.org/foo). All characters before the first "/" must be a valid
      # subdomain as defined by RFC 1123. All characters trailing the first "/" must
      # be valid HTTP Path characters as defined by RFC 3986.
      # k8s.io, kubernetes.io and their subdomains are reserved for Kubernetes use and cannot be used.
      # key must be lowercase and unique across all extra attributes.
    - key: 'example.com/tenant'
      # valueExpression is a CEL expression that evaluates to a string or a list of strings.
      valueExpression: 'claims.tenant'
  # validation rules applied to the final user object.
  userValidationRules:
    # expression is a CEL expression that evaluates to a boolean.
    # all the expressions must evaluate to true for the user to be valid.
  - expression: "!user.username.startsWith('system:')"
    # Message customizes the error message seen in the API server logs when the validation fails.
    message: 'username cannot used reserved system: prefix'
  - expression: "user.groups.all(group, !group.startsWith('system:'))"
    message: 'groups cannot used reserved system: prefix'
  • 宣告驗證規則表達式

    jwt.claimValidationRules[i].expression 表示將由 CEL 評估的表達式。CEL 表達式可以存取權杖酬載的內容,組織成 claims CEL 變數。claims 是宣告名稱(作為字串)到宣告值(任何類型)的映射。

  • 使用者驗證規則表達式

    jwt.userValidationRules[i].expression 表示將由 CEL 評估的表達式。CEL 表達式可以存取 userInfo 的內容,組織成 user CEL 變數。有關 user 的架構,請參閱 UserInfo API 文件。

  • 宣告映射表達式

    jwt.claimMappings.username.expressionjwt.claimMappings.groups.expressionjwt.claimMappings.uid.expression jwt.claimMappings.extra[i].valueExpression 表示將由 CEL 評估的表達式。CEL 表達式可以存取權杖酬載的內容,組織成 claims CEL 變數。claims 是宣告名稱(作為字串)到宣告值(任何類型)的映射。

    若要了解更多資訊,請參閱 CEL 文件

    以下是具有不同權杖酬載的 AuthenticationConfiguration 範例。

    apiVersion: apiserver.config.k8s.io/v1beta1
    kind: AuthenticationConfiguration
    jwt:
    - issuer:
        url: https://example.com
        audiences:
        - my-app
      claimMappings:
        username:
          expression: 'claims.username + ":external-user"'
        groups:
          expression: 'claims.roles.split(",")'
        uid:
          expression: 'claims.sub'
        extra:
        - key: 'example.com/tenant'
          valueExpression: 'claims.tenant'
      userValidationRules:
      - expression: "!user.username.startsWith('system:')" # the expression will evaluate to true, so validation will succeed.
        message: 'username cannot used reserved system: prefix'
    
    TOKEN=eyJhbGciOiJSUzI1NiIsImtpZCI6ImY3dF9tOEROWmFTQk1oWGw5QXZTWGhBUC04Y0JmZ0JVbFVpTG5oQkgxdXMiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJrdWJlcm5ldGVzIiwiZXhwIjoxNzAzMjMyOTQ5LCJpYXQiOjE3MDExMDcyMzMsImlzcyI6Imh0dHBzOi8vZXhhbXBsZS5jb20iLCJqdGkiOiI3YzMzNzk0MjgwN2U3M2NhYTJjMzBjODY4YWMwY2U5MTBiY2UwMmRkY2JmZWJlOGMyM2I4YjVmMjdhZDYyODczIiwibmJmIjoxNzAxMTA3MjMzLCJyb2xlcyI6InVzZXIsYWRtaW4iLCJzdWIiOiJhdXRoIiwidGVuYW50IjoiNzJmOTg4YmYtODZmMS00MWFmLTkxYWItMmQ3Y2QwMTFkYjRhIiwidXNlcm5hbWUiOiJmb28ifQ.TBWF2RkQHm4QQz85AYPcwLxSk-VLvQW-mNDHx7SEOSv9LVwcPYPuPajJpuQn9C_gKq1R94QKSQ5F6UgHMILz8OfmPKmX_00wpwwNVGeevJ79ieX2V-__W56iNR5gJ-i9nn6FYk5pwfVREB0l4HSlpTOmu80gbPWAXY5hLW0ZtcE1JTEEmefORHV2ge8e3jp1xGafNy6LdJWabYuKiw8d7Qga__HxtKB-t0kRMNzLRS7rka_SfQg0dSYektuxhLbiDkqhmRffGlQKXGVzUsuvFw7IGM5ZWnZgEMDzCI357obHeM3tRqpn5WRjtB8oM7JgnCymaJi-P3iCd88iu1xnzA
    

    其中權杖酬載為

      {
        "aud": "kubernetes",
        "exp": 1703232949,
        "iat": 1701107233,
        "iss": "https://example.com",
        "jti": "7c337942807e73caa2c30c868ac0ce910bce02ddcbfebe8c23b8b5f27ad62873",
        "nbf": 1701107233,
        "roles": "user,admin",
        "sub": "auth",
        "tenant": "72f988bf-86f1-41af-91ab-2d7cd011db4a",
        "username": "foo"
      }
    

    具有上述 AuthenticationConfiguration 的權杖將產生以下 UserInfo 物件,並成功驗證使用者。

    {
        "username": "foo:external-user",
        "uid": "auth",
        "groups": [
            "user",
            "admin"
        ],
        "extra": {
            "example.com/tenant": "72f988bf-86f1-41af-91ab-2d7cd011db4a"
        }
    }
    

    apiVersion: apiserver.config.k8s.io/v1beta1
    kind: AuthenticationConfiguration
    jwt:
    - issuer:
        url: https://example.com
        audiences:
        - my-app
      claimValidationRules:
      - expression: 'claims.hd == "example.com"' # the token below does not have this claim, so validation will fail.
        message: the hd claim must be set to example.com
      claimMappings:
        username:
          expression: 'claims.username + ":external-user"'
        groups:
          expression: 'claims.roles.split(",")'
        uid:
          expression: 'claims.sub'
        extra:
        - key: 'example.com/tenant'
          valueExpression: 'claims.tenant'
      userValidationRules:
      - expression: "!user.username.startsWith('system:')" # the expression will evaluate to true, so validation will succeed.
        message: 'username cannot used reserved system: prefix'
    
    TOKEN=eyJhbGciOiJSUzI1NiIsImtpZCI6ImY3dF9tOEROWmFTQk1oWGw5QXZTWGhBUC04Y0JmZ0JVbFVpTG5oQkgxdXMiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJrdWJlcm5ldGVzIiwiZXhwIjoxNzAzMjMyOTQ5LCJpYXQiOjE3MDExMDcyMzMsImlzcyI6Imh0dHBzOi8vZXhhbXBsZS5jb20iLCJqdGkiOiI3YzMzNzk0MjgwN2U3M2NhYTJjMzBjODY4YWMwY2U5MTBiY2UwMmRkY2JmZWJlOGMyM2I4YjVmMjdhZDYyODczIiwibmJmIjoxNzAxMTA3MjMzLCJyb2xlcyI6InVzZXIsYWRtaW4iLCJzdWIiOiJhdXRoIiwidGVuYW50IjoiNzJmOTg4YmYtODZmMS00MWFmLTkxYWItMmQ3Y2QwMTFkYjRhIiwidXNlcm5hbWUiOiJmb28ifQ.TBWF2RkQHm4QQz85AYPcwLxSk-VLvQW-mNDHx7SEOSv9LVwcPYPuPajJpuQn9C_gKq1R94QKSQ5F6UgHMILz8OfmPKmX_00wpwwNVGeevJ79ieX2V-__W56iNR5gJ-i9nn6FYk5pwfVREB0l4HSlpTOmu80gbPWAXY5hLW0ZtcE1JTEEmefORHV2ge8e3jp1xGafNy6LdJWabYuKiw8d7Qga__HxtKB-t0kRMNzLRS7rka_SfQg0dSYektuxhLbiDkqhmRffGlQKXGVzUsuvFw7IGM5ZWnZgEMDzCI357obHeM3tRqpn5WRjtB8oM7JgnCymaJi-P3iCd88iu1xnzA
    

    其中權杖酬載為

      {
        "aud": "kubernetes",
        "exp": 1703232949,
        "iat": 1701107233,
        "iss": "https://example.com",
        "jti": "7c337942807e73caa2c30c868ac0ce910bce02ddcbfebe8c23b8b5f27ad62873",
        "nbf": 1701107233,
        "roles": "user,admin",
        "sub": "auth",
        "tenant": "72f988bf-86f1-41af-91ab-2d7cd011db4a",
        "username": "foo"
      }
    

    具有上述 AuthenticationConfiguration 的權杖將無法驗證,因為 hd 宣告未設定為 example.com。API 伺服器將傳回 401 Unauthorized 錯誤。

    apiVersion: apiserver.config.k8s.io/v1beta1
    kind: AuthenticationConfiguration
    jwt:
    - issuer:
        url: https://example.com
        audiences:
        - my-app
      claimValidationRules:
      - expression: 'claims.hd == "example.com"'
        message: the hd claim must be set to example.com
      claimMappings:
        username:
          expression: '"system:" + claims.username' # this will prefix the username with "system:" and will fail user validation.
        groups:
          expression: 'claims.roles.split(",")'
        uid:
          expression: 'claims.sub'
        extra:
        - key: 'example.com/tenant'
          valueExpression: 'claims.tenant'
      userValidationRules:
      - expression: "!user.username.startsWith('system:')" # the username will be system:foo and expression will evaluate to false, so validation will fail.
        message: 'username cannot used reserved system: prefix'
    
    TOKEN=eyJhbGciOiJSUzI1NiIsImtpZCI6ImY3dF9tOEROWmFTQk1oWGw5QXZTWGhBUC04Y0JmZ0JVbFVpTG5oQkgxdXMiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJrdWJlcm5ldGVzIiwiZXhwIjoxNzAzMjMyOTQ5LCJoZCI6ImV4YW1wbGUuY29tIiwiaWF0IjoxNzAxMTEzMTAxLCJpc3MiOiJodHRwczovL2V4YW1wbGUuY29tIiwianRpIjoiYjViMDY1MjM3MmNkMjBlMzQ1YjZmZGZmY2RjMjE4MWY0YWZkNmYyNTlhYWI0YjdlMzU4ODEyMzdkMjkyMjBiYyIsIm5iZiI6MTcwMTExMzEwMSwicm9sZXMiOiJ1c2VyLGFkbWluIiwic3ViIjoiYXV0aCIsInRlbmFudCI6IjcyZjk4OGJmLTg2ZjEtNDFhZi05MWFiLTJkN2NkMDExZGI0YSIsInVzZXJuYW1lIjoiZm9vIn0.FgPJBYLobo9jnbHreooBlvpgEcSPWnKfX6dc0IvdlRB-F0dCcgy91oCJeK_aBk-8zH5AKUXoFTlInfLCkPivMOJqMECA1YTrMUwt_IVqwb116AqihfByUYIIqzMjvUbthtbpIeHQm2fF0HbrUqa_Q0uaYwgy8mD807h7sBcUMjNd215ff_nFIHss-9zegH8GI1d9fiBf-g6zjkR1j987EP748khpQh9IxPjMJbSgG_uH5x80YFuqgEWwq-aYJPQxXX6FatP96a2EAn7wfPpGlPRt0HcBOvq5pCnudgCgfVgiOJiLr_7robQu4T1bis0W75VPEvwWtgFcLnvcQx0JWg
    

    其中權杖酬載為

      {
        "aud": "kubernetes",
        "exp": 1703232949,
        "hd": "example.com",
        "iat": 1701113101,
        "iss": "https://example.com",
        "jti": "b5b0652372cd20e345b6fdffcdc2181f4afd6f259aab4b7e35881237d29220bc",
        "nbf": 1701113101,
        "roles": "user,admin",
        "sub": "auth",
        "tenant": "72f988bf-86f1-41af-91ab-2d7cd011db4a",
        "username": "foo"
      }
    

    具有上述 AuthenticationConfiguration 的權杖將產生以下 UserInfo 物件

    {
        "username": "system:foo",
        "uid": "auth",
        "groups": [
            "user",
            "admin"
        ],
        "extra": {
            "example.com/tenant": "72f988bf-86f1-41af-91ab-2d7cd011db4a"
        }
    }
    

    由於使用者名稱以 system: 開頭,因此使用者驗證將失敗。API 伺服器將傳回 401 Unauthorized 錯誤。

限制
  1. 分散式宣告無法透過 CEL 表達式運作。
  2. issuer.urlissuer.discoveryURL 的呼叫不支援出口選擇器組態。

Kubernetes 不提供 OpenID Connect 身份提供者。您可以使用現有的公開 OpenID Connect 身份提供者(例如 Google 或 其他)。或者,您可以運行自己的身份提供者,例如 dexKeycloak、CloudFoundry UAA 或 Tremolo Security 的 OpenUnison

為了使身份提供者與 Kubernetes 協同工作,它必須

  1. 支援 OpenID connect 探索

    用於驗證簽名的公開金鑰是從發行者的公開端點使用 OIDC 探索來發現的。如果您使用身份驗證組態檔案,則身份提供者不需要公開公開探索端點。您可以將探索端點託管在與發行者不同的位置(例如叢集本地),並在組態檔案中指定 issuer.discoveryURL

  2. 在 TLS 中使用非過時的密碼運行

  3. 具有 CA 簽署的憑證(即使 CA 不是商業 CA 或自簽)

關於上述要求 #3,要求 CA 簽署的憑證。如果您部署自己的身份提供者(而不是像 Google 或 Microsoft 這樣的雲端提供者之一),您必須讓您的身份提供者的網站伺服器憑證由 CA 旗標設定為 TRUE 的憑證簽署,即使它是自簽的。這是因為 GoLang 的 TLS 用戶端實作對憑證驗證標準非常嚴格。如果您沒有可用的 CA,您可以使用 Dex 團隊的 gencert 腳本 來建立簡單的 CA 以及簽署的憑證和金鑰對。或者,您可以使用 這個類似的腳本,它會產生具有更長壽命和更大金鑰大小的 SHA256 憑證。

請參閱特定系統的設定說明

使用 kubectl

選項 1 - OIDC 驗證器

第一個選項是使用 kubectl oidc 驗證器,它將 id_token 設定為所有請求的持有者權杖,並在權杖過期後刷新權杖。在您登入提供者後,請使用 kubectl 新增您的 id_tokenrefresh_tokenclient_idclient_secret 以組態此外掛程式。

此插件不支援在刷新權杖回應中不傳回 id_token 的提供者,應使用下面的「選項 2」。

kubectl config set-credentials USER_NAME \
   --auth-provider=oidc \
   --auth-provider-arg=idp-issuer-url=( issuer url ) \
   --auth-provider-arg=client-id=( your client id ) \
   --auth-provider-arg=client-secret=( your client secret ) \
   --auth-provider-arg=refresh-token=( your refresh token ) \
   --auth-provider-arg=idp-certificate-authority=( path to your ca certificate ) \
   --auth-provider-arg=id-token=( your id_token )

例如,在向您的身份提供者驗證身份後運行以下命令

kubectl config set-credentials mmosley  \
        --auth-provider=oidc  \
        --auth-provider-arg=idp-issuer-url=https://oidcidp.tremolo.lan:8443/auth/idp/OidcIdP  \
        --auth-provider-arg=client-id=kubernetes  \
        --auth-provider-arg=client-secret=1db158f6-177d-4d9c-8a8b-d36869918ec5  \
        --auth-provider-arg=refresh-token=q1bKLFOyUiosTfawzA93TzZIDzH2TNa2SMm0zEiPKTUwME6BkEo6Sql5yUWVBSWpKUGphaWpxSVAfekBOZbBhaEW+VlFUeVRGcluyVF5JT4+haZmPsluFoFu5XkpXk5BXqHega4GAXlF+ma+vmYpFcHe5eZR+slBFpZKtQA= \
        --auth-provider-arg=idp-certificate-authority=/root/ca.pem \
        --auth-provider-arg=id-token=eyJraWQiOiJDTj1vaWRjaWRwLnRyZW1vbG8ubGFuLCBPVT1EZW1vLCBPPVRybWVvbG8gU2VjdXJpdHksIEw9QXJsaW5ndG9uLCBTVD1WaXJnaW5pYSwgQz1VUy1DTj1rdWJlLWNhLTEyMDIxNDc5MjEwMzYwNzMyMTUyIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL29pZGNpZHAudHJlbW9sby5sYW46ODQ0My9hdXRoL2lkcC9PaWRjSWRQIiwiYXVkIjoia3ViZXJuZXRlcyIsImV4cCI6MTQ4MzU0OTUxMSwianRpIjoiMm96US15TXdFcHV4WDlHZUhQdy1hZyIsImlhdCI6MTQ4MzU0OTQ1MSwibmJmIjoxNDgzNTQ5MzMxLCJzdWIiOiI0YWViMzdiYS1iNjQ1LTQ4ZmQtYWIzMC0xYTAxZWU0MWUyMTgifQ.w6p4J_6qQ1HzTG9nrEOrubxIMb9K5hzcMPxc9IxPx2K4xO9l-oFiUw93daH3m5pluP6K7eOE6txBuRVfEcpJSwlelsOsW8gb8VJcnzMS9EnZpeA0tW_p-mnkFc3VcfyXuhe5R3G7aa5d8uHv70yJ9Y3-UhjiN9EhpMdfPAoEB9fYKKkJRzF7utTTIPGrSaSU6d2pcpfYKaxIwePzEkT4DfcQthoZdy9ucNvvLoi1DIC-UocFD8HLs8LYKEqSxQvOcvnThbObJ9af71EwmuE21fO5KzMW20KtAeget1gnldOosPtz1G5EwvaQ401-RPQzPGMVBld0_zMCAwZttJ4knw

這將產生以下組態

users:
- name: mmosley
  user:
    auth-provider:
      config:
        client-id: kubernetes
        client-secret: 1db158f6-177d-4d9c-8a8b-d36869918ec5
        id-token: eyJraWQiOiJDTj1vaWRjaWRwLnRyZW1vbG8ubGFuLCBPVT1EZW1vLCBPPVRybWVvbG8gU2VjdXJpdHksIEw9QXJsaW5ndG9uLCBTVD1WaXJnaW5pYSwgQz1VUy1DTj1rdWJlLWNhLTEyMDIxNDc5MjEwMzYwNzMyMTUyIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL29pZGNpZHAudHJlbW9sby5sYW46ODQ0My9hdXRoL2lkcC9PaWRjSWRQIiwiYXVkIjoia3ViZXJuZXRlcyIsImV4cCI6MTQ4MzU0OTUxMSwianRpIjoiMm96US15TXdFcHV4WDlHZUhQdy1hZyIsImlhdCI6MTQ4MzU0OTQ1MSwibmJmIjoxNDgzNTQ5MzMxLCJzdWIiOiI0YWViMzdiYS1iNjQ1LTQ4ZmQtYWIzMC0xYTAxZWU0MWUyMTgifQ.w6p4J_6qQ1HzTG9nrEOrubxIMb9K5hzcMPxc9IxPx2K4xO9l-oFiUw93daH3m5pluP6K7eOE6txBuRVfEcpJSwlelsOsW8gb8VJcnzMS9EnZpeA0tW_p-mnkFc3VcfyXuhe5R3G7aa5d8uHv70yJ9Y3-UhjiN9EhpMdfPAoEB9fYKKkJRzF7utTTIPGrSaSU6d2pcpfYKaxIwePzEkT4DfcQthoZdy9ucNvvLoi1DIC-UocFD8HLs8LYKEqSxQvOcvnThbObJ9af71EwmuE21fO5KzMW20KtAeget1gnldOosPtz1G5EwvaQ401-RPQzPGMVBld0_zMCAwZttJ4knw
        idp-certificate-authority: /root/ca.pem
        idp-issuer-url: https://oidcidp.tremolo.lan:8443/auth/idp/OidcIdP
        refresh-token: q1bKLFOyUiosTfawzA93TzZIDzH2TNa2SMm0zEiPKTUwME6BkEo6Sql5yUWVBSWpKUGphaWpxSVAfekBOZbBhaEW+VlFUeVRGcluyVF5JT4+haZmPsluFoFu5XkpXk5BXq
      name: oidc

一旦您的 id_token 過期,kubectl 將嘗試使用您的 refresh_tokenclient_secret 刷新您的 id_token,並將 refresh_tokenid_token 的新值儲存在您的 .kube/config 中。

選項 2 - 使用 --token 選項

kubectl 命令允許您使用 --token 選項傳入權杖。將 id_token 複製並貼到此選項中

kubectl --token=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL21sYi50cmVtb2xvLmxhbjo4MDQzL2F1dGgvaWRwL29pZGMiLCJhdWQiOiJrdWJlcm5ldGVzIiwiZXhwIjoxNDc0NTk2NjY5LCJqdGkiOiI2RDUzNXoxUEpFNjJOR3QxaWVyYm9RIiwiaWF0IjoxNDc0NTk2MzY5LCJuYmYiOjE0NzQ1OTYyNDksInN1YiI6Im13aW5kdSIsInVzZXJfcm9sZSI6WyJ1c2VycyIsIm5ldy1uYW1lc3BhY2Utdmlld2VyIl0sImVtYWlsIjoibXdpbmR1QG5vbW9yZWplZGkuY29tIn0.f2As579n9VNoaKzoF-dOQGmXkFKf1FMyNV0-va_B63jn-_n9LGSCca_6IVMP8pO-Zb4KvRqGyTP0r3HkHxYy5c81AnIh8ijarruczl-TK_yF5akjSTHFZD-0gRzlevBDiH8Q79NAr-ky0P4iIXS8lY9Vnjch5MF74Zx0c3alKJHJUnnpjIACByfF2SCaYzbWFMUNat-K1PaUk5-ujMBG7yYnr95xD-63n8CO8teGUAAEMx6zRjzfhnhbzX-ajwZLGwGUBT4WqjMs70-6a7_8gZmLZb2az1cZynkFRj2BaCkVT3A2RrjeEwZEtGXlMqKJ1_I2ulrOVsYx01_yD35-rw get nodes

Webhook 權杖身份驗證

Webhook 身份驗證是用於驗證持有者權杖的掛鉤。

  • --authentication-token-webhook-config-file 一個組態檔案,描述如何存取遠端 Webhook 服務。
  • --authentication-token-webhook-cache-ttl 身份驗證決策的快取時間。預設為兩分鐘。
  • --authentication-token-webhook-version 確定是使用 authentication.k8s.io/v1beta1 還是 authentication.k8s.io/v1 TokenReview 物件來從 Webhook 發送/接收資訊。預設為 v1beta1

組態檔案使用 kubeconfig 檔案格式。在檔案中,clusters 指的是遠端服務,而 users 指的是 API 伺服器 Webhook。一個範例是

# Kubernetes API version
apiVersion: v1
# kind of the API object
kind: Config
# clusters refers to the remote service.
clusters:
  - name: name-of-remote-authn-service
    cluster:
      certificate-authority: /path/to/ca.pem         # CA for verifying the remote service.
      server: https://authn.example.com/authenticate # URL of remote service to query. 'https' recommended for production.

# 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-authn-service
    user: name-of-api-server
  name: webhook

當用戶端嘗試使用持有者權杖向 API 伺服器進行身份驗證時,如上方所述,身份驗證 Webhook 將包含權杖的 JSON 序列化 TokenReview 物件 POST 到遠端服務。

請注意,Webhook API 物件與其他 Kubernetes API 物件一樣,受相同的 版本相容性規則約束。實作者應檢查請求的 apiVersion 欄位以確保正確的反序列化,並且必須使用與請求相同版本的 TokenReview 物件回應。

{
  "apiVersion": "authentication.k8s.io/v1",
  "kind": "TokenReview",
  "spec": {
    # Opaque bearer token sent to the API server
    "token": "014fbff9a07c...",

    # Optional list of the audience identifiers for the server the token was presented to.
    # Audience-aware token authenticators (for example, OIDC token authenticators)
    # should verify the token was intended for at least one of the audiences in this list,
    # and return the intersection of this list and the valid audiences for the token in the response status.
    # This ensures the token is valid to authenticate to the server it was presented to.
    # If no audiences are provided, the token should be validated to authenticate to the Kubernetes API server.
    "audiences": ["https://myserver.example.com", "https://myserver.internal.example.com"]
  }
}

{
  "apiVersion": "authentication.k8s.io/v1beta1",
  "kind": "TokenReview",
  "spec": {
    # Opaque bearer token sent to the API server
    "token": "014fbff9a07c...",

    # Optional list of the audience identifiers for the server the token was presented to.
    # Audience-aware token authenticators (for example, OIDC token authenticators)
    # should verify the token was intended for at least one of the audiences in this list,
    # and return the intersection of this list and the valid audiences for the token in the response status.
    # This ensures the token is valid to authenticate to the server it was presented to.
    # If no audiences are provided, the token should be validated to authenticate to the Kubernetes API server.
    "audiences": ["https://myserver.example.com", "https://myserver.internal.example.com"]
  }
}

遠端服務預期會填寫請求的 status 欄位,以指示登入是否成功。回應本文的 spec 欄位將被忽略,並且可以省略。遠端服務必須使用與其接收的 TokenReview API 版本相同的版本傳回回應。成功驗證持有者權杖將傳回

{
  "apiVersion": "authentication.k8s.io/v1",
  "kind": "TokenReview",
  "status": {
    "authenticated": true,
    "user": {
      # Required
      "username": "janedoe@example.com",
      # Optional
      "uid": "42",
      # Optional group memberships
      "groups": ["developers", "qa"],
      # Optional additional information provided by the authenticator.
      # This should not contain confidential data, as it can be recorded in logs
      # or API objects, and is made available to admission webhooks.
      "extra": {
        "extrafield1": [
          "extravalue1",
          "extravalue2"
        ]
      }
    },
    # Optional list audience-aware token authenticators can return,
    # containing the audiences from the `spec.audiences` list for which the provided token was valid.
    # If this is omitted, the token is considered to be valid to authenticate to the Kubernetes API server.
    "audiences": ["https://myserver.example.com"]
  }
}

{
  "apiVersion": "authentication.k8s.io/v1beta1",
  "kind": "TokenReview",
  "status": {
    "authenticated": true,
    "user": {
      # Required
      "username": "janedoe@example.com",
      # Optional
      "uid": "42",
      # Optional group memberships
      "groups": ["developers", "qa"],
      # Optional additional information provided by the authenticator.
      # This should not contain confidential data, as it can be recorded in logs
      # or API objects, and is made available to admission webhooks.
      "extra": {
        "extrafield1": [
          "extravalue1",
          "extravalue2"
        ]
      }
    },
    # Optional list audience-aware token authenticators can return,
    # containing the audiences from the `spec.audiences` list for which the provided token was valid.
    # If this is omitted, the token is considered to be valid to authenticate to the Kubernetes API server.
    "audiences": ["https://myserver.example.com"]
  }
}

不成功的請求將傳回

{
  "apiVersion": "authentication.k8s.io/v1",
  "kind": "TokenReview",
  "status": {
    "authenticated": false,
    # Optionally include details about why authentication failed.
    # If no error is provided, the API will return a generic Unauthorized message.
    # The error field is ignored when authenticated=true.
    "error": "Credentials are expired"
  }
}

{
  "apiVersion": "authentication.k8s.io/v1beta1",
  "kind": "TokenReview",
  "status": {
    "authenticated": false,
    # Optionally include details about why authentication failed.
    # If no error is provided, the API will return a generic Unauthorized message.
    # The error field is ignored when authenticated=true.
    "error": "Credentials are expired"
  }
}

驗證 Proxy

可以組態 API 伺服器以從請求標頭值(例如 X-Remote-User)識別使用者。它旨在與驗證 Proxy 結合使用,驗證 Proxy 會設定請求標頭值。

  • --requestheader-username-headers 必要,不區分大小寫。要檢查的使用者身分標頭名稱,按順序排列。包含值的第一個標頭將用作使用者名稱。
  • --requestheader-group-headers 1.6+。可選,不區分大小寫。「X-Remote-Group」為建議值。要檢查的使用者群組標頭名稱,按順序排列。所有指定標頭中的所有值都將用作群組名稱。
  • --requestheader-extra-headers-prefix 1.6+。可選,不區分大小寫。「X-Remote-Extra-」為建議值。要查找以確定有關使用者的額外資訊的標頭前綴(通常由組態的授權外掛程式使用)。任何以任何指定前綴開頭的標頭都會移除前綴。標頭名稱的其餘部分會轉換為小寫,並進行 百分比解碼,並成為額外金鑰,而標頭值是額外值。

例如,使用此組態

--requestheader-username-headers=X-Remote-User
--requestheader-group-headers=X-Remote-Group
--requestheader-extra-headers-prefix=X-Remote-Extra-

此請求

GET / HTTP/1.1
X-Remote-User: fido
X-Remote-Group: dogs
X-Remote-Group: dachshunds
X-Remote-Extra-Acme.com%2Fproject: some-project
X-Remote-Extra-Scopes: openid
X-Remote-Extra-Scopes: profile

將產生此使用者資訊

name: fido
groups:
- dogs
- dachshunds
extra:
  acme.com/project:
  - some-project
  scopes:
  - openid
  - profile

為了防止標頭詐騙,驗證 Proxy 必須向 API 伺服器提交有效的用戶端憑證,以針對指定的 CA 進行驗證,然後才能檢查請求標頭。警告:不要重複使用在不同環境中使用的 CA,除非您了解風險和保護 CA 使用的機制。

  • --requestheader-client-ca-file 必要。PEM 編碼的憑證套件。在檢查請求標頭以獲取使用者名稱之前,必須提交有效的用戶端憑證,並針對指定檔案中的憑證授權單位進行驗證。
  • --requestheader-allowed-names 可選。通用名稱值 (CN) 列表。如果設定,則在檢查請求標頭以獲取使用者名稱之前,必須提交 CN 在指定列表中的有效用戶端憑證。如果為空,則允許任何 CN。

匿名請求

啟用後,其他組態的身份驗證方法未拒絕的請求將被視為匿名請求,並給定使用者名稱 system:anonymous 和群組 system:unauthenticated

例如,在組態了權杖身份驗證且啟用了匿名存取的伺服器上,提供無效持有者權杖的請求將收到 401 Unauthorized 錯誤。未提供持有者權杖的請求將被視為匿名請求。

在 1.5.1-1.5.x 中,匿名存取預設為停用,可以透過將 --anonymous-auth=true 選項傳遞給 API 伺服器來啟用。

在 1.6+ 中,如果使用 AlwaysAllow 以外的授權模式,則匿名存取預設為啟用,並且可以透過將 --anonymous-auth=false 選項傳遞給 API 伺服器來停用。從 1.6 開始,ABAC 和 RBAC 授權器需要對 system:anonymous 使用者或 system:unauthenticated 群組進行明確授權,因此授予 * 使用者或 * 群組存取權限的舊版策略規則不包含匿名使用者。

匿名驗證器組態

功能狀態: Kubernetes v1.32 [beta] (預設啟用:true)

AuthenticationConfiguration 可用於組態匿名驗證器。如果您在 AuthenticationConfiguration 檔案中設定了 anonymous 欄位,則無法設定 --anonymous-auth 旗標。

使用身份驗證組態檔案組態匿名驗證器的主要優點是,除了啟用和停用匿名身份驗證之外,您還可以組態哪些端點支援匿名身份驗證。

以下是一個範例身份驗證組態檔案

---
#
# CAUTION: this is an example configuration.
#          Do not use this for your own cluster!
#
apiVersion: apiserver.config.k8s.io/v1beta1
kind: AuthenticationConfiguration
anonymous:
  enabled: true
  conditions:
  - path: /livez
  - path: /readyz
  - path: /healthz

在上面的組態中,只有 /livez/readyz/healthz 端點可以透過匿名請求訪問。即使 RBAC 組態允許,任何其他端點也無法訪問。

使用者模擬

使用者可以透過模擬標頭充當另一個使用者。這些標頭允許請求手動覆寫請求驗證為的使用者資訊。例如,管理員可以使用此功能來偵錯授權策略,方法是暫時模擬另一個使用者,並查看請求是否被拒絕。

模擬請求首先以請求使用者的身分進行身份驗證,然後切換到模擬的使用者資訊。

  • 使用者使用其憑證模擬標頭發出 API 呼叫。
  • API 伺服器驗證使用者身分。
  • API 伺服器確保已驗證的使用者具有模擬權限。
  • 請求使用者資訊將被模擬值取代。
  • 評估請求,授權對模擬的使用者資訊起作用。

以下 HTTP 標頭可用於執行模擬請求

  • Impersonate-User:要充當的使用者名稱。
  • Impersonate-Group:要充當的群組名稱。可以多次提供以設定多個群組。可選。需要 "Impersonate-User"。
  • Impersonate-Extra-( extra name ):用於將額外欄位與使用者關聯的動態標頭。可選。需要 "Impersonate-User"。為了保持一致性,( extra name ) 必須為小寫,並且任何不是 在 HTTP 標頭標籤中合法的 字元都必須是 utf8 且 百分比編碼
  • Impersonate-Uid:代表被模擬使用者的唯一識別碼。可選。需要 "Impersonate-User"。Kubernetes 對此字串沒有任何格式要求。

以下是模擬具有群組的使用者時使用的模擬標頭範例

Impersonate-User: jane.doe@example.com
Impersonate-Group: developers
Impersonate-Group: admins

以下是模擬具有 UID 和額外欄位的使用者時使用的模擬標頭範例

Impersonate-User: jane.doe@example.com
Impersonate-Extra-dn: cn=jane,ou=engineers,dc=example,dc=com
Impersonate-Extra-acme.com%2Fproject: some-project
Impersonate-Extra-scopes: view
Impersonate-Extra-scopes: development
Impersonate-Uid: 06f6ce97-e2c5-4ab8-7ba5-7654dd08d52b

當使用 kubectl 時,設定 --as 旗標以組態 Impersonate-User 標頭,設定 --as-group 旗標以組態 Impersonate-Group 標頭。

kubectl drain mynode
Error from server (Forbidden): User "clark" cannot get nodes at the cluster scope. (get nodes mynode)

設定 --as--as-group 旗標

kubectl drain mynode --as=superman --as-group=system:masters
node/mynode cordoned
node/mynode drained

若要模擬使用者、群組、使用者識別碼 (UID) 或額外欄位,模擬使用者必須能夠對正在模擬的屬性種類(「user」、「group」、「uid」等)執行「impersonate」動詞。對於啟用 RBAC 授權外掛程式的叢集,以下 ClusterRole 包含設定使用者和群組模擬標頭所需的規則

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: impersonator
rules:
- apiGroups: [""]
  resources: ["users", "groups", "serviceaccounts"]
  verbs: ["impersonate"]

對於模擬,額外欄位和模擬的 UID 都位於 "authentication.k8s.io" apiGroup 下。額外欄位被評估為資源 "userextras" 的子資源。若要允許使用者對額外欄位 "scopes" 和 UID 使用模擬標頭,應授予使用者以下角色

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: scopes-and-uid-impersonator
rules:
# Can set "Impersonate-Extra-scopes" header and the "Impersonate-Uid" header.
- apiGroups: ["authentication.k8s.io"]
  resources: ["userextras/scopes", "uids"]
  verbs: ["impersonate"]

模擬標頭的值也可以透過限制資源可以採用的 resourceNames 集合來限制。

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: limited-impersonator
rules:
# Can impersonate the user "jane.doe@example.com"
- apiGroups: [""]
  resources: ["users"]
  verbs: ["impersonate"]
  resourceNames: ["jane.doe@example.com"]

# Can impersonate the groups "developers" and "admins"
- apiGroups: [""]
  resources: ["groups"]
  verbs: ["impersonate"]
  resourceNames: ["developers","admins"]

# Can impersonate the extras field "scopes" with the values "view" and "development"
- apiGroups: ["authentication.k8s.io"]
  resources: ["userextras/scopes"]
  verbs: ["impersonate"]
  resourceNames: ["view", "development"]

# Can impersonate the uid "06f6ce97-e2c5-4ab8-7ba5-7654dd08d52b"
- apiGroups: ["authentication.k8s.io"]
  resources: ["uids"]
  verbs: ["impersonate"]
  resourceNames: ["06f6ce97-e2c5-4ab8-7ba5-7654dd08d52b"]

client-go 憑證外掛程式

功能狀態: Kubernetes v1.22 [stable]

k8s.io/client-go 和使用它的工具(例如 kubectlkubelet)能夠執行外部命令以接收使用者憑證。

此功能旨在用於用戶端與 k8s.io/client-go 本身不支援的身份驗證協定(LDAP、Kerberos、OAuth2、SAML 等)整合。此外掛程式實作了協定特定的邏輯,然後傳回要使用的不透明憑證。幾乎所有憑證外掛程式用例都需要伺服器端組件支援 Webhook 權杖驗證器,以解釋用戶端外掛程式產生的憑證格式。

範例用例

在一個假設的用例中,組織將運行一個外部服務,該服務將 LDAP 憑證交換為使用者特定的已簽署權杖。該服務也將能夠回應 Webhook 權杖驗證器 請求以驗證權杖。使用者將需要在其工作站上安裝憑證外掛程式。

若要針對 API 進行身份驗證

  • 使用者發出 kubectl 命令。
  • 憑證外掛程式提示使用者輸入 LDAP 憑證,並與外部服務交換憑證以獲取權杖。
  • 憑證外掛程式將權杖傳回給 client-go,client-go 將其用作針對 API 伺服器的持有者權杖。
  • API 伺服器使用 Webhook 權杖驗證器 向外部服務提交 TokenReview
  • 外部服務驗證權杖上的簽名,並傳回使用者的使用者名稱和群組。

配置

憑證外掛程式透過 kubectl 組態檔案 組態為使用者欄位的一部分。

apiVersion: v1
kind: Config
users:
- name: my-user
  user:
    exec:
      # Command to execute. Required.
      command: "example-client-go-exec-plugin"

      # API version to use when decoding the ExecCredentials resource. Required.
      #
      # The API version returned by the plugin MUST match the version listed here.
      #
      # To integrate with tools that support multiple versions (such as client.authentication.k8s.io/v1beta1),
      # set an environment variable, pass an argument to the tool that indicates which version the exec plugin expects,
      # or read the version from the ExecCredential object in the KUBERNETES_EXEC_INFO environment variable.
      apiVersion: "client.authentication.k8s.io/v1"

      # Environment variables to set when executing the plugin. Optional.
      env:
      - name: "FOO"
        value: "bar"

      # Arguments to pass when executing the plugin. Optional.
      args:
      - "arg1"
      - "arg2"

      # Text shown to the user when the executable doesn't seem to be present. Optional.
      installHint: |
        example-client-go-exec-plugin is required to authenticate
        to the current cluster.  It can be installed:

        On macOS: brew install example-client-go-exec-plugin

        On Ubuntu: apt-get install example-client-go-exec-plugin

        On Fedora: dnf install example-client-go-exec-plugin

        ...        

      # Whether or not to provide cluster information, which could potentially contain
      # very large CA data, to this exec plugin as a part of the KUBERNETES_EXEC_INFO
      # environment variable.
      provideClusterInfo: true

      # The contract between the exec plugin and the standard input I/O stream. If the
      # contract cannot be satisfied, this plugin will not be run and an error will be
      # returned. Valid values are "Never" (this exec plugin never uses standard input),
      # "IfAvailable" (this exec plugin wants to use standard input if it is available),
      # or "Always" (this exec plugin requires standard input to function). Required.
      interactiveMode: Never
clusters:
- name: my-cluster
  cluster:
    server: "https://172.17.4.100:6443"
    certificate-authority: "/etc/kubernetes/ca.pem"
    extensions:
    - name: client.authentication.k8s.io/exec # reserved extension name for per cluster exec config
      extension:
        arbitrary: config
        this: can be provided via the KUBERNETES_EXEC_INFO environment variable upon setting provideClusterInfo
        you: ["can", "put", "anything", "here"]
contexts:
- name: my-cluster
  context:
    cluster: my-cluster
    user: my-user
current-context: my-cluster

apiVersion: v1
kind: Config
users:
- name: my-user
  user:
    exec:
      # Command to execute. Required.
      command: "example-client-go-exec-plugin"

      # API version to use when decoding the ExecCredentials resource. Required.
      #
      # The API version returned by the plugin MUST match the version listed here.
      #
      # To integrate with tools that support multiple versions (such as client.authentication.k8s.io/v1),
      # set an environment variable, pass an argument to the tool that indicates which version the exec plugin expects,
      # or read the version from the ExecCredential object in the KUBERNETES_EXEC_INFO environment variable.
      apiVersion: "client.authentication.k8s.io/v1beta1"

      # Environment variables to set when executing the plugin. Optional.
      env:
      - name: "FOO"
        value: "bar"

      # Arguments to pass when executing the plugin. Optional.
      args:
      - "arg1"
      - "arg2"

      # Text shown to the user when the executable doesn't seem to be present. Optional.
      installHint: |
        example-client-go-exec-plugin is required to authenticate
        to the current cluster.  It can be installed:

        On macOS: brew install example-client-go-exec-plugin

        On Ubuntu: apt-get install example-client-go-exec-plugin

        On Fedora: dnf install example-client-go-exec-plugin

        ...        

      # Whether or not to provide cluster information, which could potentially contain
      # very large CA data, to this exec plugin as a part of the KUBERNETES_EXEC_INFO
      # environment variable.
      provideClusterInfo: true

      # The contract between the exec plugin and the standard input I/O stream. If the
      # contract cannot be satisfied, this plugin will not be run and an error will be
      # returned. Valid values are "Never" (this exec plugin never uses standard input),
      # "IfAvailable" (this exec plugin wants to use standard input if it is available),
      # or "Always" (this exec plugin requires standard input to function). Optional.
      # Defaults to "IfAvailable".
      interactiveMode: Never
clusters:
- name: my-cluster
  cluster:
    server: "https://172.17.4.100:6443"
    certificate-authority: "/etc/kubernetes/ca.pem"
    extensions:
    - name: client.authentication.k8s.io/exec # reserved extension name for per cluster exec config
      extension:
        arbitrary: config
        this: can be provided via the KUBERNETES_EXEC_INFO environment variable upon setting provideClusterInfo
        you: ["can", "put", "anything", "here"]
contexts:
- name: my-cluster
  context:
    cluster: my-cluster
    user: my-user
current-context: my-cluster

相對命令路徑被解釋為相對於組態檔案的目錄。如果 KUBECONFIG 設定為 /home/jane/kubeconfig 並且 exec 命令為 ./bin/example-client-go-exec-plugin,則執行二進位檔 /home/jane/bin/example-client-go-exec-plugin

- name: my-user
  user:
    exec:
      # Path relative to the directory of the kubeconfig
      command: "./bin/example-client-go-exec-plugin"
      apiVersion: "client.authentication.k8s.io/v1"
      interactiveMode: Never

輸入和輸出格式

執行的指令會將 ExecCredential 物件列印到 stdoutk8s.io/client-go 會使用 status 中傳回的憑證向 Kubernetes API 進行身份驗證。執行的指令會透過 KUBERNETES_EXEC_INFO 環境變數傳遞 ExecCredential 物件作為輸入。此輸入包含實用資訊,例如傳回的 ExecCredential 物件的預期 API 版本,以及外掛程式是否可以使用 stdin 與使用者互動。

從互動式工作階段(即終端機)執行時,stdin 可以直接暴露給外掛程式。外掛程式應使用來自 KUBERNETES_EXEC_INFO 環境變數的輸入 ExecCredential 物件的 spec.interactive 欄位,以判斷是否已提供 stdin。外掛程式的 stdin 需求(即,為了讓外掛程式成功執行,stdin 是選用的、嚴格要求的,還是從未使用)是透過 kubeconfig 中的 user.exec.interactiveMode 欄位宣告的(有效值請參閱下表)。user.exec.interactiveMode 欄位在 client.authentication.k8s.io/v1beta1 中是選用的,在 client.authentication.k8s.io/v1 中是必要的。

interactiveMode 值
interactiveMode含義
Never此 exec 外掛程式永遠不需要使用標準輸入,因此無論標準輸入是否可用於使用者輸入,都將執行 exec 外掛程式。
IfAvailable如果標準輸入可用,此 exec 外掛程式希望使用標準輸入,但即使標準輸入不可用,仍然可以運作。因此,無論 stdin 是否可用於使用者輸入,都將執行 exec 外掛程式。如果標準輸入可用於使用者輸入,則將提供給此 exec 外掛程式。
Always此 exec 外掛程式需要標準輸入才能執行,因此只有在標準輸入可用於使用者輸入時,才會執行 exec 外掛程式。如果標準輸入不可用於使用者輸入,則不會執行 exec 外掛程式,並且 exec 外掛程式執行器將傳回錯誤。

若要使用持有者權杖憑證,外掛程式會在 ExecCredential 的狀態中傳回權杖

{
  "apiVersion": "client.authentication.k8s.io/v1",
  "kind": "ExecCredential",
  "status": {
    "token": "my-bearer-token"
  }
}

{
  "apiVersion": "client.authentication.k8s.io/v1beta1",
  "kind": "ExecCredential",
  "status": {
    "token": "my-bearer-token"
  }
}

或者,可以傳回 PEM 編碼的用戶端憑證和金鑰以使用 TLS 用戶端身份驗證。如果外掛程式在後續呼叫中傳回不同的憑證和金鑰,k8s.io/client-go 將關閉與伺服器的現有連線,以強制執行新的 TLS 交握。

如果指定了 clientKeyDataclientCertificateData,則兩者都必須存在。

clientCertificateData 可能包含要傳送給伺服器的其他中繼憑證。

{
  "apiVersion": "client.authentication.k8s.io/v1",
  "kind": "ExecCredential",
  "status": {
    "clientCertificateData": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----",
    "clientKeyData": "-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----"
  }
}

{
  "apiVersion": "client.authentication.k8s.io/v1beta1",
  "kind": "ExecCredential",
  "status": {
    "clientCertificateData": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----",
    "clientKeyData": "-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----"
  }
}

選擇性地,回應可以包含憑證的到期時間,格式為 RFC 3339 時間戳記。

到期時間的存在與否有以下影響

  • 如果包含到期時間,則持有者權杖和 TLS 憑證將被快取,直到達到到期時間,或者伺服器回應 401 HTTP 狀態碼,或者程序結束。
  • 如果省略到期時間,則持有者權杖和 TLS 憑證將被快取,直到伺服器回應 401 HTTP 狀態碼或直到程序結束。

{
  "apiVersion": "client.authentication.k8s.io/v1",
  "kind": "ExecCredential",
  "status": {
    "token": "my-bearer-token",
    "expirationTimestamp": "2018-03-05T17:30:20-08:00"
  }
}

{
  "apiVersion": "client.authentication.k8s.io/v1beta1",
  "kind": "ExecCredential",
  "status": {
    "token": "my-bearer-token",
    "expirationTimestamp": "2018-03-05T17:30:20-08:00"
  }
}

若要讓 exec 外掛程式能夠取得叢集特定的資訊,請在 kubeconfig 中的 user.exec 欄位上設定 provideClusterInfo。然後,外掛程式將在 KUBERNETES_EXEC_INFO 環境變數中獲得此叢集特定的資訊。來自此環境變數的資訊可用於執行叢集特定的憑證取得邏輯。以下 ExecCredential 資訊清單描述了叢集資訊範例。

{
  "apiVersion": "client.authentication.k8s.io/v1",
  "kind": "ExecCredential",
  "spec": {
    "cluster": {
      "server": "https://172.17.4.100:6443",
      "certificate-authority-data": "LS0t...",
      "config": {
        "arbitrary": "config",
        "this": "can be provided via the KUBERNETES_EXEC_INFO environment variable upon setting provideClusterInfo",
        "you": ["can", "put", "anything", "here"]
      }
    },
    "interactive": true
  }
}

{
  "apiVersion": "client.authentication.k8s.io/v1beta1",
  "kind": "ExecCredential",
  "spec": {
    "cluster": {
      "server": "https://172.17.4.100:6443",
      "certificate-authority-data": "LS0t...",
      "config": {
        "arbitrary": "config",
        "this": "can be provided via the KUBERNETES_EXEC_INFO environment variable upon setting provideClusterInfo",
        "you": ["can", "put", "anything", "here"]
      }
    },
    "interactive": true
  }
}

用戶端身份驗證資訊的 API 存取

功能狀態: Kubernetes v1.28 [stable]

如果您的叢集已啟用 API,您可以使用 SelfSubjectReview API 來找出您的 Kubernetes 叢集如何將您的身份驗證資訊對應到將您識別為用戶端。無論您是以使用者(通常代表真人)或 ServiceAccount 的身分進行身份驗證,這都適用。

SelfSubjectReview 物件沒有任何可配置的欄位。在收到請求時,Kubernetes API 伺服器會用使用者屬性填寫狀態,並將其傳回給使用者。

請求範例(主體將是 SelfSubjectReview

POST /apis/authentication.k8s.io/v1/selfsubjectreviews
{
  "apiVersion": "authentication.k8s.io/v1",
  "kind": "SelfSubjectReview"
}

回應範例

{
  "apiVersion": "authentication.k8s.io/v1",
  "kind": "SelfSubjectReview",
  "status": {
    "userInfo": {
      "name": "jane.doe",
      "uid": "b6c7cfd4-f166-11ec-8ea0-0242ac120002",
      "groups": [
        "viewers",
        "editors",
        "system:authenticated"
      ],
      "extra": {
        "provider_id": ["token.company.example"]
      }
    }
  }
}

為了方便起見,提供了 kubectl auth whoami 指令。執行此指令將產生以下輸出(但會顯示不同的使用者屬性)

  • 簡單輸出範例

    ATTRIBUTE         VALUE
    Username          jane.doe
    Groups            [system:authenticated]
    
  • 包含額外屬性的複雜範例

    ATTRIBUTE         VALUE
    Username          jane.doe
    UID               b79dbf30-0c6a-11ed-861d-0242ac120002
    Groups            [students teachers system:authenticated]
    Extra: skills     [reading learning]
    Extra: subjects   [math sports]
    

透過提供輸出旗標,也可以列印結果的 JSON 或 YAML 表示法

{
  "apiVersion": "authentication.k8s.io/v1",
  "kind": "SelfSubjectReview",
  "status": {
    "userInfo": {
      "username": "jane.doe",
      "uid": "b79dbf30-0c6a-11ed-861d-0242ac120002",
      "groups": [
        "students",
        "teachers",
        "system:authenticated"
      ],
      "extra": {
        "skills": [
          "reading",
          "learning"
        ],
        "subjects": [
          "math",
          "sports"
        ]
      }
    }
  }
}

apiVersion: authentication.k8s.io/v1
kind: SelfSubjectReview
status:
  userInfo:
    username: jane.doe
    uid: b79dbf30-0c6a-11ed-861d-0242ac120002
    groups:
    - students
    - teachers
    - system:authenticated
    extra:
      skills:
      - reading
      - learning
      subjects:
      - math
      - sports

當 Kubernetes 叢集中使用複雜的身份驗證流程時,此功能非常有用,例如,如果您使用 webhook 權杖身份驗證身份驗證 Proxy

預設情況下,當啟用 APISelfSubjectReview 功能時,所有已驗證的使用者都可以建立 SelfSubjectReview 物件。這是由 system:basic-user 叢集角色允許的。

下一步

上次修改時間:2024 年 9 月 4 日下午 2:48 PST:KEP-4633:升級至 BETA。(7a1cdab150)