认证
本页面概述了 Kubernetes 中的身份验证,重点是针对 Kubernetes API 的身份验证。
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 请求。当 HTTP 请求发送到 API 服务器时,插件会尝试将以下属性与请求关联起来
- 用户名:标识最终用户的字符串。常见值可能是
kube-admin
或jane@example.com
。 - UID:标识最终用户的字符串,并尝试比用户名更具一致性和唯一性。
- 组:一组字符串,每个字符串表示用户所属的命名逻辑用户集合。常见值可能是
system:masters
或devops-team
。 - 额外字段:一个从字符串到字符串列表的映射,其中包含授权器可能认为有用的附加信息。
所有值对身份验证系统都是不透明的,只有在 授权器 解释时才具有意义。
您可以同时启用多种身份验证方法。通常应至少使用两种方法
- 用于服务账号的服务账号令牌
- 至少一种其他用户身份验证方法。
当启用多个身份验证模块时,第一个成功验证请求的模块会短路评估。API 服务器不保证身份验证器运行的顺序。
system:authenticated
组包含在所有已验证用户的组列表中。
与其他身份验证协议(LDAP、SAML、Kerberos、备用 x509 方案等)的集成可以通过使用 身份验证代理 或 身份验证 Webhook 来实现。
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”两个组。
有关如何生成客户端证书,请参阅 管理证书。
静态令牌文件
当命令行上给出 --token-auth-file=SOMEFILE
选项时,API 服务器从文件中读取不记名令牌。目前,令牌无限期有效,并且在不重新启动 API 服务器的情况下无法更改令牌列表。
令牌文件是一个 CSV 文件,至少包含 3 列:令牌、用户名、用户 UID,后跟可选的组名。
注意
如果您的组不止一个,则该列必须用双引号引起来,例如
token,user,uid,"group1,group2,group3"
在请求中放置不记名令牌
当从 HTTP 客户端使用不记名令牌身份验证时,API 服务器期望一个 Authorization
标头,其值为 Bearer
。不记名令牌必须是一个字符序列,可以使用 HTTP 的编码和引用工具放入 HTTP 标头值中,且不超过这些工具的能力。例如:如果不记名令牌是 31ada4fd-adec-460c-809a-9e56ceb75269
,则它将显示在 HTTP 标头中,如下所示。
Authorization: Bearer 31ada4fd-adec-460c-809a-9e56ceb75269
引导令牌
Kubernetes v1.18 [stable]
为了简化新集群的引导,Kubernetes 包含一种动态管理的不记名令牌类型,称为 引导令牌。这些令牌作为 Secret 存储在 kube-system
命名空间中,可以在其中动态管理和创建。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:
身份进行身份验证。它包含在 system:bootstrappers
组中。名称和组有意限制,以阻止用户在引导后使用这些令牌。用户名和组可用于(并由 kubeadm
使用)制定适当的授权策略以支持集群引导。
有关引导令牌身份验证器和控制器的深入文档以及如何使用 kubeadm
管理这些令牌,请参阅 引导令牌。
服务账号令牌
服务账号是一个自动启用的身份验证器,它使用签名的不记名令牌来验证请求。该插件接受两个可选标志
--service-account-key-file
包含 PEM 编码的 x509 RSA 或 ECDSA 私钥或公钥的文件,用于验证 ServiceAccount 令牌。指定的文件可以包含多个密钥,并且该标志可以指定多次,使用不同的文件。如果未指定,则使用 --tls-private-key-file。--service-account-lookup
如果启用,从 API 删除的令牌将被撤销。
服务账号通常由 API 服务器自动创建,并通过 ServiceAccount
准入控制器 与集群中运行的 Pod 相关联。不记名令牌安装在 Pod 的已知位置,并允许集群内进程与 API 服务器通信。可以使用 PodSpec
的 serviceAccountName
字段将账号显式与 Pod 关联。
注意
通常会省略serviceAccountName
,因为这是自动完成的。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:serviceaccounts
和 system:serviceaccounts:(NAMESPACE)
。
警告
由于服务账号令牌也可以存储在 Secret API 对象中,任何对 Secret 具有写入访问权限的用户都可以请求令牌,任何对这些 Secret 具有读取访问权限的用户都可以以服务账号的身份进行身份验证。在授予服务账号权限以及 Secret 的读取或写入能力时要谨慎。OpenID Connect 令牌
OpenID Connect 是一种 OAuth2 风格,受某些 OAuth2 提供商(尤其是 Microsoft Entra ID、Salesforce 和 Google)支持。该协议对 OAuth2 的主要扩展是随访问令牌返回的一个附加字段,称为 ID 令牌。此令牌是一个 JSON Web 令牌 (JWT),包含众所周知的字段,例如用户电子邮件,并由服务器签名。
为了识别用户,身份验证器使用 OAuth2 令牌响应 中的 id_token
(而不是 access_token
)作为不记名令牌。有关令牌如何包含在请求中,请参阅 上文。
id_token, and refresh_token deactivate idp activate user user ->> kube: 3. Call kubectl
with --token being the id_token
OR add tokens to .kube/config deactivate user activate kube kube ->> api: 4. Authorization: Bearer... deactivate kube activate api api ->> api: 5. Is JWT signature valid? api ->> api: 6. Has the JWT expired? (iat+exp) api ->> api: 7. User authorized? api -->> kube: 8. Authorized: Perform
action and return result deactivate api activate kube kube --x user: 9. Return result deactivate kube
登录到您的身份提供商
您的身份提供商将为您提供
access_token
、id_token
和refresh_token
使用
kubectl
时,将您的id_token
与--token
标志一起使用,或将其直接添加到您的kubeconfig
kubectl
将您的id_token
通过名为 Authorization 的标头发送到 API 服务器API 服务器将确保 JWT 签名有效
检查以确保
id_token
未过期如果使用
AuthenticationConfiguration
配置了 CEL 表达式,则执行声明和/或用户验证。确保用户已获得授权
授权后,API 服务器将响应返回给
kubectl
kubectl
向用户提供反馈
由于验证您身份所需的所有数据都在 id_token
中,Kubernetes 不需要“打电话回家”给身份提供商。在每个请求都是无状态的模型中,这为身份验证提供了一个高度可扩展的解决方案。它确实带来了一些挑战
- Kubernetes 没有“Web 界面”来触发身份验证过程。没有浏览器或界面来收集凭据,这就是您需要首先向身份提供商进行身份验证的原因。
id_token
无法撤销,它就像证书一样,因此它应该是短期的(只有几分钟),因此每隔几分钟就必须获取一个新令牌可能会非常烦人。- 要向 Kubernetes 控制面板进行身份验证,您必须使用
kubectl proxy
命令或注入id_token
的反向代理。
配置 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 ,它应该是最终用户的唯一标识符。管理员可以选择其他声明,例如 email 或 name ,具体取决于其提供商。但是,除了 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:engineering 和 oidc:infra 的组名。 | oidc | 否 |
--oidc-required-claim | 描述 ID 令牌中所需声明的键=值对。如果设置,将验证 ID 令牌中是否存在具有匹配值的声明。重复此标志以指定多个声明。 | claim=value | 否 |
--oidc-ca-file | 您的身份提供商 Web 证书签名 CA 的证书路径。默认为主机的根 CA。 | /etc/kubernetes/ssl/kc-ca.pem | 否 |
--oidc-signing-algs | 接受的签名算法。默认为 RS256。允许的值为:RS256、RS384、RS512、ES256、ES384、ES512、PS256、PS384、PS512。这些值由 RFC 7518 https://tools.ietf.org/html/rfc7518#section-3.1 定义。 | RS512 | 否 |
通过文件进行身份验证配置
Kubernetes v1.34 [稳定]
(默认启用: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.url
和 issuer.discoveryURL
。配置文件甚至允许您指定 CEL 表达式以将声明映射到用户属性,并验证声明和用户信息。当配置文件修改时,API 服务器还会自动重新加载身份验证器。您可以使用 apiserver_authentication_config_controller_automatic_reload_last_timestamp_seconds
指标来监视 API 服务器上次重新加载配置的时间。
您必须使用 API 服务器上的 --authentication-config
标志指定身份验证配置文件的路径。如果您想使用命令行标志而不是配置文件,这些标志将继续按原样工作。要访问新功能,例如配置多个身份验证器,为颁发者设置多个受众,请切换到使用配置文件。
对于 Kubernetes v1.34,结构化身份验证配置文件格式为 Beta 级别,使用该配置的机制也为 Beta 级别。如果您没有特意为您的集群禁用 StructuredAuthenticationConfiguration
功能门,您可以通过为 kube-apiserver 指定 --authentication-config
命令行参数来启用结构化身份验证。下面显示了结构化身份验证配置文件的示例。
注意
如果您同时指定--authentication-config
和任何 --oidc-*
命令行参数,则这是配置错误。在这种情况下,API 服务器会报告错误,然后立即退出。如果要切换到使用结构化身份验证配置,则必须删除 --oidc-*
命令行参数,并改用配置文件。Kubernetes v1.34 [beta]
(默认启用:true)JWT 颁发者配置中的 egressSelectorType 字段允许您指定应使用哪个出口选择器来发送与颁发者相关的所有流量(发现、JWKS、分布式声明等)。此功能需要启用 StructuredAuthenticationConfigurationEgressSelector
功能门。
---
#
# CAUTION: this is an example configuration.
# Do not use this for your own cluster!
#
apiVersion: apiserver.config.k8s.io/v1
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
# egressSelectorType is an indicator of which egress selection should be used for sending all traffic related
# to this issuer (discovery, JWKS, distributed claims, etc). If unspecified, no custom dialer is used.
# When specified, the valid choices are "controlplane" and "cluster". These correspond to the associated
# values in the --egress-selector-config-file.
# - controlplane: for traffic intended to go to the control plane.
# - cluster: for traffic intended to go to the system being managed by Kubernetes.
egressSelectorType: <egress-selector-type>
# 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) == true'.
# By explicitly comparing the value to true, we let type-checking see the result will be a boolean, and
# to make sure a non-boolean email_verified claim will be caught at runtime.
# 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.expression
、jwt.claimMappings.groups.expression
、jwt.claimMappings.uid.expression
jwt.claimMappings.extra[i].valueExpression
表示将由 CEL 评估的表达式。CEL 表达式可以访问令牌负载的内容,这些内容组织到claims
CEL 变量中。claims
是一个从声明名称(字符串)到声明值(任何类型)的映射。要了解更多信息,请参阅 CEL 文档
以下是使用不同令牌负载的
AuthenticationConfiguration
示例。apiVersion: apiserver.config.k8s.io/v1 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/v1 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/v1 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
错误。
限制
- 分布式声明不通过 CEL 表达式工作。
Kubernetes 不提供 OpenID Connect 身份提供商。您可以使用现有的公共 OpenID Connect 身份提供商或运行您自己的支持 OpenID Connect 协议的身份提供商。
为了使身份提供商与 Kubernetes 协同工作,它必须
用于验证签名的公钥通过 OIDC 发现从颁发者的公共端点中发现。如果您使用身份验证配置文件,身份提供商不需要公开暴露发现端点。您可以在与颁发者不同的位置(例如集群本地)托管发现端点,并在配置文件中指定
issuer.discoveryURL
。在 TLS 中运行,使用非过时密码
拥有 CA 签名的证书(即使 CA 不是商业 CA 或自签名)
关于上述要求 #3,即需要 CA 签名证书的说明。如果您部署自己的身份提供商,您必须让您的身份提供商的 Web 服务器证书由设置了 CA
标志为 TRUE
的证书签名,即使它是自签名的。这是因为 GoLang 的 TLS 客户端实现对证书验证的标准非常严格。如果您手头没有 CA,您可以使用标准证书生成工具创建简单的 CA 和签名的证书和密钥对。
使用 kubectl
选项 1 - OIDC 身份验证器
第一个选项是使用 kubectl oidc
身份验证器,它将 id_token
设置为所有请求的不记名令牌,并在令牌过期后刷新令牌。登录到提供商后,使用 kubectl 添加您的 id_token
、refresh_token
、client_id
和 client_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_token
和 client_secret
刷新您的 id_token
,并将 refresh_token
和 id_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
对象进行响应。
注意
Kubernetes API 服务器默认为向后兼容性发送authentication.k8s.io/v1beta1
令牌审查。要选择接收 authentication.k8s.io/v1
令牌审查,API 服务器必须使用 --authentication-token-webhook-version=v1
启动。{
"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"
}
}
身份验证代理
API 服务器可以配置为从请求头值(例如 X-Remote-User
)识别用户。它旨在与设置请求头值的身份验证代理结合使用。
--requestheader-username-headers
必需,不区分大小写。按顺序检查用户身份的头部名称。第一个包含值的头部用作用户名。--requestheader-group-headers
1.6+。可选,不区分大小写。“X-Remote-Group”建议使用。按顺序检查用户组的头部名称。所有指定头部中的所有值都用作组名称。--requestheader-extra-headers-prefix
1.6+。可选,不区分大小写。“X-Remote-Extra-”建议使用。查找头部前缀以确定有关用户的额外信息(通常由配置的授权插件使用)。任何以指定前缀开头的头部都会删除前缀。头部名称的其余部分转换为小写并进行 百分比解码,成为额外键,头部值是额外值。
注意
在 1.11.3 之前(以及 1.10.7、1.9.11),额外键只能包含 HTTP 头部标签中合法的 字符。例如,使用此配置
--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.34 [稳定]
(默认启用:true)AuthenticationConfiguration
可用于配置匿名身份验证器。如果您在 AuthenticationConfiguration
文件中设置匿名字段,则不能设置 --anonymous-auth
标志。
使用身份验证配置文件配置匿名身份验证器的主要优点是,除了启用和禁用匿名身份验证之外,您还可以配置哪些端点支持匿名身份验证。
下面是一个示例身份验证配置文件
---
#
# CAUTION: this is an example configuration.
# Do not use this for your own cluster!
#
apiVersion: apiserver.config.k8s.io/v1
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-Uid
仅在 1.22.0 及更高版本中可用。模拟用户组时使用的模拟头部的示例
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
注意
kubectl
无法模拟额外字段或 UID。要模拟用户、组、用户标识符 (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"]
注意
模拟用户或组允许您执行任何操作,就像您是该用户或组一样;因此,模拟不属于命名空间范围。如果您希望使用 Kubernetes RBAC 允许模拟,这需要使用ClusterRole
和 ClusterRoleBinding
,而不是 Role
和 RoleBinding
。client-go 凭证插件
k8s.io/client-go
以及使用它的工具(例如 kubectl
和 kubelet
)能够执行外部命令以接收用户凭据。
此功能旨在用于与 k8s.io/client-go
不原生支持的身份验证协议(LDAP、Kerberos、OAuth2、SAML 等)的客户端集成。该插件实现了协议特定的逻辑,然后返回不透明的凭据以供使用。几乎所有凭据插件用例都需要一个支持 Webhook 令牌身份验证器 的服务器端组件来解释客户端插件生成的凭据格式。
注意
早期版本的kubectl
包含了对 AKS 和 GKE 身份验证的内置支持,但现在已不再提供。示例用例
在一个假设的用例中,一个组织将运行一个外部服务,该服务将 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
对象打印到 stdout
。k8s.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 值 | 含义 |
---|---|
Never | 此执行插件从不需要使用标准输入,因此无论标准输入是否可用于用户输入,此执行插件都将运行。 |
如果可用 | 此执行插件希望在可用时使用标准输入,但即使标准输入不可用,它仍可运行。因此,无论标准输入是否可用于用户输入,此执行插件都将运行。如果标准输入可用于用户输入,则将提供给此执行插件。 |
Always | 此执行插件需要标准输入才能运行,因此仅当标准输入可用于用户输入时,此执行插件才会运行。如果标准输入不可用于用户输入,则执行插件将不运行,并且执行插件运行程序将返回错误。 |
要使用不记名令牌凭据,插件将在 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 握手。
如果指定,clientKeyData
和 clientCertificateData
都必须存在。
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 令牌身份验证 或 身份验证代理。
注意
Kubernetes API 服务器在应用所有身份验证机制(包括 模拟)后填充userInfo
。如果您或身份验证代理使用模拟进行 SelfSubjectReview,您将看到被模拟用户的用户详细信息和属性。默认情况下,当 APISelfSubjectReview
功能启用时,所有已认证的用户都可以创建 SelfSubjectReview
对象。这由 system:basic-user
集群角色允许。
注意
您只能在以下情况下发出 SelfSubjectReview
请求
下一步
- 要了解如何为用户颁发证书,请阅读 使用 CertificateSigningRequest 为 Kubernetes API 客户端颁发证书
- 阅读 客户端身份验证参考 (v1)
- 阅读 客户端身份验证参考 (v1beta1)