通过 kube-apiserver 进行身份验证和授权来访问 Kubernetes 集群

通过 kube-apiserver 进行身份验证和授权来访问 Kubernetes 集群,Kubernetes 提供了以下几种主要的身份验证方式:

方式 描述 工作原理 使用场景
使用私钥证书 这种方式使用 X.509 证书进行身份验证。客户端提供一个由集群内部的证书颁发机构(CA)签名的客户端证书 当客户端尝试连接到 kube-apiserver 时,它会提供这个证书。kube-apiserver 会验证证书的有效性和签名。如果证书有效,请求会被允许 通常用于节点、管理员或其他高级用户的身份验证
使用 ServiceAccount Token ServiceAccount 是 Kubernetes 中的一个对象,它可以为 Pod 提供身份验证信息 当 ServiceAccount 被创建时,一个与之关联的 JWT(JSON Web Token)会被生成并存储在 Secrets 中。这个 Token 会被自动挂载到使用该 ServiceAccount 的 Pod 内。Pod 内的进程可以使用这个 Token 与 kube-apiserver 进行身份验证 主要用于 Pod 的身份验证,让 Pod 可以与集群内的其他服务进行交互
使用 kubeconfig kubeconfig 文件是一个配置文件,包含了访问 Kubernetes 集群所需的所有信息 kubeconfig 文件中可以包含多个上下文(context),每个上下文都与一个用户、集群和命名空间关联。文件中还可以包含用户的身份验证信息,如证书、Token 或其他凭证。当使用 kubectl 或其他客户端工具时,这些工具会读取 kubeconfig 文件并使用其中的信息与 kube-apiserver 进行交互 主要用于开发者或管理员从本地机器访问 Kubernetes 集群
使用 OpenID Connect (OIDC) OpenID Connect 是一个基于 OAuth 2.0 的身份层,它允许客户端验证用户身份基于认证返回的 JWT (JSON Web Token) 当 kube-apiserver 配置为使用 OIDC 时,它会与 OIDC 提供者交互来验证传入的 JWT。验证成功后,kube-apiserver 会从 JWT 中提取用户信息 适用于那些已经使用 OIDC 提供者(如 Google Identity、Auth0、Keycloak 等)的组织
使用Webhook Token Webhook Token 身份验证允许 kube-apiserver 使用外部服务进行身份验证 当接收到身份验证请求时,kube-apiserver 会将 Token 发送到一个外部 HTTP(S) 服务。这个外部服务会决定 Token 是否有效,并返回相应的用户信息 适用于那些希望集成自定义身份验证服务或已有的身份验证系统的组织
使用Auth Proxy 在这种模式下,一个反向代理或负载均衡器在 kube-apiserver 前面处理身份验证 反向代理会处理所有传入的请求,进行身份验证,并在请求头中设置已验证的用户名和可选的用户组。然后,请求会被转发到 kube-apiserver。kube-apiserver 会从请求头中提取用户信息,而不是直接处理身份验证 适用于那些已经有一个前端代理处理身份验证的组织,或希望在 Kubernetes 外部进行身份验证的组织

每种身份验证方式都有其特定的配置需求和安全性考虑。在选择和配置身份验证方式时,应确保满足组织的安全和运营需求。

一、使用私钥证书

一般来说,使用kubeadm安装的集群,它的证书路径在master节点的/etc/kubernetes/pki 目录下。

$ ls /etc/kubernetes/pki/
apiserver.crt              apiserver.key                 ca.crt  front-proxy-ca.crt      front-proxy-client.key
apiserver-etcd-client.crt  apiserver-kubelet-client.crt  ca.key  front-proxy-ca.key      sa.key
apiserver-etcd-client.key  apiserver-kubelet-client.key  etcd    front-proxy-client.crt  sa.pub

使用如下命令进行访问:

$ curl --cacert ca.crt --cert apiserver.crt --key apiserver.key https://$kube-apiserver/api

其中kube-apiserver是集群apiserver公网访问地址。

示例请求与返回如下:

$ curl --cacert ca.crt --cert apiserver.crt --key apiserver.key https://x.x.x.x:6443/api
{
  "kind": "APIVersions",
  "versions": [
    "v1"
  ],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "x.x.x.x:6443"
    },
    {
      "clientCIDR": "10.96.0.0/12",
      "serverAddress": "10.96.0.1:443"
    }
  ]
}

在某种情况下,可能会遇见如下返回

{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {

  },
  "status": "Failure",
  "message": "forbidden: User \"system:anonymous\" cannot get path \"/api\"",
  "reason": "Forbidden",
  "details": {

  },
  "code": 403
}

这说明集群不允许匿名用户访问,给匿名用户授权即可。创建一个集群角色绑定,把匿名用户绑定到有一定权限的集群角色上。以下示例创建一个名为test:anonymous的clusterrolebinding,把匿名用户system:anonymous赋予集群管理员角色。之后,可以正常访问了。

$ kubectl create clusterrolebinding test:anonymous --clusterrole=cluster-admin --user=system:anonymous
clusterrolebinding.rbac.authorization.k8s.io/test:anonymous created

$ curl --cacert ca.crt --cert apiserver.crt --key apiserver.key https://10.104.117.233:6443/api
{
  "kind": "APIVersions",
  "versions": [
    "v1"
  ],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "10.104.117.233:6443"
    },
    {
      "clientCIDR": "10.96.0.0/12",
      "serverAddress": "10.96.0.1:443"
    }
  ]
}

注意:使用匿名用户授权一般用于测试环境,在生产环境不建议使用。因为处理不当,将导致集群资源安全问题。这时,我们需要使用第二种方式,给用户创建一个真实的用户角色绑定。

二、使用ServiceAccount Token

serviceaccount的权限由集群中对应的rolebinding决定。这在控制多租户访问权限用得比较多,用于赋予每个租户不同权限。

1、选择对应权限的ServiceAccount来获取token

在此,选择使用的SA是:kubernetes-dashboardServiceAccount

$ kubectl -n kubernetes-dashboard get sa kubernetes-dashboard -oyaml
apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","kind":"ServiceAccount","metadata":{"annotations":{},"labels":{"k8s-app":"kubernetes-dashboard"},"name":"kubernetes-dashboard","namespace":"kubernetes-dashboard"}}
  creationTimestamp: "2022-03-20T02:27:58Z"
  labels:
    k8s-app: kubernetes-dashboard
  name: kubernetes-dashboard
  namespace: kubernetes-dashboard
  resourceVersion: "12710746"
  selfLink: /api/v1/namespaces/kubernetes-dashboard/serviceaccounts/kubernetes-dashboard
  uid: 91879529-1fa7-4062-b12b-3989dd1b08a8
secrets:
- name: kubernetes-dashboard-token-t554j

2、查看对应的clusterrolebinding

$ kubectl -n kubernetes-dashboard get clusterrolebinding kubernetes-dashboard -oyaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"rbac.authorization.k8s.io/v1","kind":"ClusterRoleBinding","metadata":{"annotations":{},"name":"kubernetes-dashboard"},"roleRef":{"apiGroup":"rbac.authorization.k8s.io","kind":"ClusterRole","name":"kubernetes-dashboard"},"subjects":[{"kind":"ServiceAccount","name":"kubernetes-dashboard","namespace":"kubernetes-dashboard"}]}
  creationTimestamp: "2022-03-20T02:28:27Z"
  name: kubernetes-dashboard
  resourceVersion: "12710799"
  selfLink: /apis/rbac.authorization.k8s.io/v1/clusterrolebindings/kubernetes-dashboard
  uid: adf4cfbb-4b25-4bd7-8df3-5e0681a7907e
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: kubernetes-dashboard
subjects:
- kind: ServiceAccount
  name: kubernetes-dashboard
  namespace: kubernetes-dashboard

3、查看kubernetes-dashboard sa绑定的clusterrole及其权限

$ kubectl -n kubernetes-dashboard get clusterrole kubernetes-dashboard -oyaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"rbac.authorization.k8s.io/v1","kind":"ClusterRole","metadata":{"annotations":{},"labels":{"k8s-app":"kubernetes-dashboard"},"name":"kubernetes-dashboard"},"rules":[{"apiGroups":["metrics.k8s.io"],"resources":["pods","nodes"],"verbs":["get","list","watch"]},{"apiGroups":[""],"resources":["namespaces"],"verbs":["get","list","watch"]}]}
  creationTimestamp: "2022-03-20T02:28:27Z"
  labels:
    k8s-app: kubernetes-dashboard
  name: kubernetes-dashboard
  resourceVersion: "12711363"
  selfLink: /apis/rbac.authorization.k8s.io/v1/clusterroles/kubernetes-dashboard
  uid: 695e27eb-1aa6-4e5c-9890-28c0fd4195fd
rules:
- apiGroups:
  - metrics.k8s.io
  resources:
  - pods
  - nodes
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - ""
  resources:
  - namespaces
  verbs:
  - get
  - list
  - watch

4、获取对应sa的secret从中获取token。并进行base64解码

$ kubectl -n kubernetes-dashboard get secret kubernetes-dashboard-token-t554j -o jsonpath={".data.token"} | base64 -d
eyJhb..省略..jrw

5、使用token访问集群Apiserver

curl -k -H 'Authorization: Bearer eyJhb..省略..jrw' https://x.x.x.x:6443/api
{
  "kind": "APIVersions",
  "versions": [
    "v1"
  ],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "x.x.x.x:6443"
    },
    {
      "clientCIDR": "10.96.0.0/12",
      "serverAddress": "10.96.0.1:443"
    }
  ]
}

此外,还可以在clusterrole中限制某类资源中的特定资源访问。如下所示:

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  labels:
    k8s-app: kubernetes-dashboard
  name: kubernetes-dashboard
rules:
# Allow Metrics Scraper to get metrics from the Metrics server
- apiGroups: ["metrics.k8s.io"]
  resources: ["pods", "nodes"]
  verbs: ["get", "list", "watch"]
- apiGroups: [""]
  resources: ["namespaces"]
  resourceNames: ["kubernetes-dashboard"]
  verbs: ["get"]

上述clusterrole限制绑定到clusterrole上的用户只能获取到kubernetes-dashboard名称空间以及其下的资源(当然还需要增加具体资源的规则)。这在多租户共享集群是很重要的实践。

三、使用kubeconfig

kubeconfig 文件是一个配置文件,它包含了访问 Kubernetes 集群所需的所有信息,包括 API server 的地址、证书、用户名和命名空间等。kubectl 命令行工具默认使用 ~/.kube/config 文件,但也可以通过 –kubeconfig 选项指定其他位置。

1、使用证书访问

在.kube/config找到kubeconfig文件,直接使用kubeconfig访问。如果没对kubeconfig修改,默认可以获取到集群中所有资源。如自建集群kubeconfig证书含有用户访问字段如下:

users:
- name: kubernetes-admin
  user:
    client-certificate-data: <client certificate data>
    client-key-data: <client key data>

2、使用token访问

重新创建一个kubeconfig文件,在config文件末尾加上一个token字段,值为第一种或者第二种方式中获取到的token

users:
- name: kubernetes-admin
  user:
    token: <your token>

便于验证,在这里仅使用kubectl做验证。

$ kubectl --kubeconfig=./admin-kubeconfig.yaml get ns
Error from server (Forbidden): namespaces is forbidden: User "system:serviceaccount:kubernetes-dashboard:kubernetes-dashboard" cannot list resource "namespaces" in API group "" at the cluster scope

$ kubectl --kubeconfig=./admin-kubeconfig.yaml get ns kubernetes-dashboard
NAME                   STATUS   AGE
kubernetes-dashboard   Active   4h24m

在第一个命令获取不到所有namespaces, 是因为在clusterrole中限制了用户只可以获取kubernetes-dashboard名称空间。如果我们单独获取名称空间,即可以获取到。

四、使用kOpenID Connect (OIDC)

OpenID Connect (OIDC) 是一个身份验证协议,它在 OAuth 2.0 协议的基础上增加了一个身份层。OIDC 允许客户端验证用户的身份,并获取基本的用户信息。它是为现代应用程序和服务设计的,特别是那些需要知道用户是谁的应用程序。

OIDC 的一些关键概念和特点:

  • ID Token:这是 OIDC 的核心。ID Token 是一个 JSON Web Token (JWT),它包含关于用户的声明(claims)。这些声明提供了关于用户的基本信息,如 sub(用户的唯一标识符)、name、email 等。
  • UserInfo Endpoint:这是一个可选的 OIDC 终端,允许客户端使用访问令牌(access token)来获取更多关于用户的信息。
  • Discovery:OIDC 提供了一个发现机制,允许客户端自动获取 OIDC 提供者的配置。这通常通过访问 /.well-known/openid-configuration 终端实现。
  • Flows:OIDC 支持多种流程,如授权码流程、隐式流程、混合流程等。这些流程定义了如何通过 OIDC 获取 ID Token 和访问令牌。
  • Client Registration:在使用 OIDC 之前,客户端通常需要在 OIDC 提供者处注册。注册后,客户端将获得一个客户端 ID 和可能的客户端密钥。

在 Kubernetes 中使用 OIDC 进行身份验证的简化流程如下:
(1)Kubernetes API Server 被配置为信任特定的 OIDC 提供者。
(2)用户首先通过 OIDC 提供者进行身份验证,并获得一个 ID Token。
(3)用户使用此 ID Token 作为凭证来访问 Kubernetes。
(4)Kubernetes API Server 验证 ID Token 的签名和有效性。
(5)如果 ID Token 有效,API Server 从 Token 中提取用户信息,并根据 Kubernetes 的 RBAC 策略进行授权。

为了在 Kubernetes 中使用 OIDC,需要在 kube-apiserver 中设置一些参数,如下所示:

kube-apiserver \
  --oidc-issuer-url=https://[YOUR_IDP_URL] \  #OIDC 提供者的 URL
  --oidc-client-id=[YOUR_CLIENT_ID]           #在 OIDC 提供者处注册的客户端 ID

在 kubeconfig 中为用户配置 OIDC:

users:
- name: oidc-user
  user:
    auth-provider:
      name: oidc
      config:
        idp-issuer-url: https://[YOUR_IDP_URL]
        client-id: [YOUR_CLIENT_ID]
        # ... 其他 OIDC 配置

OIDC 提供了一个标准化的方式来集成多种身份提供者,如 Google、Azure AD、Keycloak 等,这使得在 Kubernetes 环境中实现单点登录和跨多个平台的身份验证变得更加容易。

五、使用Webhook Token

Webhook Token 身份验证是 Kubernetes 提供的一种扩展身份验证机制,允许外部服务对 API 请求进行身份验证。

Webhook Token 身份验证的关键概念如下:

  • 外部服务:Webhook 身份验证依赖于一个外部的 HTTP(S) 服务来确定一个给定的 Token 是否有效,以及它代表的是哪个用户。这意味着可以与现有的身份验证系统集成,或创建一个特定的服务来满足的身份验证需求。
  • 身份验证请求:当 kube-apiserver 收到一个 API 请求时,如果该请求包含一个 Token,且 apiserver 配置为使用 Webhook Token 身份验证,它会将这个 Token 发送到外部的 Webhook 服务。请求的格式是一个简单的 JSON 结构。
  • 身份验证响应:Webhook 服务需要回应一个 JSON 消息,告诉 kube-apiserver 这个 Token 是否有效,如果有效,它代表的是哪个用户,以及用户属于哪些组。
  • 安全性:由于 Webhook 请求可能包含敏感信息,通常建议使用 HTTPS 来确保请求的安全性。

在 Kubernetes 中使用 Webhook Token 身份验证的简化流程如下:
(1)用户使用 Token 发起一个到 Kubernetes API 的请求。
(2)kube-apiserver 将这个 Token 发送到外部的 Webhook 服务进行验证。
(3)Webhook 服务检查 Token 的有效性,并返回关于用户的信息。
(4)kube-apiserver 根据 Webhook 的回应决定是否接受这个请求。
为了在 Kubernetes 中使用 Webhook Token 身份验证,需要:

在 kube-apiserver 中设置 –authentication-token-webhook-config-file 参数,指向一个包含 Webhook 服务配置的文件。如下所示:

kube-apiserver \
  --authentication-token-webhook-config-file=/path/to/webhook-config.yaml

该配置文件通常会包含 Webhook 服务的 URL、客户端证书和其他相关设置。下面为 webhook-config.yaml 文件的一个sample:

apiVersion: v1
kind: Config
clusters:
- name: local
  cluster:
    server: https://[YOUR_WEBHOOK_URL]
    certificate-authority: /path/to/ca.pem

Webhook Token机制的一个主要优势是它提供了极大的灵活性,允许 Kubernetes 集成到各种各样的身份验证环境中,无论是企业的 LDAP/SSO 系统,还是云提供商的 IAM 平台。

六、使用Auth Proxy

Auth Proxy 是 Kubernetes 中的一种身份验证策略,它依赖于一个前置的反向代理来处理身份验证,并将已验证的用户信息传递给 kube-apiserver。

以下是 Auth Proxy 身份验证的关键概念和特点:
– 反向代理:在这种设置中,所有到 kube-apiserver 的请求首先经过一个反向代理。这个代理负责处理身份验证,例如与 LDAP、OAuth2、SAML 或其他身份验证系统集成。
– 传递用户信息:一旦用户被代理验证,代理会在请求头中设置已验证的用户名和可选的用户组信息,然后将请求转发到 kube-apiserver。
– 信任边界:kube-apiserver 必须被配置为信任来自代理的请求头。这意味着网络配置和安全策略必须确保只有代理可以直接与 kube-apiserver 通信。
– 请求头:常见的请求头包括 X-Remote-User(表示已验证的用户名)和 X-Remote-Group(表示用户所属的组)。但这些头部名称可以在 kube-apiserver 的配置中自定义。

使用 Auth Proxy 进行身份验证的简化流程如下:
(1)用户发起一个到 Kubernetes API 的请求。
(2)反向代理捕获此请求并触发身份验证流程。
(3)一旦用户被验证,代理在请求头中设置用户信息。
(4)代理将请求转发到 kube-apiserver。
(5)kube-apiserver 从请求头中提取用户信息,并根据 RBAC 策略进行授权。

为了在 Kubernetes 中使用 Auth Proxy 身份验证,需要:
(1)设置并配置一个反向代理,如 Nginx、HAProxy 或 Traefik,使其能够处理身份验证并设置相应的请求头。假设使用了一个反向代理(如 nginx),配置它以添加验证的用户信息到请求头:

location / {
    proxy_set_header X-Remote-User remote_user;
    proxy_set_header X-Remote-Groupremote_user_group;
    proxy_pass https://kube-apiserver;
}

(2)配置 kube-apiserver 以接受和信任来自代理的请求头。这通常涉及设置如 –requestheader-allowed-names、–requestheader-extra-headers-prefix、–requestheader-group-headers 和 –requestheader-username-headers 等参数。

在 kube-apiserver 中配置来信任这些头部:

kube-apiserver \
  --requestheader-allowed-names="*" \
  --requestheader-extra-headers-prefix=X-Remote- \
  --requestheader-group-headers=X-Remote-Group \
  --requestheader-username-headers=X-Remote-User

Auth Proxy 身份验证的一个主要优势是它允许 Kubernetes 集成到现有的身份验证解决方案中,而无需对 kube-apiserver 进行大的修改。但它也引入了额外的复杂性,因为需要维护和保护反向代理。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注