官方文档地址: TLS Bootstrapping
TLS BootStrapping
在一个 kubernetes 集群中,工作节点上的组件(Kubelet 和 kube-proxy)需要与 kubernetes master 组件通信,尤其是 kube-apiserver。为了确保通信本身是私密的。不被干扰,并且确保集群的每个组件都在与另一个收信人的组件通信。我们强烈建议在节点上使用客户端 TLS 证书。
启动引导这些组件的正常过程,尤其是需要证书来与 kube-apiserver 安全通信的工作节点,可能会是一个具有挑战性的过程,因为这一过程不受 kubernetes 控制,需要不少额外的工作。这也使得初始化或者扩缩一个集群的操作变得具有挑战性。
为了简化 这一过程,从 1.4 版本开始,kubernetes 引入了一个证书请求和签名 API 以便简化此过程。
Kubelet 初始化过程
当 worker 节点启动时,Kubelet 执行以下操作:
- 寻找自己的 kubeconfig 文件;
- 检索 API 服务器的 URL 和凭据,通常是来自 kubeconfig 文件中的 TLS 秘钥和已签名证书;
- 尝试使用这些凭据来与 API 服务器通信。
假设 kube-apiserver 成功地认证了 Kubelet 的凭据数据,那么它会将 Kubelet 视为一个合法的节点,并开始将 Pods 分配给该节点。
注意,签名的过程依赖于:
- kubeconfig 中包含秘钥和本地主机的证书;
- 证书被 kube-apiserver 所信任的一个证书机构(CA)所签名;
启动引导初始化
在启动引导初始化过程中,会发生以下事情:
- Kubelet 启动;
- Kubelet 发现没有对应的 kubeconfig 文件;
- Kubelet 搜索并发现 bootstrap-kubeconfig 文件;
- Kubelet 读取该启动引导文件,从中获取 API 服务器的 URL,和用途有限的一个令牌(token);
- Kubelet 建立与 API 服务器的连接,使用上述令牌执行身份认证;
- Kubelet 现在用受限制的凭据来创建和取回证书签名请求(CSR);
- Kubelet 为自己创建一个 CSR,并将其 singerName 设置为
kubernetes.io/kube-apiserver-client-kubelet
; - CSR 被以如下两种方式之一批复:
- 如果配置了,kube-controller-manager 会自动批复该 CSR;
- 如果配置了,一个外部进程,或者是人,使用 kubernetes API 或者使用 kubectl 来批复该 CSR。
- Kubelet 所需要的证书被创建;
- 证书被发放给 Kubelet;
- Kubelet 取回该证书;
- kubelet 创建一个合适的 kubeconfig,其中包含秘钥和已签名的证书;
- Kubelet 开始正常工作;
- 可选的,如果配置了,Kubelet 在证书接近于过期的时候自动请求更新证书;
- 更新的证书被批复并发放;取决于配置,这一过程可能是自动的或者手动完成。
配置 TLS Bootstrapping
要配置 TLS Bootstrapping 以及可选的自动批复,需要配置以下选项:
- kube-apiserver
- kube-controller-manager
- Kubelet
- 集群内的资源 ClusterRoleBinding 以及可能需要的 ClusterRole
- 此外,需要有 kubernetes 证书机构(CA),这里使用自签证书颁发机构
证书机构(CA)
在没有 Bootstrapping 的情况下,我们需要将证书机构(CA)秘钥和证书。这些数据会被用来对 Kubelet 证书进行签名。将 CA 秘钥和证书发布到 master 节点是kubernetes 管理员的任务;
就这里而言,我们假定这些数据被发布到 master 节点上的 /usr/local/kubernetes/ssl/ca.pem
(证书) 和 /usr/local/kubernetes/ssl/ca-key.pem
(秘钥) 文件中。我们将这两个文件称作 “kubernetes CA 证书和秘钥”。所有 kubernetes 组件(kubelet,kube-apiserver,kube-controller-manager) 都使用这些凭据,并假设这里的秘钥和证书都是使用 PEM 进行编码的。
kube-apiserver 配置
启用 TLS Bootstrapping 对 kube-apiserver 有若干需求,如下:
- 能够识别对客户端证书进行签名的 CA;
- 向
system:bootstrappers
组验证 Bootstrapping Kubelet; - 授权 Bootstrapping Kubelet 创建证书签名请求(CSR);
识别客户端证书
对所有客户端证书的认证操作而言,这是很常见的。如果还没有设置,要为 kube-apiserver 命令添加 --client-ca-file=FILENAME
参数来启用客户端证书认证,在参数中引用一个包含用来签名的证书机构CA,例如:--client-ca-file=/usr/local/kubernetes/ssl/ca.pem
。
初始启动引导认证
为了让启动引导的 kubelet 能够连接到 kube-apiserver 并请求证书, 它必须首先在服务器上认证自身身份。你可以使用任何一种能够对 kubelet 执行身份认证的身份认证组件。尽管所有身份认证策略都可以用来对 kubelet 的初始启动凭据来执行认证,但出于容易准备的因素,建议使用如下两个身份认证组件:
- 启动引导令牌(Bootstrap Token)
- 令牌认证文件
启动引导令牌是一种对 kubelet 进行身份认证的方法,相对简单且容易管理, 且不需要在启动 kube-apiserver 时设置额外的标志。 启动引导令牌从 Kubernetes 1.12 开始是一种 Beta 功能特性。
无论选择哪种方法,这里的需求是 kubelet 能够被身份认证为某个具有如下权限的用户:
- 创建和读取 CSR,
- 在启用了自动批复时,能够在请求节点客户端证书时得到自动批复;
使用启动引导令牌执行身份认证的 kubelet 会被认证为 system:bootstrappers 组中的用户。这是使用启动引导令牌的一种标准方法。
启动引导令牌
启动引导令牌在 Kubernetes 集群中存储为 Secret 对象,被发放给各个 kubelet。 你可以在整个集群中使用同一个令牌,也可以为每个节点发放单独的令牌。
这一过程有两个方面:
- 基于令牌 ID、机密数据和范畴信息创建 Kubernetes Secret
- 将令牌发放给 kubelet
从 kubelet 的角度来看,所有令牌看起来都很像,没有特别的含义。但从 kube-apiserver 服务器的角度来看,启动引导令牌是很特殊的。根据其 type
、namespace
和 name
,kube-apiserver 能够将其认作特殊的令牌,并授予携带该令牌的任何人特殊的启动引导权限,换言之,如果将其视为 system:bootstrappers
组的成员。那么这就满足了 TLS 启动引导的基本需求。
如果你希望使用启动引导令牌,那么必须在 kube-apiserver 上添加下面的参数
1 | --enable-bootstrap-token-auth=true |
令牌认证文件
kube-apiserver 能够将令牌视作身份认证依据。这些令牌可以是任意数据,但出于安全考虑,应该使用安全随机数生成器生成的至少 128 位的数据。例如大多数 Linux 系统上的 /dev/urandom
。可以通过多种方式生成令牌,例如:
1 | head -c 16 /dev/urandom | od -An -t x | tr -d ' ' |
这将生成类似 5b8a610fa4e20c1203e237193939e46f
的 token。
令牌文件内容类似如下内容,其中前面三个值可以随意修改,使用引号括起来的组名称则只能是例子中给定的值:
1 | 5b8a610fa4e20c1203e237193939e46f,kubelet-bootstrap,10001,"system:bootstrappers" |
向 kube-apiserver 添加 --token-auth-file=FILENAME
标志(或许这要对 systemd 的单元文件作修改)以启用令牌文件。
授权 Kubelet 创建 CSR
现在 Bootstrapping 节点已作为 system:bootstrappers
组的一部分进行身份认证,它需要获得授权才能创建证书签名请求(CSR)并在完成后检索它。kubernetes 附带了一个 ClusterRole,它具有这些(并且只有这些)权限,system:node-bootstrapper
。
为此,你只需要创建一个 ClusterRoleBinding,将 system:bootstrappers
组绑定到集群角色 system:node-bootstrapper
。如下所示:
1 | # enable bootstrapping nodes to create CSR |
kube-controller-manager 配置
API 服务器从 kubelet 收到证书请求并对这些请求执行身份认证,但真正负责发放签名证书的是 kube-controller-manager。
kube-controller-manager 通过一个证书发放的控制回路来执行此操作。该操作的执行方式是使用磁盘上的文件用 cfssl 本地签名组件来完成。目前,所有颁发的证书都具有一年的有效期和一组迷人的秘钥用法。
为了让 kube-controller-manager 对证书签名,它需要:
- 能够访问你之前所创建并分发的 kubernetes CA 秘钥和证书;
- 启用 CSR 签名
访问 CA 秘钥和证书
如前所述,你需要创建一个 Kubernetes CA 密钥和证书,并将其发布到 master 节点。这些数据会被 kube-controller-manager 用来对 kubelet 证书进行签名。
由于这些被签名的证书反过来会被 kubelet 用来在 kube-apiserver 执行普通的 kubelet 身份认证,很重要的一点,为 kube-controller-manager 所提供的 CA 也被 kube-apiserver 所信任并用来执行身份认证。CA 密钥和证书是通过 kube-apiserver 的标志 --client-ca-file=FILENAME
(例如,--client-ca-file=/usr/local/kubernetes/ssl/ca.pem
), 来设定的,正如 kube-apiserver 配置节所述。
要将 Kubernetes CA 密钥和证书提供给 kube-controller-manager,可使用以下标志:
1 | --cluster-signing-cert-file="/etc/path/to/kubernetes/ca/ca.crt" \ |
例如:
1 | --cluster-signing-cert-file="/usr/local/kubernetes/ssl/ca.pem" \ |
所签名的证书的合法期限可以通过下面的标志来配置:
1 | --cluster-signing-duration |
批复
为了对 CSR 进行批复,你需要告诉 kube-controller-manager 批复这些 CSR 是可接受的。 这是通过将 RBAC 访问权限授予正确的组来实现的。
许可权限有两组:
- nodeclient: 如果节点是在为节点创建新的证书,则该节点还没有证书。那么该节点使用前文所列的令牌之一来执行身份认证,因此是组
system:bootstrappers
组的成员。 - selfnodeclient: 如果节点在对证书执行续期操作,则该节点已经拥有一个证书。节点持续使用现有的证书将自己认证为
system:nodes
组的成员。
要允许 kubelet 请求并接收新的证书,可以创建一个 ClusterRoleBinding 将启动引导节点所处的组 system:bootstrappers
绑定到为其赋予访问权限的 ClusterRole system:certificates.k8s.io:certificatesigningrequests:nodeclient
:
1 | # 批复 "system:bootstrappers" 组的所有 CSR |
要允许 kubelet 对其客户端证书执行续期操作,可以创建一个 ClusterRoleBinding 将正常工作的节点所处的组 system:nodes
绑定到为其授予访问许可的 ClusterRole system:certificates.k8s.io:certificatesigningrequests:selfnodeclient
:
1 | # 批复 "system:nodes" 组的 CSR 续约请求 |
作为 kube-controller-manager 的一部分的 csrapproving 控制器是自动被启用的。该控制器使用 SubjectAccessReview API 来确定某给定用户是否被授权请求 CSR,之后基于鉴权结果执行批复操作。为了避免与其它批复组件发生冲突,内置的批复组件不会显式地拒绝任何 CSRs。该组件仅是忽略未被授权的请求。控制器也会作为垃圾收集的一部分清除已过期的证书。
Kubelet 配置
最后,当主控节点被正确配置并且所有必要的身份认证和鉴权机制都就绪时, 我们可以配置 kubelet。
kubelet 需要以下配置来执行启动引导:
- 一个用来存储所生成的密钥和证书的路径(可选,可以使用默认配置);
- 一个用来指向尚不存在的 kubeconfig 文件的路径;kubelet 会将启动引导 配置文件放到这个位置;
- 一个指向 bootstrapping kubeconfig 文件的路径,用来提供 API 服务器的 URL 和启动引导凭据,例如,启动引导令牌;
- 可选的:轮换证书的指令
Bootstrapping kubeconfig 文件应该放在一个 kubelet 可访问的路径下,例如 /usr/local/kubernetes/cfg/bootstrap-kubelet.kubeconfig.
其格式与普通的 kubeconfig 文件完全相同,实际文件看起来可能如下所示:
1 | apiVersion: v1 |
需要额外注意的一些因素有:
- certificate-authority:指向 CA 文件的路径,用来对 kube-apiserver 所出示的服务器证书进行验证;
- server: 用来访问 kube-apiserver 的 URL;
- token:要使用的令牌
令牌的格式并不重要,只要它与 kube-apiserver 的期望匹配即可。在上面的例子中,我们使用的是启动引导令牌。如前所述,任何合法的身份认证方法都可以使用,不限于令牌。
因为 Bootstrapping kubeconfig 文件是一个标准的 kubeconfig 文件,你可以使用 kubectl 来生成该文件,要生成上面的示例文件:
1 | kubectl config set-cluster bootstrap \ |
要指示 kubelet 使用 bootstrapping kubeconfig 文件,可以使用下面的 kubelet 参数:
1 | --bootstrap-kubeconfig="/usr/local/kubernetes/cfg/bootstrap-kubelet.kubeconfig" --kubeconfig="/usr/local/kubernetes/cfg/kubelet.kubeconfig" |
在启动 kubelet 时,如果 --kubeconfig
标志所指定的文件并不存在,会使用通过标志 --bootstrap-kubeconfig
所指定的 Bootstrapping kubeconfig 配置来向 API 服务器请求客户端证书。在证书请求被批复并被 kubelet 收回时,一个引用所生成的密钥和所获得证书的 kubeconfig 文件会被写入到通过 --kubeconfig
所指定的文件路径下。证书和密钥文件会被放到 --cert-dir
所指定的目录中。
客户个服务证书
前文所述的内容都与 kubelet 客户端 证书相关,尤其是 kubelet 用来向 kube-apiserver 认证自身身份的证书。
kubelet 也可以使用 服务(Serving) 证书。kubelet 自身向外提供一个 HTTPS 末端,包含若干功能特性。要保证这些末端的安全性,kubelet 可以执行以下操作 之一:
- 使用通过
--tls-private-key-file
和--tls-cert-file
所设置的密钥和证书; - 如果没有提供密钥和证书,则创建自签名的密钥和证书;
- 通过 CSR API 从集群服务器请求服务证书.
TLS Bootstrapping 所提供的客户端证书默认被签名为仅用于 client auth(客户端认证),因此不能作为提供服务的证书,或者 server auth。不过,你可以启用服务器证书,至少可以部分地通过证书轮换来实现这点。
证书轮换
Kubernetes v1.8 和更高版本的 kubelet 实现了对客户端证书与/或服务证书进行轮换这一 Beta 特性。这一特性通过 kubelet 对应的 RotateKubeletClientCertificate
和 RotateKubeletServerCertificate
特性门控标志来控制,并且是默认启用的。
RotateKubeletClientCertificate 会导致 kubelet 在其现有凭据即将过期时通过创建新的 CSR 来轮换其客户端证书。要启用此功能特性,可将下面的标志传递给 kubelet:
1 | --rotate-certificates |
RotateKubeletServerCertificate 会让 kubelet 在启动引导其客户端凭据之后请求一个服务证书且对该服务证书执行轮换操作。要启用此功能特性,将下面的标志传递给 kubelet:
1 | --rotate-server-certificates |
kubectl 批复
CSRs 可以在控制器管理其内置的批复工作流之外被批复。
签名控制器并不会立即对所有证书请求执行签名操作。相反,它会等待这些请求被某具有适当特权的用户标记为 “Approved(已批准)”状态。这一流程有意允许由外部批复控制器来自动执行的批复,或者由控制器管理器内置的批复控制器来自动批复。不过,集群管理员也可以使用 kubectl 来手动批准证书请求。管理员可以通过 kubectl get csr
来列举所有的 CSR,使用 kubectl descsribe csr <name>
来描述某个 CSR 的细节。管理员可以使用 kubectl certificate approve <name>
来批准某 CSR,或者 kubectl certificate deny <name>
来拒绝某 CSR。