Kubernetes 认证机制学习

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
简介: # 引言 Kubernetes API Server 组件是 Kubernetes 具有网关性质的组件,它是 Kubernetes 集群资源操作的唯一入口,它通过 HTTP RESTful 的形式暴露服务,允许不同的用户、外部组件等访问它。我们使用 curl 命令去模拟访问 apisever 请求过程中,发生了什么。```bashiZj6ccqyhc7xduup9vl8mvZ :: ~ »

引言

Kubernetes API Server 组件是 Kubernetes 具有网关性质的组件,它是 Kubernetes 集群资源操作的唯一入口,它通过 HTTP RESTful 的形式暴露服务,允许不同的用户、外部组件等访问它。我们使用 curl 命令去模拟访问 apisever 请求过程中,发生了什么。

iZj6ccqyhc7xduup9vl8mvZ :: ~ » curl --cacert /root/.minikube/ca.crt https://192.168.49.2:8443/api/v1/namespaces -k
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {},
  "status": "Failure",
  "message": "namespaces is forbidden: User \"system:anonymous\" cannot list resource \"namespaces\" in API group \"\" at the cluster scope",
  "reason": "Forbidden",
  "details": {
    "kind": "namespaces"
  },
  "code": 403
}

从上面返回的结果可以看出:

  • apiserver 识别这次请求的用户为 system:anonymous
  • apiserver 禁止了该用户 list namespaces,并返回 403

所以通过上面的测试大致了解 apiserver 的工作机制:

  • 首先,它会识别请求的用户是谁(AuthN)
  • 然后,它会识别该用户具有什么样的权限(AuthZ)

而本文将重点介绍认证机制(AuthN)。

k8s认证机制的概述

在 TLS 连接建立之后,请求就进入了认证阶段。在这个阶段,一个或多个认证器模块(由管路员配置)会对请求进行认证,直到其中一个模块认证成功,之后请求的身份等信息会被记录下来,用于后续的授权和准入控制阶段。如果所有的认证器模块都没认证成功,这时候会返回给用户 401 Unauthorized 的错误码。

![[Pasted image 20230715103614.png]]

身份标识

Kubernetes API Server 支持下列两种类别的用户

  • Kubernetes 管理的用户:service account,由 Kubernetes 集群创建并被集群内的 Pod 使用。
  • 非 Kubernetes 管理的用户:一种是由集群管理员提供 static token 或者是 x509 证书;另一种是通过第三方如 Keystone、Google帐户和LDAP等外部身份提供商进行身份验证的用户。

除了上述用户之外,其他的都被视为匿名用户。

认证策略

Kubernetes 使用 x509 客户端证书,bearer tokens 或者认证代理去认证请求,认证成功之后会将以下属性与请求关联:

  • Username:标识用户的字符串(人类可读)。
  • UID:标识用户的字符串,比 Username 更具有唯一性。
  • Groups:一组字符串,每个字符串标识用户所在的组。如:system:authenticated,它包含了所有已经通过认证的用户。
  • Extra:map类型,存储了对后续的授权有用的额外信息。

X509 Client Certs

通过给 APIServer 配置 --client-ca-file=</path/to/file> 启动参数来使能客户端证书认证策略。该文件为 CA,用于 APIServer 校验客户端证书,如果一个客户端证书验证通过,那么证书主体的 common name 会作为用户名,证书的组织字段作为用户组。
创建私钥

openssl genrsa -out server.key 2048

生成证书请求文件
该证书标识了一个名为 foo123 的用户,归属组于 group1。

openssl req -new -key client.key -out client.csr -subj "/O=group1/CN=foo"

使用根证书签发证书请求
这里使用 minikube 的集群 ca 进行签发。

openssl x509 -req -days 365 -CA /var/lib/minikube/certs/ca.crt -CAkey /var/lib/minikube/certs/ca.key -CAserial /var/lib/minikube/certs/ca.srl -CAcreateserial -in client.csr -out client.crt

带着证书访问 apiserver 可以发现,认证已经通过了,识别出该证书对应的用户是 foo,返回 403 是因为该用户没有查看 namespaces 的权限。

root@minikube:~ curl https://localhost:8443/api/v1/namespaces --cacert /var/lib/minikube/certs/ca.crt  --cert ./client.crt --key ./client.key
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {},
  "status": "Failure",
  "message": "namespaces is forbidden: User \"foo\" cannot list resource \"namespaces\" in API group \"\" at the cluster scope",
  "reason": "Forbidden",
  "details": {
    "kind": "namespaces"
  },
  "code": 403
}

更对关于如何生成客户端证书的,可以参考这篇文档。 Managing Certificates 

Static Token File 

通过给 APIServer 配置 --token-auth-file=</path/to/file> 启动参数来使能 Bearer Tokens 认证策略。除非重启 APIServer 修改 Token,否则该 Token 不会过期。

iZj6ccqyhc7xduup9vl8mvZ :: ~ » curl --cacert /root/.minikube/ca.crt https://192.168.49.2:8443/api/v1/namespaces --header "Authorization: Bearer foo123"
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {},
  "status": "Failure",
  "message": "Unauthorized",
  "reason": "Unauthorized",
  "code": 401
}

上图是没有配置 Static Token 的结果,虽然 reason 是未授权的意思,但是返回 401 错误码的意思表示没通过认证。
接下来给 APIServer 配置 Static Token 文件,格式为

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

foo123,foo,123,"group1,group2"

再次访问,认证已经通过了,识别出该 Bearer Token 对应的用户是 foo,返回 403 是因为该用户没有查看 namespaces 的权限。

iZj6ccqyhc7xduup9vl8mvZ :: ~ » curl --cacert /root/.minikube/ca.crt https://192.168.49.2:8443/api/v1/namespaces --header "Authorization: Bearer foo123"
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {},
  "status": "Failure",
  "message": "namespaces is forbidden: User \"foo\" cannot list resource \"namespaces\" in API group \"\" at the cluster scope",
  "reason": "Forbidden",
  "details": {
    "kind": "namespaces"
  },
  "code": 403
}

Bootstrap Tokens

Bootstrap Token 是一种简单的 Bearer Token,
这种 Token 是在新建集群或者现有集群中加入节点时使用。一般是由 kubeadm 管理,以 secret 形式保存在 kube-system 命名空间,可以动态地创建删除,并且 kube-controller-manager 中 TokenCleaner 会在 Token 过期时删除。线下 IDC 集群 接入 VNode 可以使用这种安全性高的方式来进行接入。TLS 引导启动

Service Account Tokens

Service Account 在 Kubernetes 集群中有单独的 API,它被看作是集群内的用户。在每一个 namespace 下都有一个名为 default 的 service account,内容如下(1.24以上版本集群)。

apiVersion: v1
kind: ServiceAccount
metadata:
  creationTimestamp: "2023-06-24T10:09:33Z"
  name: default
  namespace: default
  resourceVersion: "326"
  uid: fd058d58-31f6-4e5f-9f7b-ebd41998b1c5

默认情况下,创建的 pod 如果没有特别指定 service account,都会将 namespace 下默认的 service account 以 volume 的形式挂载到容器的这个目录下 /var/run/secrets/kubernetes.io/serviceaccount/,该目录下有三个文件:token、ca.crt、namespace。其中 namespace 指定了 pod 所在的 namespace,ca .crt 用于验证 apiserver 服务端证书, token 则作为客户端身份验证,且为 jwt 形式,解码后如下:

{
  "aud": [
    "https://kubernetes.default.svc.cluster.local"
  ],
  "exp": 1721056710,
  "iat": 1689520710,
  "iss": "https://kubernetes.default.svc.cluster.local",
  "kubernetes.io": {
    "namespace": "default",
    "pod": {
      "name": "host-centos",
      "uid": "d8616e94-5b8a-49e3-994c-82c6fd588b66"
    },
    "serviceaccount": {
      "name": "default",
      "uid": "fd058d58-31f6-4e5f-9f7b-ebd41998b1c5"
    },
    "warnafter": 1689524317
  },
  "nbf": 1689520710,
  "sub": "system:serviceaccount:default:default"
}
  • sub(Subject):与令牌关联的 ServiceAccount 唯一标识符。
  • iss(Issuer):颁发令牌的实体,可通过 --service-account-issuer 启动参数进行配置。
  • aud(Audience):接受 JWT 的一方,接受方必须检查该字段以避免攻击。
  • exp(Expiration Time):token 的过期时间。
  • iat(Issuer At):token 被签发的时间。
  • nbf(Not Before):标识 token 在这个时间点之前都是无效的。
  • kubernetes.io(additional Field):额外的字段信息,包含了 service account、pod 的一些信息。

使用上述 Bearer Token 的形式进行访问,可以发现认证已经通过。

[root@host-centos /] TOKEN=`cat /var/run/secrets/kubernetes.io/serviceaccount/token`
[root@host-centos /] curl --header "Authorization: Bearer $TOKEN"  https://kubernetes/api/v1/namespaces --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {},
  "status": "Failure",
  "message": "namespaces is forbidden: User \"system:serviceaccount:default:default\" cannot list resource \"namespaces\" in API group \"\" at the cluster scope",
  "reason": "Forbidden",
  "details": {
    "kind": "namespaces"
  },
  "code": 403
}

假如你的 Pod 没有访问集群内资源信息的需求,那么你可以通过配置 Pod.Spec.AutomountServiceAccountToken 为 false 来取消 Service Account 的挂载。

Webhook Token Authentication

它是一个 hook 点,当请求经过该模块认证时,APIServer 会根据配置文件给远端的 Service 发送 TokenReview 对象用于身份验证,远端服务验证完成之后会把认证信息回填到 TokenReview 的 Status 字段。

通过给 APIServer 配置下列启动参数来使能 Webhook 认证。

  • --authentication-token-webhook-config-file:webhook 配置文件,复用 kubeconfig 配置,用来描述远端服务和自身客户端信息。
  • --authentication-token-webhook-cache-ttl:缓存身份验证决策的时间。默认为两分钟。
  • --authentication-token-webhook-version:确定是使用 authentication.k8s.io/v1beta1 还是 authentication.k8s.io/v1 TokenReview 对象,默认为v1beta1。

Webhook 配置文件

apiVersion: v1
kind: Config
clusters:
  - name: name-of-remote-authn-service
    cluster:
      certificate-authority: /path/to/ca.pem
      server: https: //authn.example.com/authenticate
users:
  - name: name-of-api-server
    user:
      client-certificate: /path/to/cert.pem 
      client-key: /path/to/key.pem
current-context: webhook
contexts:
- context:
    cluster: name-of-remote-authn-service
    user: name-of-api-server
  name: webhook

TokenReview Spec

{
  "apiVersion": "authentication.k8s.io/v1",
  "kind": "TokenReview",
  "spec": {
    "token": "014fbff9a07c...",
    "audiences": ["https://myserver.example.com", "https://myserver.internal.example.com"]
  }
}

TokenReview Status

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

Authenticating Proxy

APIServer 可以通过配置以下启动参数来识别请求中特定的 Headers 字段作为用户的信息。

  • --requestheader-username-headers:按顺序检查请求的 Headers 字段,第一个匹配的将作为用户标识。
  • --requestheader-group-headers:按顺序检查请求的 Headers 字段,所有匹配的都将作为用户的组。
  • --requestheader-extra-headers-prefix:按顺序检查请求的 Headers 字段,所有前缀匹配的字段都将作为用户身份的附加信息,将用于后续的授权模块。

假如 APIServer 的启动参数配置如下

--requestheader-username-headers=X-Remote-User
--requestheader-group-headers=X-Remote-Group
--requestheader-extra-headers-prefix=X-Remote-Extra-

那么当请求带着下列 Headers 来访问 APIServer 时

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

为了防止 header 欺骗,认证代理访问 apiserver 时必须提供证书,可以通过给 APIServer 配置启动参数 --requestheader-client-ca-file 用于校验认证代理的证书有效性,--requestheader-allowed-names 用于校验证书的 Common Name。

参考文档

https://kubernetes.io/docs/reference/access-authn-authz/authentication/

相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
相关文章
|
1月前
|
存储 Kubernetes 持续交付
k8s学习
【10月更文挑战第1天】
84 4
|
1月前
|
Kubernetes 应用服务中间件 nginx
k8s学习--YAML资源清单文件托管服务nginx
k8s学习--YAML资源清单文件托管服务nginx
k8s学习--YAML资源清单文件托管服务nginx
|
1月前
|
Kubernetes 监控 测试技术
k8s学习--基于Ingress-nginx实现灰度发布系统
k8s学习--基于Ingress-nginx实现灰度发布系统
105 2
k8s学习--基于Ingress-nginx实现灰度发布系统
|
1月前
|
Prometheus Kubernetes 监控
k8s学习--kubernetes服务自动伸缩之水平伸缩(pod副本伸缩)HPA详细解释与案例应用
k8s学习--kubernetes服务自动伸缩之水平伸缩(pod副本伸缩)HPA详细解释与案例应用
104 1
k8s学习--kubernetes服务自动伸缩之水平伸缩(pod副本伸缩)HPA详细解释与案例应用
|
1月前
|
存储 Kubernetes 调度
|
1月前
|
Kubernetes API 调度
k8s学习--pod的所有状态详解(图例展示)
k8s学习--pod的所有状态详解(图例展示)
141 1
|
1月前
|
Kubernetes JavaScript 前端开发
k8s学习--chart包开发(创建chart包)
k8s学习--chart包开发(创建chart包)
100 1
|
1月前
|
Kubernetes 应用服务中间件 nginx
k8s学习--Traffic Shifting 流量接入
k8s学习--Traffic Shifting 流量接入
|
1月前
|
Kubernetes 监控 数据可视化
k8s学习--使用argo-rollouts实现金丝雀发布
k8s学习--使用argo-rollouts实现金丝雀发布
|
1月前
|
Kubernetes 应用服务中间件 nginx
k8s学习--kubernetes服务自动伸缩之水平收缩(pod副本收缩)VPA策略应用案例
k8s学习--kubernetes服务自动伸缩之水平收缩(pod副本收缩)VPA策略应用案例