# k8s 认证

# k8s 账户

所有 k8s 集群有两类用户:由 k8s 管理的 Service Accounts (服务账户)和(Users Accounts) 普通账户。

普通账户是假定被外部或独立服务管理的,由管理员分配 keys,用户像使用 Keystone 或 google 账号一样,被存储在包含 usernames 和 passwords 的 list 的文件里。

需要注意:在 k8s 中不能通过 API 调用将普通用户添加到集群中。

相比之下,Service Accounts 是由 k8s API 管理的帐户。它们被绑定到特定的命名空间,并由 APIserver 自动创建或通过 API 调用手动创建。Service Accounts 与存储为 Secrets 的一组证书相关联,这些凭据被挂载到 pod 中,以便集群进程与 k8s API 通信。

# 验证策略

k8s 用户可以使用 client certificates、bearer tokens、authenticating proxy、HTTP basic auth 等认证插件来验证 API 请求。比如 HTTP 请求到达 API Server,插件会尝试将以下属性与请求关联:

  • UserName:普通用户的字符串。比如“kube-admin”或“xxxx@kubernetes.org.cn”。
  • UID:普通用户的字符串,比 UserName 更具有唯一性。
  • Groups:一组字符串,将常用的 user 分组的组合字符串。
  • Extra fields:将一些有用的字符串信息映射成的列表。

所有 values 值对于认证系统都是不透明的,只有当授权解释后才有意义。

可以同时启用多个认证方法。最少使用两种:

  • 为 Service Accounts 使用 service account tokens 方法。
  • 使用另外一种用户认证方法。

system:authenticated 组被包括在所有已认证用户的组列表中。

# X509 客户证书

使用 API Server 启动时配置–client-ca-file = SOMEFILE 选项来启用客户端证书认证。引用的文件必须包含,提交给 API Server 的客户端证书的证书颁发机构。如果客户端提交的证书通过,通用名称(common name)将被用作请求的用户名。

例如,使用 openssl 命令管理工具生成证书签名请求:

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

将为“jbeda”用户名创建一个 CSR,所属组为”app1”和“app2”的签名请求。

# Static Token File

启动静态 Token 文件需要 API Server 启动时配置- -token-auth-file=SOMEFILE 选项时,目前,tokens 状态可以持续存在,需要重新启用 API server,token last 才会重启。

token 文件是至少包含 3 列的 csv 格式文件: token, user name, user uid,第四列为可选 group 名项。注意:如果有多个 group 名,列必须用””双引号包含其中,例如

token,user,uid,"group1,group2,group3"

# Putting a Bearer Token in a Request

当 http 客户端使用 bearer token 认证时,API Server 需要一个值为 Bearer THETOKEN 的授权头。bearer token 必须可以放在 HTTP 请求头中且值不需要转码和引用的一个字符串。例如:bearer token:31ada4fd-adec-460c-809a-9e56ceb75269,会在 HTTP header 中按下面的方式呈现:

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

# Bootstrap Tokens

功能目前为 Alpha 级别

k8s 包括一个 dynamically-managed 的 Bearer token 类型,称为 Bootstrap Token。这些 token 作为 Secret 存储在 kube-system namespace 中,可以动态管理和创建。Controller Manager 包含一个 TokenCleaner Controller,如果到期时可以删除 Bootstrap Tokens Controller。

Tokens 的形式是[a-z0-9]{6}.[a-z0-9]{16}。第一个组件是 Token ID,第二个组件是 Token Secret。可以在 HTTP header 中指定 Token,如下所示:

Authorization: Bearer 781292.db7bc3a58fc5f07e

如何使用 Kubeadm 来管理 Token,以及想了解更多 BootstrapToken 文档,请参考 Bootstrap Token

# Static Password File

Basic Authentication 是通过在 API Server 启动是配置-Basic-authfile=file 选项实现,Basic 认证凭证一直有效,并且如果没有重新启动 API Server,密码将无法更改。

Basic Authentication 文件 csv 也是格式文件,且必须包含:password, user, uid。在 k8s 1.6+版本中,可以指定一个可选的第四列,使用逗号来分隔 group 名,如果有多个组,必须使用双引号(“)。参考以下示例:

password,user,uid,"group1,group2,group3"

当从 http 客户端使用 Basic Authentication 时,API Server 需要在请求头加入 Basic BASE64ENCODED(USER:PASSWORD)。

# OpenID Connect Tokens

OpenID Connect 是由一些云提供商支持的 OAuth2 认证机制,特别是 Azure Active Directory,Salesforce 和 Google。OAuth2 协议的主要扩展是增加一个额外字段,返回 ID Token 的 access token。这个 token 被服务器签名的 JSON Web Token (JWT),常见的字段如 user’s email。

为了鉴定 user,认证器使用 OAuth2 token 响应的 id_token 步骤如下:

Kubernetes OpenID Connect Flow

  1. 登录 identity provider
  2. identity provider 提供一个 access_token,id_token 和 refresh_token
  3. 在使用 kubectl 时,使用 id_token 的—token flag 或者直接添加到 kubeconfig
  4. kubectl 在 header 通过 Authorization 字段将 id_token 发送到 API server
  5. API Server 检查配置中证书来确保 JWT 签名的有效性
  6. 检查 id_token 是否过期
  7. 确保 user 被授权
  8. 当被授权 API Server 会返回对 kubectl 响应
  9. kubectl 向 user 提供反馈

由于所有认证需要的数据在 id_token 中,所以 k8s 不需要向身份提供者“phone home”,每个请求都是在无状态的模型中,它为认证提供可扩展的解决方案。 也带来了一些挑战:

  1. k8s 没有“web interface”来触发认证过程,没有任何浏览器或接口来收集凭据,这就是为什么需要首先向身份提供者进行认证的原因。
  2. id_token 不能被撤销,它像一个 certificate 持续时间非常短(只有几分钟),所以每隔几分钟就得到一个新的 token。
  3. 如果不使用 kubectl -proxy 命令或注入 id_token 的反向代理,将无法向 k8s dashboard 进行认证。

# Configuring the API Server

启用插件,请在 API Server 上配置如下的选项:

Parameter Description Example Required
- -oidc-issuer-url URL of the provider which allows the API server to discover public signing keys. Only URLs which use the https:// scheme are accepted. This is typically the provider’s discovery URL without a path, for example “https://accounts.google.com” or “https://login.salesforce.com”. This URL should point to the level below .well-known/openid-configuration If the discovery URL is https://accounts.google.com/.well-known/openid-configuration the value should be https://accounts.google.com
- -oidc-client-id A client id that all tokens must be issued for. kubernetes
- -oidc-username-claim WT claim to use as the user name. By default sub, which is expected to be a unique identifier of the end user. Admins can choose other claims, such as email or name, depending on their provider. However, claims other than email will be prefixed with the issuer URL to prevent naming clashes with other plugins. sub
- -oidc-groups-claim JWT claim to use as the user’s group. If the claim is present it must be an array of strings. groups
- -oidc-ca-file The path to the certificate for the CA that signed your identity provider’s web certificate. Defaults to the host’s root CAs. /etc/kubernetes/ssl/kc-ca.pem

如果--oidc-username-claim 选择除 Email 以外的声明,该值将以--oidc-issuer-url 为前缀,以防止与现有 k8s 名称(例如 system:users)的冲突。 例如,如果提供商 URL 是 https://accounts.google.com,username 声明映射到 jane,该插件对 user 进行认证为:

https://accounts.google.com#jane

k8s 不提供 OpenID Connect Identity Provider。可以使用现有公开的 OpenID Connect Identity Provider(如 Google 或其他)。或者可以使用者些 CoreOS dex,Keycloak,CloudFoundry UAA 或 Tremolo Security 的 OpenUnison。

For an identity provider to work with Kubernetes it must

  1. 支持 OpenID connect discovery;(not all do)
  2. 在 TLS 中确保密码有效
  3. 一个 CA 签名的证书(商业或个人的)

其他安装设置说明:

  • UAA
  • Dex
  • OpenUnison

# 使用 kubectl

# 选项 1-OIDC AUTHENTICATOR

第一个选择使用 oidc authenticator。这个认证需要使用 id_token,refresh_token 和 OIDC client_secret,并会自动更新你的 token 令牌。

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 ) \
   --auth-provider-arg=extra-scopes=( comma separated list of scopes to add to "openid email profile", optional )

例如,在对身份提供商进行认证之后,运行下面的命令:

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=extra-scopes=groups \
        --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
        extra-scopes: groups
        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,在 kube/.config 中存储 refresh_token 和 id_token 新的值。

# 选项 2 - USE THE --TOKEN OPTION

kubectl 命令允许你使用--token 选项传入一个 token。只需将 idtoken 粘贴到这个选项中:

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 Token Authentication

Webhook authentication is a hook for verifying bearer tokens

    • -authentication-token-webhook-config-file 配置文件描述了如何访问远程 webhook 服务
    • -authentication-token-webhook-cache-ttl 缓存认证时间。默认 2 分钟

配置文件使用 kubeconfig file format。在文件中,“users”指的是 API 服务器的 webhook,“clusters”指的是远程服务。示例如下:

# 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. Must use 'https'.

# 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-sever
  name: webhook

当客户端使用如上所述的 bearer token 与 API 服务器进行认证时,认证 webhook 通过一个包含 token 的 review 对象来查询远程服务。k8s 不会对缺少这样的头的请求提出质疑。( k8s will not challenge a request that lacks such a header.)??

注意 webhook API 对象与其他 k8s API 对象一样受版本兼容性规则的约束,选择需谨慎。

请求主体采用的格式:

{
  "apiVersion": "authentication.k8s.io/v1beta1",
  "kind": "TokenReview",
  "spec": {
    "token": "(BEARERTOKEN)"
  }
}

远程服务预计将填写请求的 TokenAccessReviewStatus 字段,以示登录成功。 响应主体体的“spec”字段被忽略。 bearer token 认证成功将返回:

{
  "apiVersion": "authentication.k8s.io/v1beta1",
  "kind": "TokenReview",
  "status": {
    "authenticated": true,
    "user": {
      "username": "janedoe@example.com",
      "uid": "42",
      "groups": [
        "developers",
        "qa"
      ],
      "extra": {
        "extrafield1": [
          "extravalue1",
          "extravalue2"
        ]
      }
    }
  }
}

不成功的请求将返回:

{
  "apiVersion": "authentication.k8s.io/v1beta1",
  "kind": "TokenReview",
  "status": {
    "authenticated": false
  }
}

HTTP status codes can be used to supply additional error context

# Authenticating Proxy

API 服务器可以根据 header values 配置来识别 users,如 X-Remote-User。

  • –requestheader-username-headers 必选,不区分大小写。按顺序检查 Header names 用于识别用户身份,第一包含 Value 的 Header 用于 username。
  • –requestheader-group-headers 1.6+以上,可选,不区分大小写。 建议使用“X-Remote-Group”。按顺序检查 Header Names 用于识别 user’s groups。Header 中指定的所有 Value 均用作 Group Names。
  • –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.1X-Remote-User: fidoX-Remote-Group: dogsX-Remote-Group: dachshundsX-Remote-Extra-Scopes: openidX-Remote-Extra-Scopes: profile

将会产生的用户信息:

name: fidogroups:- dogs- dachshundsextra:  scopes:  - openid  - profile

为了防止 header 欺骗,Authenticating Proxy 需要在检查请求 header 之前,向 API 服务器提交有效的客户端证书,以针对指定的 CA 进行验证。

–requestheader-client-ca-file 必选,PEM-encoded 的证书包。在为用户名检查请求 header 之前,必须根据指定文件中的证书颁发机构呈现和验证有效的客户端证书。

–requestheader-allowed-names 可选,(common names)通用名称(cn)List。如果设置,则在检查请求 header 用户名之前,必须提交 List 中指定(common names)通用名中有效的客户端证书。

# Keystone Password

Keystone 认证通过--experimental-keystone-url=<AuthURL> 在启动期间将选项传递给 API 服务器来启用。该插件已实现,plugin/pkg/auth/authenticator/password/keystone/keystone.go 目前使用 basic auth 来验证用户名和密码。

如果为 Keystone 服务器配置了自签名证书,则需要--experimental-keystone-ca-file=SOMEFILE 在启动 k8s API 服务器时设置该选项。设置完成后 Keystone 服务器的证书将由其中一个 authorities 进行验证 experimental-keystone-ca-file。否则将由主机的根证书颁发机构验证。

有关如何使用 keystone 管理项目和 users 的详细信息,请参阅 Keystone 文档。需要注意此插件目前处于测试开发阶段。

Last Updated: 4/15/2023, 8:33:17 PM