TLS 引导

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

引导这些组件(特别是需要证书以便安全地与 kube-apiserver 通信的工作节点)的正常过程可能具有挑战性,因为它通常超出 Kubernetes 的范围并需要大量的额外工作。反过来,这会使集群的初始化或伸缩变得困难。

为了简化这个过程,Kubernetes 在 1.4 版本中引入了证书请求和签名 API。该提案可在此处here找到。

本文档描述了节点初始化过程、如何为 kubelet 设置 TLS 客户端证书引导以及其工作原理。

初始化过程

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

  1. 查找其 kubeconfig 文件
  2. kubeconfig 文件中检索 API 服务器的 URL 和凭据,通常是 TLS 密钥和签名证书
  3. 尝试使用凭据与 API 服务器通信。

假设 kube-apiserver 成功验证了 kubelet 的凭据,它会将 kubelet 视为有效节点,并开始为其分配 Pod。

请注意,上述过程取决于

  • 本地主机 kubeconfig 中存在密钥和证书
  • 证书已被 kube-apiserver 信任的证书颁发机构 (CA) 签署

以下所有事项均由设置和管理集群的人员负责

  1. 创建 CA 密钥和证书
  2. 将 CA 证书分发到运行 kube-apiserver 的控制平面节点
  3. 为每个 kubelet 创建密钥和证书;强烈建议每个 kubelet 都有一个唯一的密钥和证书,以及唯一的 CN
  4. 使用 CA 密钥签署 kubelet 证书
  5. 将 kubelet 密钥和签名证书分发到运行 kubelet 的特定节点上

本文档中描述的 TLS 引导旨在简化并部分或甚至完全自动化步骤 3 及之后的步骤,因为这些是初始化或扩缩集群时最常见的步骤。

引导初始化

在引导初始化过程中,会发生以下情况

  1. kubelet 开始启动
  2. kubelet 发现它没有 kubeconfig 文件
  3. kubelet 搜索并找到 bootstrap-kubeconfig 文件
  4. kubelet 读取其引导文件,检索 API 服务器的 URL 和一个有限用途的“令牌”
  5. kubelet 连接到 API 服务器,使用令牌进行认证
  6. kubelet 现在具有创建和检索证书签名请求 (CSR) 的有限凭据
  7. kubelet 为自己创建 CSR,并将 signerName 设置为 kubernetes.io/kube-apiserver-client-kubelet
  8. CSR 可以通过以下两种方式之一获得批准
    • 如果配置正确,kube-controller-manager 会自动批准 CSR
    • 如果配置正确,外部进程(可能是人工操作)会使用 Kubernetes API 或通过 kubectl 批准 CSR
  9. 为 kubelet 创建证书
  10. 将证书颁发给 kubelet
  11. kubelet 检索证书
  12. kubelet 使用密钥和签名证书创建一个适当的 kubeconfig 文件
  13. kubelet 开始正常运行
  14. 可选:如果配置正确,kubelet 会在证书接近过期时自动请求续订
  15. 续订的证书会根据配置自动或手动批准并颁发。

本文档的其余部分描述了配置 TLS 引导所需的步骤及其限制。

配置

要配置 TLS 引导和可选的自动审批,您必须在以下组件上配置选项

  • kube-apiserver
  • kube-controller-manager
  • kubelet
  • 集群内资源:ClusterRoleBinding 以及可能的 ClusterRole

此外,您还需要您的 Kubernetes 证书颁发机构 (CA)。

证书颁发机构

和没有引导一样,您需要一个证书颁发机构 (CA) 密钥和证书。和没有引导一样,这些将用于签署 kubelet 证书。同样,您有责任将其分发到控制平面节点。

为了本文档的目的,我们假设这些文件已分发到控制平面节点的 /var/lib/kubernetes/ca.pem(证书)和 /var/lib/kubernetes/ca-key.pem(密钥)。我们将这些称为“Kubernetes CA 证书和密钥”。

所有使用这些证书的 Kubernetes 组件——kubelet、kube-apiserver、kube-controller-manager——都假定密钥和证书采用 PEM 编码。

kube-apiserver 配置

kube-apiserver 需要满足几个要求才能启用 TLS 引导

  • 识别签署客户端证书的 CA
  • 将引导中的 kubelet 认证到 system:bootstrappers
  • 授权引导中的 kubelet 创建证书签名请求 (CSR)

识别客户端证书

这对于所有客户端证书认证来说是正常的。如果尚未设置,请将 --client-ca-file=FILENAME 标志添加到 kube-apiserver 命令中以启用客户端证书认证,并引用包含签名证书的证书颁发机构包,例如 --client-ca-file=/var/lib/kubernetes/ca.pem

初始引导认证

为了使引导中的 kubelet 能够连接到 kube-apiserver 并请求证书,它必须首先向服务器进行认证。您可以使用任何能够认证 kubelet 的认证器

尽管任何认证策略都可用于 kubelet 的初始引导凭据,但为了便于配置,建议使用以下两种认证器。

  1. 引导令牌
  2. 令牌认证文件

使用引导令牌是认证 kubelet 的一种更简单、更易于管理的方法,并且在启动 kube-apiserver 时不需要任何附加标志。

无论您选择哪种方法,要求是 kubelet 必须能够以具有以下权限的用户身份进行认证:

  1. 创建和检索 CSR
  2. 如果启用了自动审批,则自动获得批准以请求节点客户端证书。

使用引导令牌进行认证的 kubelet 被认证为 system:bootstrappers 组中的用户,这是标准的用法。

随着此功能的成熟,您应确保令牌绑定到基于角色的访问控制 (RBAC) 策略,该策略严格限制(使用引导令牌的)请求仅限于与证书供应相关的客户端请求。通过使用 RBAC,将令牌限定在组范围内可以带来极大的灵活性。例如,在完成节点配置后,您可以禁用特定引导组的访问权限。

引导令牌

引导令牌的详细描述可在此处here找到。这些令牌作为 Secrets 存储在 Kubernetes 集群中,然后颁发给单个 kubelet。您可以为一个完整的集群使用单个令牌,或为每个工作节点颁发一个令牌。

流程分为两部分

  1. 创建一个包含令牌 ID、Secret 和作用域的 Kubernetes Secret。
  2. 将令牌颁发给 kubelet

从 kubelet 的角度来看,令牌没有什么特殊含义。然而,从 kube-apiserver 的角度来看,引导令牌是特殊的。由于其 typenamespacename,kube-apiserver 将其识别为特殊令牌,并授予使用该令牌进行认证的任何用户特殊的引导权限,特别是将其视为 system:bootstrappers 组的成员。这满足了 TLS 引导的基本要求。

创建 Secret 的详细信息可在此处here找到。

如果您想使用引导令牌,必须通过以下标志在 kube-apiserver 上启用它

--enable-bootstrap-token-auth=true

令牌认证文件

kube-apiserver 能够接受令牌作为认证方式。这些令牌是任意的,但应代表至少 128 位来自安全随机数生成器(例如大多数现代 Linux 系统上的 /dev/urandom)的熵。有多种方法可以生成令牌。例如

head -c 16 /dev/urandom | od -An -t x | tr -d ' '

这将生成类似于 02b50b05283e98dd0fd71db496ef01e8 的令牌。

令牌文件应如下例所示,其中前三个值可以是任何内容,而引用的组名应如图所示

02b50b05283e98dd0fd71db496ef01e8,kubelet-bootstrap,10001,"system:bootstrappers"

--token-auth-file=FILENAME 标志添加到 kube-apiserver 命令中(可能在您的 systemd unit 文件中)以启用令牌文件。更多详情请参见此处的文档here

授权 kubelet 创建 CSR

现在,引导节点已经作为 system:bootstrappers 组的一部分获得**认证**,它还需要获得**授权**来创建证书签名请求 (CSR) 并在完成后检索它。幸运的是,Kubernetes 提供了一个精确地具有这些(并且仅有这些)权限的 ClusterRole,即 system:node-bootstrapper

为此,您只需创建一个 ClusterRoleBinding,将 system:bootstrappers 组绑定到集群角色 system:node-bootstrapper

# enable bootstrapping nodes to create CSR
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: create-csrs-for-bootstrapping
subjects:
- kind: Group
  name: system:bootstrappers
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: system:node-bootstrapper
  apiGroup: rbac.authorization.k8s.io

kube-controller-manager 配置

虽然 apiserver 接收 kubelet 发送的证书请求并对这些请求进行认证,但 controller-manager 负责颁发实际的签名证书。

controller-manager 通过证书颁发控制循环来执行此功能。这采取了使用磁盘上资产的 cfssl 本地签名者的形式。当前,所有颁发的证书有效期均为一年,并且具有一组默认的密钥用途。

为了让 controller-manager 签署证书,它需要以下内容

  • 访问您创建并分发的“Kubernetes CA 密钥和证书”
  • 启用 CSR 签名

访问密钥和证书

如前所述,您需要创建 Kubernetes CA 密钥和证书,并将其分发到控制平面节点。controller-manager 将使用这些文件来签署 kubelet 证书。

由于这些签名的证书将反过来被 kubelet 用作常规 kubelet 向 kube-apiserver 进行认证,因此在此时提供给 controller-manager 的 CA 也必须被 kube-apiserver 信任用于认证,这一点非常重要。这可以通过 --client-ca-file=FILENAME 标志(例如,--client-ca-file=/var/lib/kubernetes/ca.pem)提供给 kube-apiserver,如 kube-apiserver 配置部分所述。

要将 Kubernetes CA 密钥和证书提供给 kube-controller-manager,请使用以下标志

--cluster-signing-cert-file="/etc/path/to/kubernetes/ca/ca.crt" --cluster-signing-key-file="/etc/path/to/kubernetes/ca/ca.key"

例如

--cluster-signing-cert-file="/var/lib/kubernetes/ca.pem" --cluster-signing-key-file="/var/lib/kubernetes/ca-key.pem"

签名证书的有效期可以通过以下标志进行配置

--cluster-signing-duration

审批

为了批准 CSR,您需要告诉 controller-manager 可以批准它们。这通过向正确的组授予 RBAC 权限来完成。

有两种不同的权限集

  • nodeclient:如果一个节点正在为一个节点创建新证书,那么它还没有证书。它使用上述列出的令牌之一进行认证,因此属于 system:bootstrappers 组。
  • selfnodeclient:如果一个节点正在续订其证书,则(根据定义)它已经有一个证书,它会持续使用该证书作为 system:nodes 组的一部分进行认证。

为了使 kubelet 能够请求和接收新证书,请创建一个 ClusterRoleBinding,将引导节点所属的组 system:bootstrappers 绑定到授予其权限的 ClusterRole,即 system:certificates.k8s.io:certificatesigningrequests:nodeclient

# Approve all CSRs for the group "system:bootstrappers"
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: auto-approve-csrs-for-group
subjects:
- kind: Group
  name: system:bootstrappers
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: system:certificates.k8s.io:certificatesigningrequests:nodeclient
  apiGroup: rbac.authorization.k8s.io

为了使 kubelet 能够续订自己的客户端证书,请创建一个 ClusterRoleBinding,将正常运行节点所属的组 system:nodes 绑定到授予其权限的 ClusterRole,即 system:certificates.k8s.io:certificatesigningrequests:selfnodeclient

# Approve renewal CSRs for the group "system:nodes"
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: auto-approve-renewals-for-nodes
subjects:
- kind: Group
  name: system:nodes
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: system:certificates.k8s.io:certificatesigningrequests:selfnodeclient
  apiGroup: rbac.authorization.k8s.io

作为 kube-controller-manager 的一部分提供的 csrapproving 控制器默认启用。该控制器使用 SubjectAccessReview API 来确定给定用户是否被授权请求 CSR,然后根据授权结果进行审批。为了防止与其他审批者冲突,内置审批者不会明确拒绝 CSR。它只忽略未经授权的请求。该控制器还会作为垃圾回收的一部分清理已过期的证书。

kubelet 配置

最后,在正确设置控制平面节点并配置所有必要的认证和授权后,我们可以配置 kubelet。

kubelet 进行引导需要以下配置

  • 存储其生成的密钥和证书的路径(可选,可以使用默认值)
  • 尚不存在的 kubeconfig 文件路径;它将在此处放置引导配置的文件
  • 引导 kubeconfig 文件的路径,用于提供服务器的 URL 和引导凭据,例如引导令牌
  • 可选:证书轮换说明

引导 kubeconfig 文件应该位于 kubelet 可以访问的路径下,例如 /var/lib/kubelet/bootstrap-kubeconfig

它的格式与普通的 kubeconfig 文件相同。示例文件可能如下所示

apiVersion: v1
kind: Config
clusters:
- cluster:
    certificate-authority: /var/lib/kubernetes/ca.pem
    server: https://my.server.example.com:6443
  name: bootstrap
contexts:
- context:
    cluster: bootstrap
    user: kubelet-bootstrap
  name: bootstrap
current-context: bootstrap
preferences: {}
users:
- name: kubelet-bootstrap
  user:
    token: 07401b.f395accd246ae52d

需要注意的重要元素是

  • certificate-authority:CA 文件路径,用于验证 kube-apiserver 提供的服务器证书
  • server:kube-apiserver 的 URL
  • token:要使用的令牌

令牌的格式无关紧要,只要与 kube-apiserver 期望的格式匹配即可。在上面的示例中,我们使用了引导令牌。如前所述,可以使用**任何**有效的认证方法,而不仅仅是令牌。

因为引导 kubeconfig 文件**是**标准的 kubeconfig 文件,所以您可以使用 kubectl 来生成它。要创建上面的示例文件

kubectl config --kubeconfig=/var/lib/kubelet/bootstrap-kubeconfig set-cluster bootstrap --server='https://my.server.example.com:6443' --certificate-authority=/var/lib/kubernetes/ca.pem
kubectl config --kubeconfig=/var/lib/kubelet/bootstrap-kubeconfig set-credentials kubelet-bootstrap --token=07401b.f395accd246ae52d
kubectl config --kubeconfig=/var/lib/kubelet/bootstrap-kubeconfig set-context bootstrap --user=kubelet-bootstrap --cluster=bootstrap
kubectl config --kubeconfig=/var/lib/kubelet/bootstrap-kubeconfig use-context bootstrap

要指示 kubelet 使用引导 kubeconfig 文件,请使用以下 kubelet 标志

--bootstrap-kubeconfig="/var/lib/kubelet/bootstrap-kubeconfig" --kubeconfig="/var/lib/kubelet/kubeconfig"

启动 kubelet 时,如果通过 --kubeconfig 指定的文件不存在,则使用通过 --bootstrap-kubeconfig 指定的引导 kubeconfig 文件向 API 服务器请求客户端证书。在证书请求获得批准并由 kubelet 收回后,将一个引用生成的密钥和获得的证书的 kubeconfig 文件写入由 --kubeconfig 指定的路径。证书和密钥文件将放置在由 --cert-dir 指定的目录中。

客户端证书和服务器证书

以上所有内容都与 kubelet **客户端**证书有关,特别是 kubelet 用于向 kube-apiserver 进行认证的证书。

kubelet 也可以使用**服务器**证书。kubelet 本身为某些功能公开了 https 端点。为了保护这些端点,kubelet 可以执行以下操作之一

  • 通过 --tls-private-key-file--tls-cert-file 标志使用提供的密钥和证书
  • 如果未提供密钥和证书,则创建自签名密钥和证书
  • 通过 CSR API 从集群服务器请求服务器证书

TLS 引导提供的客户端证书默认仅用于 client auth 签名,因此不能用作服务器证书或 server auth

然而,您**可以**通过证书轮换至少部分地启用其服务器证书。

证书轮换

Kubernetes v1.8 及更高版本的 kubelet 实现了启用其客户端和/或服务器证书轮换的功能。请注意,服务器证书轮换是一项**Beta**功能,需要在 kubelet 上启用 RotateKubeletServerCertificate 特性门控(默认启用)。

您可以配置 kubelet 在现有凭据过期时通过创建新的 CSR 来轮换其客户端证书。要启用此功能,请使用 kubelet 配置文件rotateCertificates 字段或将以下命令行参数传递给 kubelet(已弃用)

--rotate-certificates

启用 RotateKubeletServerCertificate 会使 kubelet 在引导其客户端凭据后**既**请求服务器证书,**又**轮换该证书。要启用此行为,请使用 kubelet 配置文件serverTLSBootstrap 字段或将以下命令行参数传递给 kubelet(已弃用)

--rotate-server-certificates

其他认证组件

本文档中描述的所有 TLS 引导都与 kubelet 相关。但是,其他组件可能需要直接与 kube-apiserver 通信。值得注意的是 kube-proxy,它是 Kubernetes 节点组件的一部分,并在每个节点上运行,但也可能包括其他组件,例如监控或网络。

与 kubelet 一样,这些其他组件也需要一种向 kube-apiserver 进行认证的方法。您有几种方法可以生成这些凭据

  • 旧方法:以 TLS 引导之前为 kubelet 创建和分发证书的相同方式创建和分发证书
  • DaemonSet:由于 kubelet 本身加载在每个节点上,并且足以启动基本服务,您可以将 kube-proxy 和其他特定于节点的服务作为 kube-system 命名空间中的 DaemonSet 运行,而不是作为独立的进程运行。由于它在集群内,您可以为其分配一个具有适当权限的服务账号来执行其活动。这可能是配置此类服务的最简单方法。

kubectl 审批

CSR 可以在 controller manager 内置的审批流程之外获得批准。

签名控制器不会立即签署所有证书请求。相反,它会等到这些请求被具有适当权限的用户标记为“已批准”状态。此流程旨在允许由外部审批控制器或核心 controller-manager 中实现的审批控制器处理自动化审批。然而,集群管理员也可以使用 kubectl 手动批准证书请求。管理员可以使用 kubectl get csr 列出 CSR,并使用 kubectl describe csr <name> 详细描述某个 CSR。管理员可以使用 kubectl certificate approve <name>kubectl certificate deny <name> 批准或拒绝 CSR。

最后修改于 2023 年 10 月 6 日太平洋标准时间下午 7:24:清理 kubelet-tls-bootstrapping.md (318ff2e797)