Kubernetes TLS bootstrapping 那点事
一、TLS bootstrapping 简介
当集群开启了 TLS 认证后,每个节点的 kubelet 组件都要使用由 apiserver 使用的 CA 签发的有效证书才能与 apiserver 通讯;此时如果节点多起来,为每个节点单独签署证书将是一件非常繁琐的事情;TLS bootstrapping 功能就是让 kubelet 先使用一个预定的低权限用户连接到 apiserver,然后向 apiserver 申请证书,kubelet 的证书由 apiserver 动态签署;在配合 RBAC 授权模型下的工作流程大致如下所示
二、TLS bootstrapping 相关术语
2.1、kubelet
kubelet 组件在工作时,采用主动的查询机制,即定期请求 apiserver 获取自己所应当处理的任务,如哪些 pod 分配到了自己身上,从而去处理这些任务;同时 kubelet 自己还会暴露出两个本身 api 的端口,用于将自己本身的私有 api 暴露出去,这两个端口分别是 10250 与 10255;对于 10250 端口,kubelet 会在其上采用 TLS 加密以提供适当的鉴权功能;对于 10255 端口,kubelet 会以只读形式暴露组件本身的私有 api,并且不做鉴权处理
总结一下,就是说 kubelet 上实际上有两个地方用到证书,一个是用于与 API server 通讯所用到的证书,另一个是 kubelet 的 10250 私有 api 端口需要用到的证书
2.2、CSR 请求类型
kubelet 发起的 CSR 请求都是由
kube-controller-manager
来做实际签署的,对于kube-controller-manager
来说,TLS bootstrapping 下 kubelet 发起的 CSR 请求大致分为以下两种
- nodeclient: kubelet 首次创建新证书以
O=system:bootstrappers
和CN=system:bootstrappers
形式发起的 CSR 请求 - selfnodeclient: kubelet client 更新自己的证书以
O=system:nodes
和CN=system:nodes
形式发起的 CSR 请求
大白话加自己测试得出的结果: nodeclient 类型的 CSR 仅在第一次启动时会产生,selfnodeclient 类型的 CSR 请求实际上就是 kubelet 更新自己作为 client 跟 apiserver 通讯时使用的证书产生的。
三、TLS bootstrapping 具体引导过程
3.1、Kubernetes TLS 与 RBAC 认证
在说具体的引导过程之前先谈一下 TLS 和 RBAC,因为这两个事不整明白下面的都不用谈;
- TLS 作用
众所周知 TLS 的作用就是对通讯加密,防止中间人窃听;同时如果证书不信任的话根本就无法与 apiserver 建立连接,更不用提有没有权限向 apiserver 请求指定内容
- RBAC 作用
当 TLS 解决了通讯问题后,那么权限问题就应由 RBAC 解决(可以使用其他权限模型,如 ABAC);RBAC 中规定了一个用户或者用户组(subject)具有请求哪些 api 的权限;在配合 TLS 加密的时候,实际上 apiserver 读取客户端证书的 CN 字段作为用户名,读取 O 字段作为用户组
从以上两点上可以总结出两点: 第一,想要与 apiserver 通讯就必须采用由 apiserver CA 签发的证书,这样才能形成信任关系,建立 TLS 连接;第二,可以通过证书的 CN、O 字段来提供 RBAC 所需的用户与用户组
3.2、kubelet 首次启动流程
看完上面的介绍,不知道有没有人想过,既然 TLS bootstrapping 功能是让 kubelet 组件去 apiserver 申请证书,然后用于连接 apiserver;那么第一次启动时没有证书如何连接 apiserver ?
- kubelet启动
- kubelet看到它没有kubeconfig文件
- kubelet搜索并查找bootstrap-kubeconfig文件
- kubelet读取它的bootstrap文件,检索API server的URL和一个低权限的“token”
- kubelet连接到API服务器,使用token进行身份验证
- kubelet现在具有创建和检索证书签名请求(CSR)的有限凭据
- kubelet为自己创建了一个CSR
- CSR通过以下两种方式之一获得批准:
- 如果已配置,kube-controller-manager将自动批准CSR
- 如果已配置,则外部流程(可能是人员)使用Kubernetes API或通过批准CSR kubectl
- 为kubelet创建证书
- 证书颁发给kubelet
- kubelet检索证书
- kubelet 使用密钥和签名证书创建一个正确的kubeconfig文件
- kubelet开始正常运作
- 可选:如果已配置,则当证书接近到期时,kubelet会自动请求更新证书
在有些用户首次启动时,可能与遇到 kubelet 报 401 无权访问 apiserver 的错误;kubelet 通过bootstrap.kubeconfig 中的预设用户 Token 声明了自己的身份,然后创建 CSR 请求;但是不要忘记这个“用户和组”在我们不处理的情况下他没任何权限的,包括创建 CSR 请求;所以需要如下命令创建一个 ClusterRoleBinding,将预设“用户”或“组”与内置的 ClusterRole system:node-bootstrapper 绑定到一起,使其能够发起 CSR 请求(以下是用“组”做绑定)。
1 |
|
1 |
|
1 |
|
3.3、手动签发证书
在 kubelet 首次启动后,如果用户 Token 没问题,并且 RBAC 也做了相应的设置,那么此时在集群内应该能看到 kubelet 发起的 CSR 请求
出现 CSR 请求后,可以使用 kubectl 手动签发(允许) kubelet 的证书
当成功签发证书后,目标节点的 kubelet 会将证书写入到 –cert-dir= 选项指定的目录中;注意此时如果不做其他设置应当生成四个文件
而 kubelet 与 apiserver 通讯所使用的证书为 kubelet-client.crt,剩下的 kubelet.crt 将会被用于 kubelet server(10250) 做鉴权使用;注意,此时 kubelet.crt 这个证书是个独立于 apiserver CA 的自签 CA,并且删除后 kubelet 组件会重新生成它
四、证书文件作用
- kubelet-client.crt
该文件在 kubelet 完成 TLS bootstrapping 后生成,此证书是由 controller manager 签署的,此后 kubelet 将会加载该证书,用于与 apiserver 建立 TLS 通讯,同时使用该证书的 CN 字段作为用户名,O 字段作为用户组向 apiserver 发起其他请求
- kubelet-client-current.pem
这是一个软连接文件,会在证书总有效期的 70%~90% 的时间内发起续期请求,请求被批准后会生成一个 kubelet-client-时间戳.pem;kubelet-client-current.pem 文件则始终软连接到最新的真实证书文件,除首次启动外,kubelet 一直会使用这个证书同 apiserver 通讯
- kubelet.crt
该文件在 kubelet 完成 TLS bootstrapping 后并且没有配置--rotate-server-certificates
时才会生成;这种情况下该文件为一个独立于 apiserver CA 的自签 CA 证书,有效期为 1 年;被用作 kubelet 10250 api 端口
- kubelet-server.crt
该文件在 kubelet 完成 TLS bootstrapping 后并且配置了--rotate-server-certificates
时才会生成;这种情况下该证书由 apiserver CA 签署,默认有效期同样是 1 年,被用作 kubelet 10250 api 端口鉴权
- kubelet-server-current.pem
同样是一个软连接文件,当 kubelet 配置了--rotate-server-certificates
选项后,会在证书总有效期的 70%~90% 的时间内发起续期请求,请求被批准后会生成一个 kubelet-server-时间戳.pem;kubelet-server-current.pem 文件则始终软连接到最新的真实证书文件,该文件将会一直被用于 kubelet 10250 api 端口鉴权
五、使用 Bootstrap Token
5.1、创建 Bootstrap Token
既然整个功能都时刻强调这个 Token,那么第一步肯定是生成一个 token,生成方式如下:
1 |
|
这个
47f392.d22d04e89a65eb22
就是生成的Bootstrap Token
,保存好 token,因为后续要用;关于这个 token 解释如下:
Token 必须满足[a-z0-9]{6}\.[a-z0-9]{16}
格式;以.
分割,前面的部分被称作 Token ID,Token ID 并不是 “机密信息”,它可以暴露出去;相对的后面的部分称为 Token Secret,它应该是保密的。
5.2、创建 Bootstrap Token Secret
对于 Kubernetes 来说 Bootstrap Token Secret 也仅仅是一个特殊的 Secret 而已;对于这个特殊的 Secret 样例 yaml 配置如下:
1 |
|
需要注意几点:
- 作为
Bootstrap Token Secret
的type
必须为bootstrap.kubernetes.io/token
,name 必须为bootstrap-token-<token id>
(Token ID 就是上一步创建的 Token 前一部分) usage-bootstrap-authentication
表示令牌可以用于 API 服务器的认证。认证器会以system:bootstrap:<Token ID>
认证。它被包含在system:bootstrappers
组中。 命名和组是故意受限制的,以防止用户在启动引导后再使用这些令牌。usage-bootstrap-signing
表示令牌应该被用于cluster-info
ConfigMap 的签名expiration
字段是可选的,如果设置则 Secret 到期后将由 Controller Manager 中的 tokencleaner 自动清理auth-extra-groups
是可选的,令牌的扩展认证组,组必须以system:bootstrappers:
开头。在1.16后可以设置system:bootstrappers:kubeadm:default-node-token
组,后面就不需要创建自动批准csr相关的操作。
最后使用
kubectl create -f bootstrap.secret.yaml
创建即可
5.3、创建 ClusterRoleBinding 将 ClusterRole 绑定到适当的用户组,以完成自动批准相关 CSR 请求
需要注意的是在使用 Bootstrap Token 进行引导时,Kubelet 组件使用 Token 发起的请求其用户名为
system:bootstrap:<token id>
,用户组为system:bootstrappers
;所以我们在创建 ClusterRoleBinding 时要绑定到这个用户或者组上;
- 允许 system:bootstrappers 组用户创建 CSR 请求
1 |
|
- 自动批准 system:bootstrappers 组用户 TLS bootstrapping 首次申请证书的 CSR 请求
1 |
|
- 自动批准 system:nodes 组用户更新 kubelet 自身与 apiserver 通讯证书的 CSR 请求
1 |
|
六、调整 Controller Manager
根据官方文档描述,Controller Manager 需要启用 tokencleaner 和 bootstrapsigner
1 |
|
七、生成 bootstrap.kubeconfig
前面所有步骤实际上都是在处理 Api Server、Controller Manager 这一块,为的就是 “老子启动后 TLS Bootstarpping 发证书申请你两个要立马允许,不能拒绝老子”;接下来就是比较重要的 bootstrap.kubeconfig 配置生成,这个 bootstrap.kubeconfig 是最终被 Kubelet 使用的,里面包含了相关的 Token,以帮助 Kubelet 在第一次通讯时能成功沟通 Api Server;生成方式如下:
7.1、设置 bootstrap , 创建 bootstrap 令牌(此步骤上面已执行,这里只是讲另外一种创建方法)
1 |
|
7.2、创建 bootstrap kubeconfig 文件
1 |
|
八、调整 Kubelet
Kubelet 启动参数需要做一些相应调整,以使其能正确的使用 Bootstartp Token,完整配置如下(与使用 token.csv 配置没什么变化,因为主要变更在 bootstrap.kubeconfig 中):
1 |
|
九、TLS bootstrapping 证书自动续期
9.1、开启自动续期
在 kubelet 启动时增加一个
--rotate-certificates
参数后,kubelet client
才会自动重载新证书。
9.2、证书过期问题
需要重复强调一个问题是: TLS bootstrapping 时的证书实际是由 kube-controller-manager 组件来签署的,也就是说证书有效期是 kube-controller-manager 组件控制的; kube-controller-manager 组件提供了一个
--experimental-cluster-signing-duration
参数来设置签署的证书有效时间;默认为8760h0m0s
,将其改为87600h0m0s
即 10 年后再进行TLS bootstrapping
签署证书即可。
扩展知识:
在文档中多次提到过 kubelet server 这个东西;kubelet server 指的应该是 kubelet 的 10250 端口;在上面我们仅仅提到了两种类型的CSR 请求,其实实际上还有一种CSR请求。这种请求是用来更新kubelet server证书(10250端口)
注意:出于安全原因,核心Kubernetes中实现的CSR批准控制器不批准节点服务证书。需要手动批准服务证书请求。(此句摘自官方)
在kubelet中开启
--rotate-server-certificates
参数后会生成kubelet-server.crt
证书,如果没开启此参数则仅生成kubelet-client.crt
证书。
1 |
|
1 |
|