身份验证

此页面提供了身份验证的概述。

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 使用客户端证书、承载令牌或身份验证代理来通过身份验证插件对 API 请求进行身份验证。当向 API 服务器发出 HTTP 请求时,插件会尝试将以下属性与请求关联起来

  • 用户名:用于标识最终用户的字符串。常见值可能是 kube-admin[email protected]
  • UID:用于标识最终用户的字符串,它比用户名更一致和唯一。
  • 组:一组字符串,每个字符串都表示用户在命名逻辑用户集合中的成员身份。常见值可能是 system:mastersdevops-team
  • 额外字段:一个字符串到字符串列表的映射,它包含授权者可能觉得有用的其他信息。

所有值对身份验证系统都是不透明的,只有在由 授权者 解释时才具有意义。

您可以同时启用多种身份验证方法。您通常应该至少使用两种方法

  • 服务帐户令牌用于服务帐户
  • 至少使用另一种方法进行用户身份验证。

当启用多个身份验证器模块时,第一个成功对请求进行身份验证的模块会短路评估。API 服务器不保证身份验证器运行的顺序。

system:authenticated 组包含在所有经过身份验证用户的组列表中。

可以使用 身份验证代理身份验证 Webhook 与其他身份验证协议(LDAP、SAML、Kerberos、备用 x509 方案等)集成。

X509 客户端证书

通过向 API 服务器传递 --client-ca-file=SOMEFILE 选项来启用客户端证书身份验证。引用的文件必须包含一个或多个证书颁发机构,用于验证向 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”。

请参阅 管理证书,了解如何生成客户端证书。

静态令牌文件

API 服务器在命令行上给出 --token-auth-file=SOMEFILE 选项时,会从文件中读取承载令牌。目前,令牌无限期有效,并且令牌列表无法在不重启 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 [稳定]

为了允许为新集群提供简化的引导,Kubernetes 包含一种动态管理的承载令牌类型,称为引导令牌。这些令牌存储为 kube-system 命名空间中的 Secrets,可以在其中动态管理和创建。Controller Manager 包含一个 TokenCleaner 控制器,该控制器在引导令牌过期时将其删除。

令牌的格式为 [a-z0-9]{6}.[a-z0-9]{16}。第一个组件是令牌 ID,第二个组件是令牌密钥。您可以在 HTTP 标头中指定令牌,如下所示

Authorization: Bearer 781292.db7bc3a58fc5f07e

您必须使用 API 服务器上的 --enable-bootstrap-token-auth 标志启用引导令牌身份验证器。您必须通过 Controller Manager 上的 --controllers 标志启用 TokenCleaner 控制器。这可以使用类似 --controllers=*,tokencleaner 的内容来完成。如果您使用 kubeadm 来引导集群,它将为您执行此操作。

身份验证器以 system:bootstrap:<Token ID> 的身份进行身份验证。它包含在 system:bootstrappers 组中。命名和组故意被限制,以阻止用户在引导后使用这些令牌。用户名和组可用于(并被 kubeadm 使用)来创建适当的授权策略,以支持引导集群。

请参阅 引导令牌,以深入了解引导令牌身份验证器和控制器,以及如何使用 kubeadm 管理这些令牌。

服务帐户令牌

服务帐户是一个自动启用的身份验证器,它使用签名承载令牌来验证请求。插件采用两个可选标志

  • --service-account-key-file 包含 PEM 编码的 x509 RSA 或 ECDSA 私钥或公钥的文件,用于验证服务帐户令牌。指定的文件可以包含多个密钥,并且可以对不同的文件多次指定该标志。如果未指定,则使用 --tls-private-key-file。
  • --service-account-lookup 如果启用,则从 API 中删除的令牌将被吊销。

服务帐户通常由 API 服务器自动创建,并通过 ServiceAccount 准入控制器 与集群中运行的 Pod 关联。承载令牌会挂载到 Pod 中的众所周知的 location,并允许集群内进程与 API 服务器交谈。可以使用 PodSpecserviceAccountName 字段将帐户显式地与 Pod 关联。

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 可以用作承载令牌,以给定服务帐户的身份进行身份验证。有关如何在请求中包含令牌的信息,请参阅 上面。通常,这些令牌会被挂载到 Pod 中,用于集群内访问 API 服务器,但也可以从集群外使用。

服务帐户使用用户名 system:serviceaccount:(NAMESPACE):(SERVICEACCOUNT) 进行身份验证,并被分配到组 system:serviceaccountssystem:serviceaccounts:(NAMESPACE) 中。

OpenID Connect 令牌

OpenID Connect 是某些 OAuth2 提供商(尤其是 Microsoft Entra ID、Salesforce 和 Google)支持的 OAuth2 的一种风格。该协议对 OAuth2 的主要扩展是使用访问令牌返回的额外字段,称为 ID 令牌。此令牌是一个 JSON Web 令牌 (JWT),包含众所周知的字段(如用户的电子邮件),由服务器签名。

为了识别用户,身份验证器使用 OAuth2 令牌响应 中的 id_token(而不是 access_token)作为承载令牌。有关如何在请求中包含令牌的信息,请参阅 上面

sequenceDiagram participant user as 用户 participant idp as 身份提供者 participant kube as kubectl participant api as API 服务器 用户 ->> idp: 1. 登录到 IdP activate idp idp -->> 用户: 2. 提供 access_token,
id_token 和 refresh_token deactivate idp activate 用户 用户 ->> kube: 3. 调用 kubectl
其中 --token 是 id_token
或将令牌添加到 .kube/config deactivate 用户 activate kube kube ->> api: 4. 授权:承载... 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 用户: 9. 返回结果 deactivate kube
  1. 登录到您的身份提供者

  2. 您的身份提供者将为您提供 access_tokenid_tokenrefresh_token

  3. 使用 kubectl 时,请使用 --token 标志使用您的 id_token,或将其直接添加到您的 kubeconfig

  4. kubectl 会在名为 Authorization 的标头中将您的 id_token 发送到 API 服务器

  5. API 服务器将确保 JWT 签名有效

  6. 检查以确保 id_token 未过期

    如果使用 AuthenticationConfiguration 配置了 CEL 表达式,则执行声明和/或用户验证。

  7. 确保用户已授权

  8. 一旦授权,API 服务器将返回响应给kubectl

  9. kubectl 向用户提供反馈

由于验证用户身份所需的所有数据都在id_token中,Kubernetes 不需要“呼叫”身份提供者。在每个请求都是无状态的模型中,这为身份验证提供了一种非常可扩展的解决方案。它确实提供了一些挑战

  1. Kubernetes 没有“web 界面”来触发身份验证过程。没有浏览器或界面来收集凭据,这就是为什么你需要先向你的身份提供者进行身份验证。
  2. id_token 无法撤销,它就像一个证书,所以它应该很短命(只有几分钟),所以每隔几分钟就不得不获取一个新的令牌会很烦人。
  3. 要向 Kubernetes 仪表板进行身份验证,必须使用kubectl proxy 命令或一个将id_token 注入的反向代理。

配置 API 服务器

使用标志

要启用插件,请在 API 服务器上配置以下标志

参数描述示例必需
--oidc-issuer-url允许 API 服务器发现公共签名密钥的提供者的 URL。只接受使用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-claim 的值为email 以外的值,则前缀默认为( Issuer URL )#,其中( Issuer URL )--oidc-issuer-url 的值。值- 可用于禁用所有前缀。oidc
--oidc-groups-claim用作用户组的 JWT 声明。如果声明存在,则它必须是字符串数组。groups
--oidc-groups-prefix添加到组声明之前的前缀,以防止与现有名称(例如system: 组)发生冲突。例如,值oidc: 将创建组名,例如oidc:engineeringoidc:infraoidc
--oidc-required-claim描述 ID 令牌中所需声明的键值对。如果设置,则声明将被验证为存在于 ID 令牌中,并且具有匹配的值。重复此标志以指定多个声明。claim=value
--oidc-ca-file用于签署身份提供者的 web 证书的 CA 证书的路径。默认为主机根 CA。/etc/kubernetes/ssl/kc-ca.pem
--oidc-signing-algs接受的签名算法。默认值为 "RS256"。RS512
来自文件的身份验证配置
功能状态: Kubernetes v1.30 [beta]

JWT Authenticator 是一种身份验证器,用于使用符合 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.31,结构化身份验证配置文件格式为 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: '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 连接发现

    用于验证签名的公钥是使用 OIDC 发现从发行者的公共端点发现的。如果你使用的是身份验证配置文件,则身份提供者不需要公开暴露发现端点。你可以将发现端点托管在与发行者不同的位置(例如集群中的本地位置),并在配置文件中指定issuer.discoveryURL

  2. 在 TLS 中运行,并使用非过时的密码

  3. 拥有 CA 签名的证书(即使 CA 不是商业 CA 或自签名的)

关于上面第 3 条要求的说明,要求 CA 签名的证书。如果你部署了自己的身份提供者(而不是 Google 或 Microsoft 等云提供者中的一个),则必须让你的身份提供者的 web 服务器证书由带有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": "[email protected]",
      # 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": "[email protected]",
      # 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"
  }
}

身份验证代理

可以将 API 服务器配置为从请求头值(例如X-Remote-User)中识别用户。它旨在与身份验证代理结合使用,身份验证代理设置请求头值。

  • --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

为了防止标头欺骗,身份验证代理需要向 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 中,匿名访问默认情况下处于禁用状态,可以通过向 API 服务器传递--anonymous-auth=true选项来启用。

在 1.6+ 中,如果使用除AlwaysAllow以外的授权模式,则匿名访问默认情况下处于启用状态,可以通过向 API 服务器传递--anonymous-auth=false选项来禁用。从 1.6 开始,ABAC 和 RBAC 授权器需要对system:anonymous用户或system:unauthenticated组进行显式授权,因此将访问权限授予*用户或*组的旧策略规则不包括匿名用户。

匿名身份验证器配置

特性状态: Kubernetes v1.31 [alpha]

AuthenticationConfiguration可用于配置匿名身份验证器。要通过配置文件启用配置匿名身份验证,您需要启用AnonymousAuthConfigurableEndpoints功能门。启用此功能门后,您无法设置--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: [email protected]
Impersonate-Group: developers
Impersonate-Group: admins

模拟具有 UID 和额外字段的用户时使用的模拟头文件的示例

Impersonate-User: [email protected]
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) 或额外字段,模拟用户必须能够对被模拟的属性类型(“用户”、“组”、“uid”等)执行“模拟”动词。对于启用 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 "[email protected]"
- apiGroups: [""]
  resources: ["users"]
  verbs: ["impersonate"]
  resourceNames: ["[email protected]"]

# 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 [稳定]

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 插件希望在标准输入可用时使用它,但即使标准输入不可用,它也能正常工作。因此,无论标准输入是否可用,都会运行该 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 [稳定]

如果您的集群启用了 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 令牌身份验证身份验证代理.

默认情况下,所有经过身份验证的用户都可以在启用 APISelfSubjectReview 功能时创建 SelfSubjectReview 对象。它由 system:basic-user 集群角色允许。

下一步

最后修改时间:2024 年 6 月 26 日下午 9:32 PST:KEP-4633:为匿名身份验证添加可配置终结点的文档。(4b2940eb26)