调用k8s API

如题所述

第1个回答  2022-06-13

如上图所示,用户(User和Service Account)在调用API时会经过三个步骤:认证、鉴权和准入控制。

如上图步骤 1 所示,建立 TLS 后, HTTP 请求将进入认证(Authentication)步骤。 集群创建脚本或者集群管理员配置 API 服务器,使之运行一个或多个身份认证组件。

认证步骤输入的是整个 HTTP 请求,但是组件通常只检查头部和客户端证书。

认证模块包含客户端证书、密码、普通令牌、引导令牌和 JSON Web 令牌(JWT,用于服务账户)。

可以指定多个认证模块,在这种情况下,服务器依次尝试每个验证模块,直到其中一个成功。

如果请求认证不通过,服务器将以 HTTP 状态码 401 拒绝该请求。 反之,该用户被认证为特定的 username ,并且该用户名可用于后续步骤以在其决策中使用。 部分验证器还提供用户的组成员身份,其他则不提供。

如上图的步骤 2 所示,将请求验证为来自特定的用户后,请求必须被鉴权。

请求必须包含请求者的用户名、请求的行为以及受该操作影响的对象。 如果现有策略声明用户有权完成请求的操作,那么该请求被鉴权通过。

Kubernetes 支持多种鉴权模块,例如 ABAC 模式、RBAC 模式和 Webhook 模式等。 管理员创建集群时,他们配置应在 API 服务器中使用的鉴权模块。 如果配置了多个鉴权模块,则 Kubernetes 会检查每个模块,任意一个模块鉴权该请求,请求即可继续; 如果所有模块拒绝了该请求,请求将会被拒绝(HTTP 状态码 403)。

准入控制模块是可以修改或拒绝请求的软件模块。 除鉴权模块可用的属性外,准入控制模块还可以访问正在创建或修改的对象的内容。

准入控制器对创建、修改、删除或(通过代理)连接对象的请求进行操作。 准入控制器不会对仅读取对象的请求起作用 。 有多个准入控制器被配置时,服务器将依次调用它们。

这一操作如上图的步骤 3 所示。

与身份认证和鉴权模块不同,如果任何准入控制器模块拒绝某请求,则该请求将立即被拒绝。

除了拒绝对象之外,准入控制器还可以为字段设置复杂的默认值。

请求通过所有准入控制器后,将使用检验例程检查对应的 API 对象,然后将其写入对象存储(如步骤 4 所示)。

所有 Kubernetes 集群都有两类用户:普通用户和由 Kubernetes 管理的服务账号。

Kubernetes 并不包含用来代表普通用户账号的对象 。 普通用户的信息无法通过 API 调用添加到集群中。

但是Kubernetes 仍然认为能够提供由集群的证书机构签名的合法证书的用户是通过身份认证的用户。基于这样的配置,Kubernetes 使用证书中的 'subject' 的通用名称(Common Name)字段(例如,"/CN=bob")来确定用户名。然后,基于角色访问控制(RBAC)子系统会确定用户是否有权针对 某资源执行特定的操作。

与普通用户不同,服务账号是 Kubernetes API 所管理的用户。它们被绑定到特定的名字空间, 或者由 API 服务器自动创建,或者通过 API 调用创建。服务账号与一组以 Secret 保存的凭据相关,这些凭据会被挂载到 Pod 中,从而允许集群内的进程访问 Kubernetes API。

Kubernetes 使用身份认证插件利用客户端证书、持有者令牌(Bearer Token)、身份认证代理(Proxy) 或者 HTTP 基本认证机制来认证 API 请求的身份。HTTP 请求发给 API 服务器时, 插件会将以下属性关联到请求本身:

你可以同时启用多种身份认证方法,并且你通常会至少使用两种方法:

当集群中启用了多个身份认证模块时,第一个成功地对请求完成身份认证的模块会 直接做出评估决定。API 服务器并不保证身份认证模块的运行顺序。

要启动客户端证书身份认证,需要配置apiserver, 传入参数 --client-ca-file=SOMEFILE ,其中ca要与集群的ca一致。集群使用的ca可以通过以下命令查看

如果客户端的证书认证通过,则 subject 中的公共名称(Common Name)就被 作为请求的用户名。 自 Kubernetes 1.4 开始,客户端证书还可以通过证书的 organization 字段标明用户的组成员信息。 要包含用户的多个组成员信息,可以在证书种包含多个 organization 字段。

当 API server的命令行设置了 --token-auth-file=SOMEFILE 选项时,会从文件中读取持有者令牌。目前,令牌会长期有效,并且在 不重启 API server的情况下 无法更改令牌列表。

令牌文件是一个 CSV 文件,包含至少 3 个列:令牌、用户名和用户的 UID。 其余列被视为可选的组名。

服务账号(Service Account)是一种自动被启用的用户认证机制,使用经过签名的 持有者令牌来验证请求。

当服务账号创建后,k8s会自动生成对应的secret,存有可以用来认证的token。

上面的token就可以用来认证。

所有使用token进行认证的请求 ,都要加上 Authorization 的 HTTP请求头,其值格式为 Bearer TOKEN 。

例如:如果持有者令牌为 31ada4fd-adec-460c-809a-9e56ceb75269 ,则其 出现在 HTTP 头部时如下所示:

使用这种认证方式,apiserver将从请求头中获取用户信息,认证过程如图所示。

使用这种认证方式需要对apiserver进行如下配置:

例子:

假设apiserver配置如下

收到的请求头如下

会生成如下的用户信息用于鉴权

Role 总是用来在某个namespace内设置访问权限;在你创建 Role 时,你必须指定该 Role 所属的namespace。

与之相对,ClusterRole 则是一个集群作用域的资源。这两种资源的名字不同(Role 和 ClusterRole)是因为 Kubernetes 对象要么是namespace作用域的,要么是集群作用域的, 不可两者兼具。

ClusterRole 有若干用法。你可以用它来:

如果你希望在namespace内定义角色,应该使用 Role; 如果你希望定义集群范围的角色,应该使用 ClusterRole。

Role例子

ClusterRole例子

角色绑定(Role Binding)是将角色中定义的权限赋予一个或者一组用户。 它包含若干 主体 (用户、组或服务账户)的列表和对这些主体所获得的角色的引用。 RoleBinding 在指定的名字空间中执行授权,而 ClusterRoleBinding 在集群范围执行授权。

一个 RoleBinding 可以引用同一的名字空间中的任何 Role。 或者,一个 RoleBinding 可以引用某 ClusterRole 并将该 ClusterRole 绑定到 RoleBinding 所在的名字空间。 如果你希望将某 ClusterRole 绑定到集群中所有名字空间,你要使用 ClusterRoleBinding。

RoleBinding例子

ClusterRoleBinding例子

以上就是RBAC鉴权的一个概述,总结下就是Role定义了角色有哪些权限,RoleBinding则将角色和用户关联起来。这里的权限指的是对哪些资源有哪些操作的权限,用户则包括了普通用户和服务账号。

更多的关于RBAC的描述可以看下 官网 ,包括了k8s内置的一些默认角色。

这里测试使用的k8s集群是通过kubeadm安装的,这种安装方式会将k8s中的组件如kube-apiserver、kube-scheduler等作为static pod的形式运行。因此可以通过 kubectl get pod 命令来查看对应组件的配置。

从上面的结果可以看到使用的ca是 /etc/kubernetes/pki/ca.crt ,接下来开始生成客户端的证书。

这样就生成了一个用户名为test的客户端证书,接下来用这个证书去调k8s的api。

这代表我们认证通过了,但是鉴权没有通过。接下来给test授权。

先创建一个角色(role.yaml),拥有kube-system namespace下读取pod的权限,使用命令 kubectl apply -f role.yaml 来让他生效。

接下来创建一个RoleBinding,将刚刚创建的角色绑定到test上,并使用命令 kubectl apply -f bind.yaml 来让他生效

生效后再次使用curl调用api,一切正常。

先创建一个kube-system下的服务账号,命令如下

查看有没有生效

可以看到名称为test的sa(service account)已经创建了。接下来查看他对应的secret来获得token。

使用token调用k8s的api

从结果可以确认认证通过了,现在给sa授权,修改bind.yaml,修改后内容如下

使用命令 kubectl apply -f bind.yaml 生效后,再次调用api,这时就不会返回403了。

之前查看apiserver的配置时,看到以下配置,说明apiserver是开启了代理认证的,并且指明了使用的ca。

接下来根据ca生成代理所使用的证书,注意这个ca一般与X509所使用的ca不一致。

并且使用这种方法调用api时,不同的用户只需更改对应的请求头即可,证书不用变更,而如果使用X509的方法,则不同的用户需要使用不同的证书。

调用api

这里由于使用的请求头表明了当前的用户是test,并且之前已经给test授权过了,所以没有返回403。

接下来更改用户名为jojo,再次调用api

返回结果如下

这里 是官方的API参考文档,说明了有哪些api以及对应的使用方式。

相似回答
大家正在搜