TLS 引导
在 Kubernetes 集群中,工作节点上的组件 - kubelet 和 kube-proxy - 需要与 Kubernetes 控制平面组件(特别是 kube-apiserver)进行通信。为了确保通信的私密性、不受干扰,并确保集群的每个组件都与另一个受信任的组件通信,我们强烈建议在节点上使用客户端 TLS 证书。
引导这些组件的正常过程,尤其是需要证书才能与 kube-apiserver 安全通信的工作节点,可能是一个具有挑战性的过程,因为它通常超出 Kubernetes 的范围,并且需要大量的额外工作。反过来,这会使初始化或扩展集群具有挑战性。
为了简化此过程,从 1.4 版本开始,Kubernetes 引入了证书请求和签名 API。提案可以在此处找到。
本文档描述了节点初始化过程,如何为 kubelet 设置 TLS 客户端证书引导,以及它的工作原理。
初始化过程
当工作节点启动时,kubelet 会执行以下操作
- 查找其
kubeconfig
文件 - 从
kubeconfig
文件中检索 API 服务器的 URL 和凭据,通常是 TLS 密钥和签名证书 - 尝试使用凭据与 API 服务器通信。
假设 kube-apiserver 成功验证了 kubelet 的凭据,它会将 kubelet 视为有效节点,并开始为其分配 Pod。
请注意,上述过程取决于
- 本地主机
kubeconfig
中存在密钥和证书 - 该证书已由 kube-apiserver 信任的证书颁发机构 (CA) 签名
以下所有内容都是设置和管理集群的人员的责任
- 创建 CA 密钥和证书
- 将 CA 证书分发到运行 kube-apiserver 的控制平面节点
- 为每个 kubelet 创建一个密钥和证书;强烈建议每个 kubelet 都具有一个唯一的密钥和证书,并且具有唯一的 CN
- 使用 CA 密钥签名 kubelet 证书
- 将 kubelet 密钥和签名证书分发到运行 kubelet 的特定节点
本文档中描述的 TLS 引导旨在简化并部分甚至完全自动化步骤 3 及以后的步骤,因为这些步骤在初始化或扩展集群时最为常见。
引导初始化
在引导初始化过程中,会发生以下情况
- kubelet 启动
- kubelet 看到它没有
kubeconfig
文件 - kubelet 搜索并找到一个
bootstrap-kubeconfig
文件 - kubelet 读取其引导文件,检索 API 服务器的 URL 和有限使用的“令牌”
- kubelet 连接到 API 服务器,使用令牌进行身份验证
- kubelet 现在拥有创建和检索证书签名请求 (CSR) 的有限凭据
- kubelet 为自己创建一个 CSR,并将 signerName 设置为
kubernetes.io/kube-apiserver-client-kubelet
- CSR 通过以下两种方式之一批准
- 如果配置,kube-controller-manager 会自动批准 CSR
- 如果配置,外部进程(可能是一个人)会使用 Kubernetes API 或通过
kubectl
批准 CSR
- 为 kubelet 创建证书
- 将证书颁发给 kubelet
- kubelet 检索证书
- kubelet 使用密钥和签名证书创建一个正确的
kubeconfig
- kubelet 开始正常运行
- 可选:如果配置,kubelet 会在证书即将过期时自动请求续订
- 续订的证书会根据配置自动或手动批准和颁发。
本文档的其余部分描述了配置 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 的初始引导凭据,但为了便于配置,建议使用以下两个身份验证器。
使用引导令牌是一种更简单、更易于管理的方法来验证 kubelet 的身份,并且在启动 kube-apiserver 时不需要任何额外的标志。
无论您选择哪种方法,要求是 kubelet 能够以具有以下权限的用户的身份进行身份验证
- 创建和检索 CSR
- 如果启用了自动批准,则自动批准请求节点客户端证书。
使用引导令牌进行身份验证的 kubelet 会作为 system:bootstrappers
组中的用户进行身份验证,这是使用的标准方法。
随着此功能的成熟,您应确保令牌绑定到基于角色的访问控制 (RBAC) 策略,该策略将请求(使用引导令牌)严格限制为与证书配置相关的客户端请求。通过 RBAC,将令牌范围限定到组可以提供极大的灵活性。例如,您可以在配置完节点后禁用特定引导组的访问权限。
引导令牌
引导令牌的详细信息在此处进行了描述。这些令牌作为 Secret 存储在 Kubernetes 集群中,然后颁发给各个 kubelet。您可以为整个集群使用单个令牌,也可以为每个工作节点颁发一个令牌。
该过程分为两个步骤
- 使用令牌 ID、密钥和范围创建 Kubernetes Secret。
- 向 kubelet 颁发令牌
从 kubelet 的角度来看,一个令牌与另一个令牌没什么区别,没有任何特殊含义。但是,从 kube-apiserver 的角度来看,引导令牌是特殊的。由于其 type
、namespace
和 name
,kube-apiserver 将其识别为特殊令牌,并授予任何使用该令牌进行身份验证的用户特殊的引导权限,特别是将其视为 system:bootstrappers
组的成员。这满足了 TLS 引导的基本要求。
创建 Secret 的详细信息可在此处找到。
如果要使用引导令牌,必须使用以下标志在 kube-apiserver 上启用它
--enable-bootstrap-token-auth=true
令牌身份验证文件
kube-apiserver 能够接受令牌作为身份验证。这些令牌是任意的,但应至少表示从安全随机数生成器(例如,大多数现代 Linux 系统上的 /dev/urandom
)派生的 128 位熵。您可以通过多种方式生成令牌。例如
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 单元文件中)以启用令牌文件。有关更多详细信息,请参阅此处的文档。
授权 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 配置部分所述。
要向 kube-controller-manager 提供 Kubernetes CA 密钥和证书,请使用以下标志
--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
csrapproving
控制器作为 kube-controller-manager 的一部分提供,并且默认情况下处于启用状态。该控制器使用 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 的 URLtoken
:要使用的令牌
令牌的格式无关紧要,只要它与 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
功能标志(默认情况下启用)。
您可以通过在现有凭据过期时创建新的 CSR 来配置 kubelet 以轮换其客户端证书。要启用此功能,请使用 kubelet 配置文件的 rotateCertificates
字段,或将以下命令行参数传递给 kubelet(已弃用)
--rotate-certificates
启用 RotateKubeletServerCertificate
会导致 kubelet 在引导其客户端凭据后 **既** 请求服务器证书 **又** 轮换该证书。要启用此行为,请使用 kubelet 配置文件的 serverTLSBootstrap
字段,或将以下命令行参数传递给 kubelet(已弃用)
--rotate-server-certificates
注意
出于安全原因,核心 Kubernetes 中实现的 CSR 批准控制器不批准节点服务器证书。要使用 RotateKubeletServerCertificate
,操作员需要运行自定义的批准控制器,或手动批准服务器证书请求。
针对 kubelet 服务器证书的特定于部署的批准过程通常应仅批准以下 CSR
- 由节点请求(确保
spec.username
字段的形式为system:node:<nodeName>
且spec.groups
包含system:nodes
) - 请求用于服务证书的用法(确保
spec.usages
包含server auth
,可选地包含digital signature
和key encipherment
,并且不包含其他用法) - 仅包含属于请求节点的 IP 和 DNS subjectAltNames,且不包含 URI 和 Email subjectAltNames(解析
spec.request
中的 x509 证书签名请求以验证subjectAltNames
)
其他身份验证组件
本文档中描述的所有 TLS 引导都与 kubelet 相关。但是,其他组件可能需要直接与 kube-apiserver 通信。值得注意的是 kube-proxy,它是 Kubernetes 节点组件的一部分,在每个节点上运行,但也可能包括其他组件,例如监控或网络。
与 kubelet 一样,这些其他组件也需要一种向 kube-apiserver 进行身份验证的方法。您有几个选项来生成这些凭据
- 旧方法:以与 TLS 引导之前为 kubelet 创建和分发证书相同的方式进行创建和分发
- DaemonSet:由于 kubelet 本身已加载到每个节点上,并且足以启动基本服务,因此您可以将 kube-proxy 和其他节点特定服务不作为独立进程运行,而是作为
kube-system
命名空间中的守护程序集运行。由于它将在集群内部,您可以为其提供具有适当权限的服务帐户来执行其活动。这可能是配置此类服务的最简单方法。
kubectl 批准
可以在控制器管理器内置的批准流程之外批准 CSR。
签名控制器不会立即签署所有证书请求。相反,它会等待,直到它们被具有适当特权的用户标记为“已批准”状态。此流程旨在允许由外部批准控制器或核心控制器管理器中实现的批准控制器处理的自动批准。但是,集群管理员也可以使用 kubectl 手动批准证书请求。管理员可以使用 kubectl get csr
列出 CSR,并使用 kubectl describe csr <name>
详细描述其中一个。管理员可以使用 kubectl certificate approve <name>
和 kubectl certificate deny <name>
批准或拒绝 CSR。