From c65afaea83b1498deb9625d5ac6b71fc87dbdf96 Mon Sep 17 00:00:00 2001 From: Qiming Teng Date: Wed, 26 Aug 2020 10:42:49 +0800 Subject: [PATCH] [zh] Translate reference/access-authn-authz/authentication.md --- .../access-authn-authz/authentication.md | 1564 +++++++++++++++++ 1 file changed, 1564 insertions(+) create mode 100644 content/zh/docs/reference/access-authn-authz/authentication.md diff --git a/content/zh/docs/reference/access-authn-authz/authentication.md b/content/zh/docs/reference/access-authn-authz/authentication.md new file mode 100644 index 0000000000..64c70cf898 --- /dev/null +++ b/content/zh/docs/reference/access-authn-authz/authentication.md @@ -0,0 +1,1564 @@ +--- +title: 用户认证 +content_type: concept +weight: 10 +--- + + + + +本页提供身份认证有关的概述。 + + + +## Kubernetes 中的用户 {#users-in-kubernetes} + +所有 Kubernetes 集群都有两类用户:由 Kubernetes 管理的服务账号和普通用户。 + +Kuernetes 假定使用以下方式之一来利用与集群无关的服务来管理普通用户: + +- 负责分发私钥的管理员 +- 类似 Keystone 或者 Google Accounts 这类用户数据库 +- 包含用户名和密码列表的文件 + +有鉴于此,_Kubernetes 并不包含用来代表普通用户账号的对象_。 +普通用户的信息无法通过 API 调用添加到集群中。 + + +尽管无法通过 API 调用来添加普通用户,Kubernetes 仍然认为能够提供由集群的证书 +机构签名的合法证书的用户是通过身份认证的用户。基于这样的配置,Kubernetes +使用证书中的 'subject' 的通用名称(Common Name)字段(例如,"/CN=bob")来 +确定用户名。接下来,基于角色访问控制(RBAC)子系统会确定用户是否有权针对 +某资源执行特定的操作。进一步的细节可参阅 +[证书请求](/zh/docs/reference/access-authn-authz/certificate-signing-requests/#normal-user) +下普通用户主题。 + + +与此不同,服务账号是 Kubernetes API 所管理的用户。它们被绑定到特定的名字空间, +或者由 API 服务器自动创建,或者通过 API 调用创建。服务账号与一组以 Secret 保存 +的凭据相关,这些凭据会被挂载到 Pod 中,从而允许集群内的进程访问 Kubernetes +API。 + +API 请求则或者与某普通用户相关联,或者与某服务账号相关联,亦或者被视作 +[匿名请求](#anonymous-requests)。这意味着集群内外的每个进程在向 API 服务器发起 +请求时都必须通过身份认证,否则会被视作匿名用户。这里的进程可以是在某工作站上 +输入 `kubectl` 命令的操作人员,也可以是节点上的 `kubelet` 组件,还可以是控制面 +的成员。 + + +## 身份认证策略 {#authentication-strategies} + +Kubernetes 使用身份认证插件利用客户端证书、持有者令牌(Bearer Token)、身份认证代理(Proxy) +或者 HTTP 基本认证机制来认证 API 请求的身份。HTTP 请求发给 API 服务器时, +插件会将以下属性关联到请求本身: + + +* 用户名:用来辩识最终用户的字符串。常见的值可以是 `kube-admin` 或 `jane@example.com`。 +* 用户 ID:用来辩识最终用户的字符串,旨在比用户名有更好的一致性和唯一性。 +* 用户组:取值为一组字符串,其中各个字符串用来标明用户是某个命名的用户逻辑集合的成员。 + 常见的值可能是 `system:masters` 或者 `devops-team` 等。 +* 附加字段:一组额外的键-值映射,键是字符串,值是一组字符串;用来保存一些鉴权组件可能 + 觉得有用的额外信息。 + + +所有(属性)值对于身份认证系统而言都是不透明的,只有被 +[鉴权组件](/zh/docs/reference/access-authn-authz/authorization/) +解释过之后才有意义。 + +你可以同时启用多种身份认证方法,并且你通常会至少使用两种方法: + +- 针对服务账号使用服务账号令牌 +- 至少另外一种方法对用户的身份进行认证 + + +当集群中启用了多个身份认证模块时,第一个成功地对请求完成身份认证的模块会 +直接做出评估决定。API 服务器并不保证身份认证模块的运行顺序。 + +对于所有通过身份认证的用户,`system:authenticated` 组都会被添加到其组列表中。 + +与其它身份认证协议(LDAP、SAML、Kerberos、X509 的替代模式等等)都可以通过 +使用一个[身份认证代理](#authenticating-proxy)或 +[身份认证 Webhoook](#webhook-token-authentication)来实现。 + + +### X509 客户证书 {#x509-client-certs} + +通过给 API 服务器传递 `--client-ca-file=SOMEFILE` 选项,就可以启动客户端证书身份认证。 +所引用的文件必须包含一个或者多个证书机构,用来验证向 API 服务器提供的客户端证书。 +如果提供了客户端证书并且证书被验证通过,则 subject 中的公共名称(Common Name)就被 +作为请求的用户名。 +自 Kubernetes 1.4 开始,客户端证书还可以通过证书的 organization 字段标明用户的组成员信息。 +要包含用户的多个组成员信息,可以在证书种包含多个 organization 字段。 + +例如,使用 `openssl` 命令行工具生成一个证书签名请求: + +``` bash +openssl req -new -key jbeda.pem -out jbeda-csr.pem -subj "/CN=jbeda/O=app1/O=app2" +``` + + +此命令将使用用户名 `jbeda` 生成一个证书签名请求(CSR),且该用户属于 "app" 和 +"app2" 两个用户组。 + +参阅[管理证书](/zh/docs/concepts/cluster-administration/certificates/)了解如何生成客户端证书。 + + +### 静态令牌文件 {#static-token-file} + +当 API 服务器的命令行设置了 `--token-auth-file=SOMEFILE` 选项时,会从文件中 +读取持有者令牌。目前,令牌会长期有效,并且在不重启 API 服务器的情况下 +无法更改令牌列表。 + +令牌文件是一个 CSV 文件,包含至少 3 个列:令牌、用户名和用户的 UID。 +其余列被视为可选的组名。 + + +{{< note >}} +如果要设置的组名不止一个,则对应的列必须用双引号括起来,例如 + +```conf +token,user,uid,"group1,group2,group3" +``` +{{< /note >}} + + +#### 在请求中放入持有者令牌 {#putting-a-bearer-token-in-a-request} + +当使用持有者令牌来对某 HTTP 客户端执行身份认证时,API 服务器希望看到 +一个名为 `Authorization` 的 HTTP 头,其值格式为 `Bearer THETOKEN`。 +持有者令牌必须是一个可以放入 HTTP 头部值字段的字符序列,至多可使用 +HTTP 的编码和引用机制。 +例如:如果持有者令牌为 `31ada4fd-adec-460c-809a-9e56ceb75269`,则其 +出现在 HTTP 头部时如下所示: + +```http +Authorization: Bearer 31ada4fd-adec-460c-809a-9e56ceb75269 +``` + + +### 启动引导令牌 {#bootstrap-tokens} + +{{< feature-state for_k8s_version="v1.18" state="stable" >}} + + +为了支持平滑地启动引导新的集群,Kubernetes 包含了一种动态管理的持有者令牌类型, +称作 *启动引导令牌(Bootstrap Token)*。 +这些令牌以 Secret 的形式保存在 `kube-system` 名字空间中,可以被动态管理和创建。 +控制器管理器包含的 `TokenCleaner` 控制器能够在启动引导令牌过期时将其删除。 + + +这些令牌的格式为 `[a-z0-9]{6}.[a-z0-9]{16}`。第一个部分是令牌的 ID;第二个部分 +是令牌的 Secret。你可以用如下所示的方式来在 HTTP 头部设置令牌: + +```http +Authorization: Bearer 781292.db7bc3a58fc5f07e +``` + + +你必须在 API 服务器上设置 `--enable-bootstrap-token-auth` 标志来启用基于启动 +引导令牌的身份认证组件。 +你必须通过控制器管理器的 `--controllers` 标志来启用 TokenCleaner 控制器; +这可以通过类似 `--controllers=*,tokencleaner` 这种设置来做到。 +如果你使用 `kubeadm` 来启动引导新的集群,该工具会帮你完成这些设置。 + + +身份认证组件的认证结果为 `system:bootstrap:<令牌 ID>`,该用户属于 +`system:bootstrappers` 用户组。 +这里的用户名和组设置都是有意设计成这样,其目的是阻止用户在启动引导集群之后 +继续使用这些令牌。 +这里的用户名和组名可以用来(并且已经被 `kubeadm` 用来)构造合适的鉴权 +策略,以完成启动引导新集群的工作。 + + +请参阅[启动引导令牌](/zh/docs/reference/access-authn-authz/bootstrap-tokens/) +以了解关于启动引导令牌身份认证组件与控制器的更深入的信息,以及如何使用 +`kubeadm` 来管理这些令牌。 + + +### Static Password File + +通过向 API 服务器传递 `--basic-auth-file=SOMEFILE` 选项可以启用基本的 +身份认证。目前,基本身份认证所涉及的凭据信息会长期有效,并且在不重启 API +服务器的情况下无法改变用户的密码。 +要注意的是,对基本身份认证的支持目前仅是出于方便性考虑。 +与此同时我们正在增强前述的、更为安全的模式的易用性。 + + +基本身份认证数据文件是一个 CSV 文件,包含至少 3 列:密码、用户名和用户 ID。 +在 Kuernetes 1.6 及后续版本中,你可以指定一个可选的第 4 列,在其中给出用逗号 +分隔的用户组名。如果用户组名不止一个,你必须将第 4 列的值用双引号括起来。 +参见下面的例子: + +```conf +password,user,uid,"group1,group2,group3" +``` + + +当在 HTTP 客户端使用基本身份认证机制时,API 服务器会期望看到名为 +`Authorization` 的 HTTP 头部,其值形如 `Basic USER:PASSWORD的Base64编码字符串` + + +### 服务账号令牌 {#service-account-tokens} + +服务账号(Service Account)是一种自动被启用的用户认证机制,使用经过签名的 +持有者令牌来验证请求。该插件可接受两个可选参数: + +* `--service-account-key-file` 一个包含用来为持有者令牌签名的 PEM 编码密钥。 + 若未指定,则使用 API 服务器的 TLS 私钥。 +* `--service-account-lookup` 如果启用,则从 API 删除的令牌会被回收。 + + +服务账号通常由 API 服务器自动创建并通过 `ServiceAccount` +[准入控制器](/zh/docs/reference/access-authn-authz/admission-controllers/) +关联到集群中运行的 Pod 上。 +持有者令牌会挂载到 Pod 中可预知的为之,允许集群内进程与 API 服务器通信。 +服务账号也可以使用 Pod 规约的 `serviceAccountName` 字段显式地关联到 Pod 上。 + + +{{< note >}} +`serviceAccountName` 通常会被忽略,因为关联关系是自动建立的。 +{{< /note >}} + +```yaml +apiVersion: apps/v1 +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 <名称>` 命令。此命令会在当前的名字空间中生成一个 +服务账号和一个与之关联的 Secret。 + +```bash +kubectl create serviceaccount jenkins +``` + +``` +serviceaccount/jenkins created +``` + + +查验相关联的 Secret: + +```bash +kubectl get serviceaccounts jenkins -o yaml +``` + +```yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + # ... +secrets: +- name: jenkins-token-1yvwg +``` + + +所创建的 Secret 中会保存 API 服务器的公开的 CA 证书和一个已签名的 JSON Web +令牌(JWT)。 + +```bash +kubectl get secret jenkins-token-1yvwg -o yaml +``` + + +```yaml +apiVersion: v1 +data: + ca.crt: + namespace: ZGVmYXVsdA== + token: +kind: Secret +metadata: + # ... +type: kubernetes.io/service-account-token +``` + + +{{< note >}} +字段值是按 Base64 编码的,这是因为 Secret 数据总是采用 Base64 编码来存储。 +{{< /note >}} + + +已签名的 JWT 可以用作持有者令牌,并将被认证为所给的服务账号。 +关于如何在请求中包含令牌,请参阅[前文](#putting-a-bearer-token-in-a-request)。 +通常,这些 Secret 数据会被挂载到 Pod 中以便集群内访问 API 服务器时使用, +不过也可以在集群外部使用。 + + +服务账号被身份认证后,所确定的用户名为 `system:serviceaccount:<名字空间>:<服务账号>`, +并被分配到用户组 `system:serviceaccounts` 和 `system:serviceaccounts:<名字空间>`。 + +警告:由于服务账号令牌保存在 Secret 对象中,任何能够读取这些 Secret 的用户 +都可以被认证为对应的服务账号。在为用户授予访问服务账号的权限时,以及对 Secret +的读权限时,要格外小心。 + + +### OpenID Connect(OIDC)令牌 {#openid-connect-tokens} + +[OpenID Connect](https://openid.net/connect/) 是一种 OAuth2 认证方式, +被某些 OAuth2 提供者支持,例如 Azure 活动目录、Salesforce 和 Google。 +协议对 OAuth2 的主要扩充体现在有一个附加字段会和访问令牌一起返回, +这一字段称作 [ID Token(ID 令牌)](https://openid.net/specs/openid-connect-core-1_0.html#IDToken)。 +ID 令牌是一种由服务器签名的 JSON Web 令牌(JWT),其中包含一些可预知的字段, +例如用户的邮箱地址, + + +要识别用户,身份认证组件使用 OAuth2 +[令牌响应](https://openid.net/specs/openid-connect-core-1_0.html#TokenResponse) +中的 `id_token`(而非 `access_token`)作为持有者令牌。 +关于如何在请求中设置令牌,可参见[前文](#putting-a-bearer-token-in-a-request)。 + +![Kubernetes OpenID Connect Flow](/images/docs/admin/k8s_oidc_login.svg) + + +1. 登录到你的身份服务(Identity Provider) +2. 你的身份服务将为你提供 `access_token`、`id_token` 和 `refresh_token` +3. 在使用 `kubectl` 时,将 `id_token` 设置为 `--token` 标志值,或者将其直接添加到 + `kubeconfig` 中 +4. `kubectl` 将你的 `id_token` 放到一个称作 `Authorization` 的头部,发送给 API 服务器 +5. API 服务器将负责通过检查配置中引用的证书来确认 JWT 的签名是合法的 +6. 检查确认 `id_token` 尚未过期 +7. 确认用户有权限执行操作 +8. 鉴权成功之后,API 服务器向 `kubectl` 返回响应 +9. `kubectl` 向用户提供反馈信息 + + +由于用来验证你是谁的所有数据都在 `id_token` 中,Kubernetes 不需要再去联系 +身份服务。在一个所有请求都是无状态请求的模型中,这一工作方式可以使得身份认证 +的解决方案更容易处理大规模请求。不过,此访问也有一些挑战: + + +1. Kubernetes 没有提供用来触发身份认证过程的 "Web 界面"。 + 因为不存在用来收集用户凭据的浏览器或用户接口,你必须自己先行完成 + 对身份服务的认证过程。 +2. `id_token` 令牌不可收回。因其属性类似于证书,其生命期一般很短(只有几分钟), + 所以,每隔几分钟就要获得一个新的令牌这件事可能很让人头疼。 +3. 如果不使用 `kubectl proxy` 命令或者一个能够注入 `id_token` 的反向代理, + 向 Kubernetes 控制面板执行身份认证是很困难的。 + + +#### 配置 API 服务器 {#configuring-the-api-server} + +要启用此插件,须在 API 服务器上配置以下标志: + + + +| 参数 | 描述 | 示例 | 必需? | +| --------- | ----------- | ------- | ------- | +| `--oidc-issuer-url` | 允许 API 服务器发现公开的签名密钥的服务的 URL。只接受模式为 `https://` 的 URL。此值通常设置为服务的发现 URL,不含路径。例如:"https://accounts.google.com" 或 "https://login.salesforce.com"。此 URL 应指向 .well-known/openid-configuration 下一层的路径。 | 如果发现 URL 是 `https://accounts.google.com/.well-known/openid-configuration`,则此值应为 `https://accounts.google.com` | 是 | +| `--oidc-client-id` | 所有令牌都应发放给此客户 ID。 | kubernetes | 是 | +| `--oidc-username-claim` | 用作用户名的 JWT 申领(JWT Claim)。默认情况下使用 `sub` 值,即最终用户的一个唯一的标识符。管理员也可以选择其他申领,例如 `email` 或者 `name`,取决于所用的身份服务。不过,除了 `email` 之外的申领都会被添加令牌发放者的 URL 作为前缀,以免与其他插件产生命名冲突。 | sub | 否 | +| `--oidc-username-prefix` | 要添加到用户名申领之前的前缀,用来避免与现有用户名发生冲突(例如:`system:` 用户)。例如,此标志值为 `oidc:` 时将创建形如 `oidc:jane.doe` 的用户名。如果此标志未设置,且 `--oidc-username-claim` 标志值不是 `email`,则默认前缀为 `<令牌发放者的 URL>#`,其中 `<令牌发放者 URL >` 的值取自 `--oidc-issuer-url` 标志的设定。此标志值为 `-` 时,意味着禁止添加用户名前缀。 | `oidc:` | 否 | +| `--oidc-groups-claim` | 用作用户组名的 JWT 申领。如果所指定的申领确实存在,则其值必须是一个字符串数组。 | groups | 否 | +| `--oidc-groups-prefix` | 添加到组申领的前缀,用来避免与现有用户组名(如:`system:` 组)发生冲突。例如,此标志值为 `oidc:` 时,所得到的用户组名形如 `oidc:engineering` 和 `oidc:infra`。 | `oidc:` | 否 | +| `--oidc-required-claim` | 取值为一个 key=value 偶对,意为 ID 令牌中必须存在的申领。如果设置了此标志,则 ID 令牌会被检查以确定是否包含取值匹配的申领。此标志可多次重复,以指定多个申领。 | `claim=value` | 否 | +| `--oidc-ca-file` | 指向一个 CA 证书的路径,该 CA 负责对你的身份服务的 Web 证书提供签名。默认值为宿主系统的根 CA。 | `/etc/kubernetes/ssl/kc-ca.pem` | 否 | + + +很重要的一点是,API 服务器并非一个 OAuth2 客户端,相反,它只能被配置为 +信任某一个令牌发放者。这使得使用公共服务(如 Google)的用户可以不信任发放给 +第三方的凭据。 +如果管理员希望使用多个 OAuth 客户端,他们应该研究一下那些支持 `azp` +(Authorized Party,被授权方)申领的服务。 +`azp` 是一种允许某客户端代替另一客户端发放令牌的机制。 + + +Kubernetes 并未提供 OpenID Connect 的身份服务。 +你可以使用现有的公共的 OpenID Connect 身份服务(例如 Google 或者 +[其他服务](https://connect2id.com/products/nimbus-oauth-openid-connect-sdk/openid-connect-providers))。 +或者,你也可以选择自己运行一个身份服务,例如 +CoreOS [dex](https://github.com/coreos/dex)、 +[Keycloak](https://github.com/keycloak/keycloak)、 +CloudFoundry [UAA](https://github.com/cloudfoundry/uaa) 或者 +Tremolo Security 的 +[OpenUnison](https://github.com/tremolosecurity/openunison)。 + + +要在 Kubernetes 环境中使用某身份服务,该服务必须: + +1. 支持 [OpenID connect 发现](https://openid.net/specs/openid-connect-discovery-1_0.html); + 但事实上并非所有服务都具备此能力 +2. 运行 TLS 协议且所使用的加密组件都未过时 +3. 拥有由 CA 签名的证书(即使 CA 不是商业 CA 或者是自签名的 CA 也可以) + + +关于上述第三条需求,即要求具备 CA 签名的证书,有一些额外的注意事项。 +如果你部署了自己的身份服务,而不是使用云厂商(如 Google 或 Microsoft)所提供的服务, +你必须对身份服务的 Web 服务器证书进行签名,签名所用证书的 `CA` 标志要设置为 +`TRUE`,即使用的是自签名证书。这是因为 GoLang 的 TLS 客户端实现对证书验证 +标准方面有非常严格的要求。如果你手头没有现成的 CA 证书,可以使用 CoreOS +团队所开发的[这个脚本](https://github.com/coreos/dex/blob/1ee5920c54f5926d6468d2607c728b71cfe98092/examples/k8s/gencert.sh)来创建一个简单的 CA 和被签了名的证书与密钥对。 +或者你也可以使用[这个类似的脚本](https://raw.githubusercontent.com/TremoloSecurity/openunison-qs-kubernetes/master/src/main/bash/makessl.sh),生成一个合法期更长、密钥尺寸更大的 SHA256 证书。 + + +特定系统的安装指令: + +- [UAA](https://docs.cloudfoundry.org/concepts/architecture/uaa.html) +- [Dex](https://github.com/dexidp/dex/blob/master/Documentation/kubernetes.md) +- [OpenUnison](https://www.tremolosecurity.com/orchestra-k8s/) + + +#### 使用 kubectl {#using-kubectl} + +##### 选项一 - OIDC 身份认证组件 + +第一种方案是使用 kubectl 的 `oidc` 身份认证组件,该组件将 `id_token` 设置 +为所有请求的持有者令牌,并且在令牌过期时自动刷新。在你登录到你的身份服务之后, +可以使用 kubectl 来添加你的 `id_token`、`refresh_token`、`client_id` 和 +`client_secret`,以配置该插件。 + +如果服务在其刷新令牌响应中不包含 `id_token`,则此插件无法支持该服务。 +这时你应该考虑下面的选项二。 + +```bash +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 ) +``` + + +作为示例,在完成对你的身份服务的身份认证之后,运行下面的命令: + +```bash +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 +``` + + +此操作会生成以下配置: + +```yaml +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` 来刷新你的 +`id_token`,并且在 `client_secret` 中存放 `refresh_token` 的新值,同时把 +`id_token` 的新值写入到 `.kube/config` 文件中。 + + +##### 选项二 - 使用 `--token` 选项 + +`kubectl` 命令允许你使用 `--token` 选项传递一个令牌。 +你可以将 `id_token` 的内容复制粘贴过来,作为此标志的取值: + +```bash +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-token-authentication} + +Webhook 身份认证是一种用来验证持有者令牌的回调机制。 + +* `--authentication-token-webhook-config-file` 指向一个配置文件,其中描述 + 如何访问远程的 Webhook 服务。 +* `--authentication-token-webhook-cache-ttl` 用来设定身份认证决定的缓存时间。 + 默认时长为 2 分钟。 + + +配置文件使用 [kubeconfig](/docs/concepts/configuration/organize-cluster-access-kubeconfig/) +文件的格式。文件中,`clusters` 指代远程服务,`users` 指代远程 API 服务 +Webhook。下面是一个例子: + + +```yaml +# Kubernetes API 版本 +apiVersion: v1 +# API 对象类别 +kind: Config +# clusters 指代远程服务 +clusters: + - name: name-of-remote-authn-service + cluster: + certificate-authority: /path/to/ca.pem # 用来验证远程服务的 CA + server: https://authn.example.com/authenticate # 要查询的远程服务 URL。必须使用 'https'。 + +# users 指代 API 服务的 Webhook 配置 +users: + - name: name-of-api-server + user: + client-certificate: /path/to/cert.pem # Webhook 插件要使用的证书 + client-key: /path/to/key.pem # 与证书匹配的密钥 + +# kubeconfig 文件需要一个上下文(Context),此上下文用于本 API 服务器 +current-context: webhook +contexts: +- context: + cluster: name-of-remote-authn-service + user: name-of-api-sever + name: webhook +``` + + +当客户端尝试在 API 服务器上使用持有者令牌完成身份认证( +如[前](#putting-a-bearer-token-in-a-request)所述)时, +身份认证 Webhook 会用 POST 请求发送一个 JSON 序列化的对象到远程服务。 +该对象是 `authentication.k8s.io/v1beta1` 组的 `TokenReview` 对象, +其中包含持有者令牌。 +Kubernetes 不会强制请求提供此 HTTP 头部。 + + +要注意的是,Webhook API 对象和其他 Kubernetes API 对象一样,也要受到同一 +[版本兼容规则](/zh/docs/concepts/overview/kubernetes-api/)约束。 +实现者要了解对 Beta 阶段对象的兼容性承诺,并检查请求的 `apiVersion` 字段, +以确保数据结构能够正常反序列化解析。此外,API 服务器必须启用 +`authentication.k8s.io/v1beta1` API 扩展组 +(`--runtime-config=authentication.k8s.io/v1beta1=true`)。 + +POST 请求的 Body 部分将是如下格式: + + +```json +{ + "apiVersion": "authentication.k8s.io/v1beta1", + "kind": "TokenReview", + "spec": { + "token": "<持有者令牌>" + } +} +``` + + +远程服务应该会填充请求的 `status` 字段,以标明登录操作是否成功。 +响应的 Body 中的 `spec` 字段会被忽略,因此可以省略。 +如果持有者令牌验证成功,应该返回如下所示的响应: + +```json +{ + "apiVersion": "authentication.k8s.io/v1beta1", + "kind": "TokenReview", + "status": { + "authenticated": true, + "user": { + "username": "janedoe@example.com", + "uid": "42", + "groups": [ + "developers", + "qa" + ], + "extra": { + "extrafield1": [ + "extravalue1", + "extravalue2" + ] + } + } + } +} +``` + + +而不成功的请求会返回: + +```json +{ + "apiVersion": "authentication.k8s.io/v1beta1", + "kind": "TokenReview", + "status": { + "authenticated": false + } +} +``` + + +HTTP 状态码可用来提供进一步的错误语境信息。 + + +### 身份认证代理 {#authenticating-proxy} + +API 服务器可以配置成从请求的头部字段值(如 `X-Remote-User`)中辩识用户。 +这一设计是用来与某身份认证代理一起使用 API 服务器,代理负责设置请求的头部字段值。 + + +* `--requestheader-username-headers` 必需字段,大小写不敏感。用来设置要获得用户身份所要检查的头部字段名称列表(有序)。第一个包含数值的字段会被用来提取用户名。 +* `--requestheader-group-headers` 可选字段,在 Kubernetes 1.6 版本以后支持,大小写不敏感。 + 建议设置为 "X-Remote-Group"。用来指定一组头部字段名称列表,以供检查用户所属的组名称。 + 所找到的全部头部字段的取值都会被用作用户组名。 +* `--requestheader-extra-headers-prefix` 可选字段,在 Kubernetes 1.6 版本以后支持,大小写不敏感。 + 建议设置为 "X-Remote-Extra-"。用来设置一个头部字段的前缀字符串,API 服务器会基于所给 + 前缀来查找与用户有关的一些额外信息。这些额外信息通常用于所配置的鉴权插件。 + API 服务器会将与所给前缀匹配的头部字段过滤出来,去掉其前缀部分,将剩余部分 + 转换为小写字符串并在必要时执行[百分号解码](https://tools.ietf.org/html/rfc3986#section-2.1) + 后,构造新的附加信息字段键名。原来的头部字段值直接作为附加信息字段的值。 + + +{{< note >}} +在 1.13.3 版本之前(包括 1.10.7、1.9.11),附加字段的键名只能包含 +[HTTP 头部标签的合法字符](https://tools.ietf.org/html/rfc7230#section-3.2.6)。 +{{< /note >}} + +例如,使用下面的配置: + +``` +--requestheader-username-headers=X-Remote-User +--requestheader-group-headers=X-Remote-Group +--requestheader-extra-headers-prefix=X-Remote-Extra- +``` + + +针对所收到的如下请求: + +```http +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 +``` + + +会生成下面的用户信息: + +```yaml +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 名的、合法的客户端证书。 + + +## 匿名请求 {#anonymous-requests} + +启用匿名请求支持之后,如果请求没有被已配置的其他身份认证方法拒绝,则被视作 +匿名请求(Anonymous Requests)。这类请求获得用户名 `system:anonymous` 和 +对应的用户组 `system:unauthenticated`。 + + +例如,在一个配置了令牌身份认证且启用了匿名访问的服务器上,如果请求提供了非法的 +持有者令牌,则会返回 `401 Unauthorized` 错误。 +如果请求没有提供持有者令牌,则被视为匿名请求。 + +在 1.5.1-1.5.x 版本中,匿名访问默认情况下是被禁用的,可以通过为 API 服务器设定 +`--anonymous-auth=true` 来启用。 + + +在 1.6 及之后版本中,如果所使用的鉴权模式不是 `AlwaysAllow`,则匿名访问默认是被启用的。 +从 1.6 版本开始,ABAC 和 RBAC 鉴权模块要求对 `system:anonymous` 用户或者 +`system:unauthenticated` 用户组执行显式的权限判定,所以之前的为 `*` 用户或 +`*` 用户组赋予访问权限的策略规则都不再包含匿名用户。 + + +## 用户伪装 {#user-impersonation} + +一个用户可以通过伪装(Impersonation)头部字段来以另一个用户的身份执行操作。 +使用这一能力,你可以手动重载请求被身份认证所识别出来的用户信息。 +例如,管理员可以使用这一功能特性来临时伪装成另一个用户,查看请求是否被拒绝, +从而调试鉴权策略中的问题, + + +带伪装的请求首先会被身份认证识别为发出请求的用户,之后会切换到使用被伪装的用户 +的用户信息。 + +* 用户发起 API 调用时 _同时_ 提供自身的凭据和伪装头部字段信息 +* API 服务器对用户执行身份认证 +* API 服务器确认通过认证的用户具有伪装特权 +* 请求用户的信息被替换成伪装字段的值 +* 评估请求,鉴权组件针对所伪装的用户信息执行操作 + + +以下 HTTP 头部字段可用来执行伪装请求: + +* `Impersonate-User`:要伪装成的用户名 +* `Impersonate-Group`:要伪装成的用户组名。可以多次指定以设置多个用户组。 + 可选字段;要求 "Impersonate-User" 必须被设置。 +* `Impersonate-Extra-<附加名称>`:一个动态的头部字段,用来设置与用户相关的附加字段。 + 此字段可选;要求 "Impersonate-User" 被设置。为了能够以一致的形式保留, + `<附加名称>`部分必须是小写字符,如果有任何字符不是 + [合法的 HTTP 头部标签字符](https://tools.ietf.org/html/rfc7230#section-3.2.6), + 则必须是 utf8 字符,且转换为[百分号编码](https://tools.ietf.org/html/rfc3986#section-2.1)。 + + +{{< note >}} +在 1.11.3 版本之前(以及 1.10.7、1.9.11),`<附加名称>` 只能包含 +合法的 HTTP 标签字符。 +{{< /note >}} + + +头部字段集合的示例: + +```http +Impersonate-User: jane.doe@example.com +Impersonate-Group: developers +Impersonate-Group: admins +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 +``` + + +在使用 `kubectl` 时,可以使用 `--as` 标志来配置 `Impersonate-User` 头部字段值, +使用 `--as-group` 标志配置 `Impersonate-Group` 头部字段值。 + +```bash +kubectl drain mynode +``` + +```none +Error from server (Forbidden): User "clark" cannot get nodes at the cluster scope. (get nodes mynode) +``` + + +设置 `--as` 和 `--as-group` 标志: + +```bash +kubectl drain mynode --as=superman --as-group=system:masters +``` + +```none +node/mynode cordoned +node/mynode drained +``` + + +要伪装成某个用户、某个组或者设置附加字段,执行伪装操作的用户必须具有对所伪装的 +类别(“user”、“group” 等)执行 “impersonate” 动词操作的能力。 +对于启用了 RBAC 鉴权插件的集群,下面的 ClusterRole 封装了设置用户和组伪装字段 +所需的规则: + +```yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: impersonator +rules: +- apiGroups: [""] + resources: ["users", "groups", "serviceaccounts"] + verbs: ["impersonate"] +``` + + +附加字段会被作为 `userextras` 资源的子资源来执行权限评估。 +如果要允许用户为附加字段 “scopes” 设置伪装头部,该用户需要被授予以下规则: + +```yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: scopes-impersonator +rules: +# 可以设置 "Impersonate-Extra-scopes" 头部 +- apiGroups: ["authentication.k8s.io"] + resources: ["userextras/scopes"] + verbs: ["impersonate"] +``` + + +你也可以通过约束资源可能对应的 `resourceNames` 限制伪装头部的取值: + +```yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: limited-impersonator +rules: +# 可以伪装成用户 "jane.doe@example.com" +- apiGroups: [""] + resources: ["users"] + verbs: ["impersonate"] + resourceNames: ["jane.doe@example.com"] + +# 可以伪装成用户组 "developers" 和 "admins" +- apiGroups: [""] + resources: ["groups"] + verbs: ["impersonate"] + resourceNames: ["developers","admins"] + +# 可以将附加字段 "scopes" 伪装成 "view" 和 "development" +- apiGroups: ["authentication.k8s.io"] + resources: ["userextras/scopes"] + verbs: ["impersonate"] + resourceNames: ["view", "development"] +``` + + +## client-go 凭据插件 {#client-go-credential-plugins} + +{{< feature-state for_k8s_version="v1.11" state="beta" >}} + + +`k8s.io/client-go` 及使用它的工具(如 `kubectl` 和 `kubelet`)可以执行某个外部 +命令来获得用户的凭据信息。 + +这一特性的目的是便于客户端与 `k8s.io/client-go` 并不支持的身份认证协议(LDAP、 +Kerberos、OAuth2、SAML 等)继承。 +插件实现特定于协议的逻辑,之后返回不透明的凭据以供使用。 +几乎所有的凭据插件使用场景中都需要在服务器端存在一个支持 +[Webhook 令牌身份认证组件](#webhook-token-authentication)的模块, +负责解析客户端插件所生成的凭据格式。 + + +### 示例应用场景 {#example-use-case} + +在一个假想的应用场景中,某组织运行这一个外部的服务,能够将特定用户的已签名的 +令牌转换成 LDAP 凭据。此服务还能够对 +[Webhook 令牌身份认证组件](#webhook-token-authentication)的请求做出响应以 +验证所提供的令牌。用户需要在自己的工作站上安装一个凭据插件。 + + +要对 API 服务器认证身份时: + +* 用户发出 `kubectl` 命令。 +* 凭据插件提示用户输入 LDAP 凭据,并与外部服务交互,获得令牌。 +* 凭据插件将令牌返回该 client-go,后者将其用作持有者令牌提交给 API 服务器。 +* API 服务器使用[Webhook 令牌身份认证组件](#webhook-token-authentication)向 + 外部服务发出 `TokenReview` 请求。 +* 外部服务检查令牌上的签名,返回用户的用户名和用户组信息。 + + +### 配置 {#configuration} + +凭据插件通过 [kubectl 配置文件](/zh/docs/tasks/access-application-cluster/configure-access-multiple-clusters/) +来作为 user 字段的一部分设置。 + + +```yaml +apiVersion: v1 +kind: Config +users: +- name: my-user + user: + exec: + # 要执行的命令。必需。 + command: "example-client-go-exec-plugin" + + # 解析 ExecCredentials 资源时使用的 API 版本。必需。 + # + # 插件返回的 API 版本必需与这里列出的版本匹配。 + # + # 要与支持多个版本的工具(如 client.authentication.k8sio/v1alpha1)集成, + # 可以设置一个环境变量或者向工具传递一个参数标明 exec 插件所期望的版本。 + apiVersion: "client.authentication.k8s.io/v1beta1" + + # 执行此插件时要设置的环境变量。可选字段。 + env: + - name: "FOO" + value: "bar" + + # 执行插件时要传递的参数。可选字段。 + args: + - "arg1" + - "arg2" +clusters: +- name: my-cluster + cluster: + server: "https://172.17.4.100:6443" + certificate-authority: "/etc/kubernetes/ca.pem" +contexts: +- name: my-cluster + context: + cluster: my-cluster + user: my-user +current-context: my-cluster +``` + + +解析相对命令路径时,kubectl 将其视为与配置文件比较而言的相对路径。 +如果 KUBECONFIG 被设置为 `/home/jane/kubeconfig`,而 exec 命令为 +`./bin/example-client-go-exec-plugin`,则要执行的可执行文件为 +`/home/jane/bin/example-client-go-exec-plugin`。 + +```yaml +- name: my-user + user: + exec: + # 对 kubeconfig 目录而言的相对路径 + command: "./bin/example-client-go-exec-plugin" + apiVersion: "client.authentication.k8s.io/v1beta1" +``` + + +### 输出和输出格式 {#input-and-output-formats} + +所执行的命令会在 `stdout` 打印 `ExecCredential` 对象。 +`k8s.io/client-go` 使用 `status` 中返回的凭据信息向 Kubernetes API 服务器 +执行身份认证。 + +在交互式会话中运行时,`stdin` 是直接暴露给插件使用的。 +插件应该使用 +[TTY check](https://godoc.org/golang.org/x/crypto/ssh/terminal#IsTerminal) +来确定是否适合用交互方式请求用户输入。 + +与使用持有者令牌凭据,插件在 `ExecCredential` 的状态中返回一个令牌: + +```json +{ + "apiVersion": "client.authentication.k8s.io/v1beta1", + "kind": "ExecCredential", + "status": { + "token": "my-bearer-token" + } +} +``` + + +另一种方案是,返回 PEM 编码的客户端证书和密钥,以便执行 TLS 客户端身份认证。 +如果插件在后续调用中返回了不同的证书或密钥,`k8s.io/client-go` +会终止其与服务器的连接,从而强制执行新的 TLS 握手过程。 + +如果指定了这种方式,则 `clientKeyData` 和 `clientCertificateData` 字段都必需存在。 + +`clientCertificateData` 字段可能包含一些要发送给服务器的中间证书(Intermediate +Certificates)。 + +```json +{ + "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-----" + } +} +``` + + +作为一种可选方案,响应中还可以包含以 RFC3339 时间戳格式给出的证书到期时间。 +证书到期时间的有无会有如下影响: + +- 如果响应中包含了到期时间,持有者令牌和 TLS 凭据会被缓存,直到到期期限到来、 + 或者服务器返回 401 HTTP 状态码,或者进程退出。 +- 如果未指定到期时间,则持有者令牌和 TLS 凭据会被缓存,直到服务器返回 401 + HTTP 状态码或者进程退出。 + +```json +{ + "apiVersion": "client.authentication.k8s.io/v1beta1", + "kind": "ExecCredential", + "status": { + "token": "my-bearer-token", + "expirationTimestamp": "2018-03-05T17:30:20-08:00" + } +} +``` +