一、什么是 TLS Bootstrapping

在一个 Kubernetes 集群中,工作节点上的组件(kubelet 和 kube-proxy)需要与 Kubernetes 控制平面组件通信,尤其是 kube-apiserver。 为了确保通信本身是私密的、不被干扰,并且确保集群的每个组件都在与另一个可信的组件通信, 强烈建议使用节点上的客户端 TLS 证书。

启动引导这些组件的正常过程,尤其是需要证书来与 kube-apiserver 安全通信的工作节点, 可能会是一个具有挑战性的过程,因为这一过程通常不受 Kubernetes 控制,需要不少额外工作。 这也使得初始化或者扩缩一个集群的操作变得具有挑战性。为了简化这一过程,从 1.4 版本开始,Kubernetes 引入了一个证书请求和签名 API。

Kubernetes 的 "bootstrapping" 过程通常涉及以下几个关键方面:

  • Master 节点初始化: 在 Kubernetes 集群中,Master 节点负责管理整个集群的控制平面。"Bootstrapping" 过程会初始化 Master 节点的各个组件,如 API Server、Controller Manager、Scheduler 等。这些组件的初始化需要确保它们能够相互通信并协同工作。
  • Node 节点初始化: Node 节点是集群中的工作节点,负责运行容器和应用程序。"Bootstrapping" 过程会初始化 Node 节点,确保它们能够连接到 Master 节点,注册自己的存在,并准备好运行容器的环境。
  • 证书和认证: Kubernetes 使用证书来确保集群中的通信安全性。"Bootstrapping" 过程涉及生成和分发证书,以及配置 Kubernetes 组件和节点来使用这些证书进行身份验证和安全通信。
  • 配置初始化: 集群配置文件的初始化是 "bootstrapping" 的一部分,这些配置文件包括 API Server、etcd 数据库、网络插件、Pod 网络范围等的配置。
  • 网络设置: 在 Kubernetes 中,Pod 和 Service 之间的网络通信是至关重要的。"Bootstrapping" 过程会确保网络插件的正确安装和配置,以及网络规则的初始化。
  • 持久化存储: 一些应用程序可能需要使用持久化存储。"Bootstrapping" 过程会确保持久化存储卷(如 PV 和 PVC)的初始化和配置。

二、为什么会出现 TLS Bootstrapping

TLS bootstrapping 的出现原因是为了以下几个目的:

  • 安全通信: Kubernetes 集群中的各个组件和节点之间需要进行安全的通信,以防止敏感数据在传输过程中被窃取或篡改。TLS 提供了一种加密通信的方式,确保数据在传输过程中是安全的。
  • 身份验证: 为了确保集群中的每个组件和节点都是合法的,TLS 证书用于身份验证。在 TLS bootstrapping 过程中,每个组件和节点都会生成证书和密钥,这些证书用于在集群内部进行身份验证,防止未经授权的访问。
  • 集群中的信任关系: 集群中的不同组件和节点需要互相信任,以便建立安全的通信连接。通过 TLS bootstrapping,集群中的证书可以被颁发和管理,建立起相互信任的关系。
  • 自动化: TLS bootstrapping 可以自动化证书生成、分发和更新的过程,使得在扩展或更新集群时能够快速地引入新的节点或组件。
  • 安全性增强: 通过使用 TLS,可以增强集群的安全性,减少潜在的漏洞和攻击面。

TLS bootstrapping 是 Kubernetes 集群中非常重要的一部分,用于建立安全的通信连接和身份验证,确保集群的安全性和稳定性。它通过自动化证书生成和分发,使得管理集群中的安全性变得更加容易。

三、Kubelet 启动过程

当工作节点启动时,kubelet 执行以下操作:

  1. 寻找自己的 kubeconfig 文件,如果是二进制安装的话,一般存放路径为:/etc/kubernetes/kubelet.kubeconfig。其中kubelet.kubeconfig文件是根据bootstrap-kubelet.kubeconfig 文件自动生成的
  2. 检索 API 服务器的 URL 和凭据,通常是来自 kubeconfig 文件中的 TLS 密钥和已签名证书
  3. 尝试使用这些凭据来与 API 服务器通信

如果想查看kubelet配置文件路径,具体如下

[root@k8s-node01 kubernetes]# cat /etc/systemd/system/kubelet.service.d/10-kubelet.conf
[Service]
Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.kubeconfig --kubeconfig=/etc/kubernetes/kubelet.kubeconfig"
Environment="KUBELET_SYSTEM_ARGS=--network-plugin=cni --cni-conf-dir=/etc/cni/net.d --cni-bin-dir=/opt/cni/bin --container-runtime=remote --runtime-request-timeout=15m --container-runtime-endpoint=unix:///run/containerd/containerd.sock --cgroup-driver=systemd"
Environment="KUBELET_CONFIG_ARGS=--config=/etc/kubernetes/kubelet-conf.yml"
Environment="KUBELET_EXTRA_ARGS=--node-labels=node.kubernetes.io/node='' "
ExecStart=
ExecStart=/usr/local/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_SYSTEM_ARGS $KUBELET_EXTRA_ARGS

当然,我们也可以查看集群的证书数据,具体如下

[root@k8s-node01 ~]# cd /etc/kubernetes
[root@k8s-node01 kubernetes]# more  kubelet.kubeconfig

Kubelet启动过程-1

将certificate-authority-data后面的数据使用base64进行解密,并保存到/root/kubeconfg-key

[root@k8s-node01 kubernetes]# echo '''
LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUQ1RENDQXN5Z0F3SUJBZ0lVQ3UvbjRHcHltSDNLOURuQnBnNWhRYys2OGNvd0RRWUpLb1pJaHZjTkFRRUwKQlFBd2R6RUxNQWtHQTFVRUJ
oTUNRMDR4RURBT0JnTlZCQWdUQjBKbGFXcHBibWN4RURBT0JnTlZCQWNUQjBKbAphV3BwYm1jeEV6QVJCZ05WQkFvVENrdDFZbVZ5Ym1WMFpYTXhHakFZQmdOVkJBc1RFVXQxWW1WeWJtVjBaWE10CmJXRnVkV0ZzTVJNd0VRWURWUVFERX
dwcmRXSmxjbTVsZEdWek1DQVhEVEl6TURNeE1URXdOVEF3TUZvWUR6SXgKTWpNd01qRTFNVEExTURBd1dqQjNNUXN3Q1FZRFZRUUdFd0pEVGpFUU1BNEdBMVVFQ0JNSFFtVnBhbWx1WnpFUQpNQTRHQTFVRUJ4TUhRbVZwYW1sdVp6RVRNQ
kVHQTFVRUNoTUtTM1ZpWlhKdVpYUmxjekVhTUJnR0ExVUVDeE1SClMzVmlaWEp1WlhSbGN5MXRZVzUxWVd3eEV6QVJCZ05WQkFNVENtdDFZbVZ5Ym1WMFpYTXdnZ0VpTUEwR0NTcUcKU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFR
Q1hiSUNrTDZhMHFzSVlMb1dNN1RadXVsMzRLZUJBdTkvcwpuV0V4Zkxtc0sweGx3L1BYTkM3L1N4MTdTQUFhQnBnc3JGeG5FeFp1MjRRTWw5MU1UQkZuYlB1SjBNdkJ2NFZRCkxwSjJtdWxvd2NYMFdHNmd0cS9mZndmQ0c4M1FlMnNVamV
LYmd5MHAyWGYwY0FpRzRYK0VPT2laangyNUt6bkgKN05CNWR3QXkxUDV1UGxXZ0crUE00cys2MHdXZHZhZmlJbm1ieGFiYVIvaDRUSTN0aWtocVMzYnZFWmZMWkszcgoxM296NTRzaThIUzcyWDRicVRjV2dwZDVYUzNKMkkyWkwrYzUrbG
9Wd3NhbWhkL25QcVNnRDE1L05vSmx6ZFZICk5kL0NWSXkxcWtZMEVWcSt6WlBHWGVGNHNTb2duMEJZa3BDdGRWVE5xTzNDK3luZFpzSDVBZ01CQUFHalpqQmsKTUE0R0ExVWREd0VCL3dRRUF3SUJCakFTQmdOVkhSTUJBZjhFQ0RBR0FRS
C9BZ0VDTUIwR0ExVWREZ1FXQkJSdgpEYllXTzl2bnRrK2pmS3VsbFBkdFIrbkUyVEFmQmdOVkhTTUVHREFXZ0JSdkRiWVdPOXZudGsramZLdWxsUGR0ClIrbkUyVEFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBSnFyekY3T09xQStUS1li
MU5UTmlVM09rMTNHUE5KekEKL2hNdWc4cFNzMEg0UTREWG0yZ045U05nRXI2UERGbk5rN2ZPSHZYbWVxWmNXN1lFUUNjRmFBQnNUcHEwdGp3Mgo3dThVSkRLSzljUExlczBmUkowYjFhYng0YlU5K20wSGlIVDFlei85Q2U4RHZHTDRwYXR
nYkc3L3JibTdpaDF4CmlBRHNmVDlobHNZLzFUUGxiK3lrcy82Q1lKcTVxT1A1QjN2ME9WOERJbXZJKzFZcFgvSmdWTXJLckxPWlFkM1IKNXNxeEN3VEFLTTN4d3ZCNzRSd1d6amlmWmJuV1lkMW91TjkwUEI4UldRbFNGVU5ITDdpRi95Qm
51MVRkQWdqYwpEMk5DaHpGb2oya0hmODkrclF4VEFYOXU4OERFNVlwa2V5UXBCelowWTd0Q1dMMlNzVXhSc3c9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==''' | base64 -d > /root/kubeconfg-key

此时,我们也可以使用OpenSSL即可查看证书过期时间

[root@k8s-node01 kubernetes]# openssl x509 -in /root/kubeconfg-key  -noout -dates
notBefore=Mar 11 10:50:00 2023 GMT
notAfter=Feb 15 10:50:00 2123 GMT

四、TLS Bootstrapping 初始化

在启动引导初始化过程中,会发生以下事情:

1.kubelet 启动

2.Kubelet查看kubelet.kubeconfig文件,如果是二进制安装的话,一般存放路径为:/etc/kubernetes/kubelet.kubeconfig。

3.假设kubelet 看到自己没有对应的 kubeconfig 文件,会查看本地的bootstrap.kubeconfig。如果是二进制安装的话,一般存放路径为:/etc/kubernetes/bootstrap.kubeconfig。

4.Kubelet读取bootstrap.kubeconfig文件,检索apiserver的url和一个token,观察到本集群中token-id为c8ad9c,token密钥为2e4d610cf3e7426e

[root@k8s-node01 ~]# more /etc/kubernetes/bootstrap-kubelet.kubeconfig

TLS bootstrapping初始化-1

5.Kubelet连接apiserver,使用这个token进行认证

(1)Apiserver会识别token-id,apiserver会查看该token-id对于的bootstrap的一个secret

[root@k8s-master01 ~]# kubectl get secret -n kube-system | grep c8ad9c
bootstrap-token-c8ad9c                           bootstrap.kubernetes.io/token         6      154d

详细查看这个secret内容

[root@k8s-master01 ~]# kubectl get secret bootstrap-token-c8ad9c   -n kube-system -oyaml
apiVersion: v1
data:
  auth-extra-groups: c3lzdGVtOmJvb3RzdHJhcHBlcnM6ZGVmYXVsdC1ub2RlLXRva2VuLHN5c3RlbTpib290c3RyYXBwZXJzOndvcmtlcixzeXN0ZW06Ym9vdHN0cmFwcGVyczppbmdyZXNz
  description: VGhlIGRlZmF1bHQgYm9vdHN0cmFwIHRva2VuIGdlbmVyYXRlZCBieSAna3ViZWxldCAnLg==
  token-id: YzhhZDlj
  token-secret: MmU0ZDYxMGNmM2U3NDI2ZQ==
  usage-bootstrap-authentication: dHJ1ZQ==
  usage-bootstrap-signing: dHJ1ZQ==
kind: Secret
metadata:
  creationTimestamp: "2023-03-11T13:19:57Z"
  name: bootstrap-token-c8ad9c
  namespace: kube-system
  resourceVersion: "1921"
  uid: d409daf1-33a6-4888-a5c8-cbbeccd513fe
type: bootstrap.kubernetes.io/token

当然我们可以进行验证,观察到token-id为c8ad9c,token密钥为2e4d610cf3e7426e

[root@k8s-master01 ~]# echo -n "YzhhZDlj" | base64 -d
c8ad9c
[root@k8s-master01 ~]# echo -n "MmU0ZDYxMGNmM2U3NDI2ZQ==" | base64 -d
2e4d610cf3e7426e

(2)找到这个secret中的一个auth-extra-groups字段,apiserver把这个token识别成一个username,名称是system:bootstrap:<token-id>,属于system:bootstrappers这个组,这个组具有申请csr的权限,该组的权限绑定在一个叫system:node-bootstrapper的clusterrole

TLS bootstrapping初始化-2

[root@k8s-master01 ~]# echo "c3lzdGVtOmJvb3RzdHJhcHBlcnM6ZGVmYXVsdC1ub2RlLXRva2VuLHN5c3RlbTpib290c3RyYXBwZXJzOndvcmtlcixzeXN0ZW06Ym9vdHN0cmFwcGVyczppbmdyZXNz" | base64 -d
system:bootstrappers:default-node-token,system:bootstrappers:worker,system:bootstrappers:ingress

验证system:bootstrappers这个组具有申请csr的权限,观察到具有申请csr的权限

[root@k8s-master01 ~]# kubectl get clusterrole system:node-bootstrapper -oyaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: "true"
  creationTimestamp: "2023-03-11T12:54:21Z"
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
  name: system:node-bootstrapper
  resourceVersion: "97"
  uid: 208e214d-e69a-4a3c-b339-878d81ebc7f8
rules:
- apiGroups:
  - certificates.k8s.io
  resources:
  - certificatesigningrequests
  verbs:
  - create
  - get
  - list
  - watch

验证system:bootstrappers这个组的权限是否绑定在一个叫system:node-bootstrapper的clusterrole

[root@k8s-master01 ~]# kubectl get clusterrolebinding kubelet-bootstrap  -oyaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  creationTimestamp: "2023-03-11T13:19:57Z"
  name: kubelet-bootstrap
  resourceVersion: "1922"
  uid: 1ce9693b-fc89-4929-a766-19e17cc566ab
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:node-bootstrapper
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: system:bootstrappers:default-node-token

6.经过上面的认证,kubelet就有了一个创建和检索CSR的权限

7.Kubelet为自己创建一个CSR,并将其 signerName 设置为kubernetes.io/kube-apiserver-client-kubelet

8.CSR被允许有两种方式:

(1)K8s管理员使用kubectl手动的颁发证书

(2)如果配置了相关权限,kube-controller-manager会自动同意

  • Controller-manager有一个CSR的ApprovingController。他会校验kubelet发来的csr的username和group是否有创建csr的权限,而且还要验证签发者是否是kubernetes.io/kube-apiserver-client-kubelet

  • Controller-manager同意CSR请求

9.CSR被同意后,controller-manager创建kubelet的证书文件

10.Controller-manager将证书更新至csr的status字段

11.Kubelet从apiserver获取证书

12.Kubelet从获取到的key和证书文件创建kubelet.kubeconfig

13.Kubelet启动完成并正常工作

14.可选地,如果配置了自动续期,kubelet会在证书文件过期的时候利用之前的kubeconfig文件去申请一个新的证书,相当于续约

15.新的证书被同意或签发,自动完成或者手动完成取决于我们的配置

(1)Kubelet创建的CSR是属于一个O(证书中的一个字段,表示证书所属的组织或实体的名称):system:nodes

(2)CN(指的是SSL/TLS数字证书中的一个字段。它通常包含了证书所代表的实体,比如网站、服务器等的名称):system:nodes:主机名