在Kubernetes集群中,工作程序节点上的组件-kubelet和kube-proxy-需要与Kubernetes主组件(特别是kube-apiserver)进行通信。为了确保通信保持私密,不受干扰,并确保群集的每个组件都在与另一个受信任的组件通信,我们强烈建议在节点上使用客户端TLS证书。
自举这些组件的正常过程,特别是需要证书的工作节点,以便它们可以与kube-apiserver安全通信,这是一个具有挑战性的过程,因为它通常不在Kubernetes的范围内,并且需要大量的额外工作。反过来,这可能使初始化或扩展集群具有挑战性。
为了简化流程,从版本1.4开始,Kubernetes引入了证书请求和签名API来简化流程。
本文档介绍了节点初始化的过程,如何为kubelet设置TLS客户端证书自举以及其工作方式。
初始化过程
本集群使用的kubernetes版本为1.19.2,使用kubeadm部署的master节点。
新加入的主机为node3,假设你已经安装并配置好docker,并且已经安装好了kubelet、kubeadm、kubectl。然后就根据下方操作来加入集群吧。
引导程序初始化
在引导程序初始化过程中,会发生以下情况:
- kubelet开始
- kubelet认为,它并没有有一个kubeconfig文件
- kubelet搜索并找到bootstrap-kubeconfig文件
- kubelet读取其引导程序文件,检索API服务器的URL和有限用法的“令牌”
- kubelet连接到API服务器,使用令牌进行身份验证
- kubelet现在具有有限的凭据来创建和检索证书签名请求(CSR)
- kubelet为自己创建一个CSR,并将signerName设置为kubernetes.io/kube-apiserver-client-kubelet
- 通过以下两种方式之一批准CSR:
1⃣️如果已配置,则kube-controller-manager会自动批准CSR
2⃣️如果进行了配置,则外部流程(可能是一个人)会使用Kubernetes API或通过以下方式批准CSR kubectl - 为kubelet创建了证书
- 证书已颁发给kubelet
- kubelet检索证书
- kubeletkubeconfig使用密钥和签名证书创建一个正确的名称
- kubelet开始正常运行
- 可选:如果已配置,则kubelet在证书即将到期时会自动请求更新证书
- 根据配置,自动或手动批准和颁发续签的证书。
- 本文档的其余部分介绍了配置TLS引导的必要步骤及其限制。
组态
要配置TLS引导和可选的自动批准,必须在以下组件上配置选项:
- kube-apiserver
- kube-controller-manager
- kubelet
- 集群内资源:ClusterRoleBinding并且可能ClusterRole
此外,您还需要Kubernetes证书颁发机构(CA)。
证书颁发机构
与不使用引导程序一样,您将需要证书颁发机构(CA)密钥和证书。与不使用引导程序一样,它们将用于签署kubelet证书。和以前一样,将它们分发到主节点是您的责任。
使用kubeadm部署的集群kubelet使用clientCAFile为ca.crt,默认的目录为/etc/kubernetes/pki下
root@master:~# ssh node3 mkdir /etc/kubernetes/pki
root@master:~# scp /etc/kubernetes/pki/ca.crt node3:/etc/kubernetes/pki/ca.crt
ca.crt
使用这些证书的所有Kubernetes组件-kubelet,kube-apiserver,kube-controller-manager-都采用要进行PEM编码的密钥和证书。
kube-apiserver配置
kube-apiserver对启用TLS引导具有若干要求:
- 识别签署客户证书的CA
- 向system:bootstrappers组验证自举kubelet
- 授权自举kubelet创建证书签名请求(CSR)
识别客户证书
对于所有客户端证书身份验证,这都是正常的。如果尚未设置,请将该–client-ca-file=FILENAME标记添加到kube-apiserver命令以启用客户端证书认证,例如,引用包含签名证书的证书颁发机构捆绑包
–client-ca-file=/etc/kubernetes/pki/ca.crt
(kubeadm部署集群kube-apiserver、kube-controller-manager是以静态pod的形式运行,kubelet静态pod的默认目录为/etc/kubernetes/manifests。)
初始引导程序身份验证
为了使引导kubelet连接到kube-apiserver并请求证书,它必须首先对服务器进行身份验证。你可以使用任何身份验证,可以验证kubelet。
尽管任何身份验证策略均可用于kubelet的初始引导程序凭据,但建议使用以下两个身份验证器以简化设置。
- 引导令牌
- 令牌认证文件
Bootstrap令牌是一种用于验证kubelet的更简单,更易于管理的方法,并且在启动kube-apiserver时不需要任何其他标志。从Kubernetes 1.12版本开始,使用引导令牌目前是beta版。
无论选择哪种方法,都要求kubelet能够以具有以下权限的用户身份进行身份验证:
- 创建和检索CSR
- 如果启用了自动批准,则将自动批准以请求节点客户端证书。
使用引导令牌进行身份验证的kubelet将作为组中的用户进行身份验证system:bootstrappers,这是要使用的标准方法。
随着此功能的成熟,您应确保将令牌绑定到基于角色的访问控制(RBAC)策略,该策略将请求(使用引导令牌)严格限制为与证书供应相关的客户端请求。有了RBAC,将令牌划分为一组即可实现极大的灵活性。例如,配置完节点后,可以禁用特定引导组的访问。
引导令牌
引导令牌将在此处详细描述。这些令牌作为秘密存储在Kubernetes集群中,然后分发给各个kubelet。您可以为整个集群使用单个令牌,也可以为每个工作节点发行一个令牌。
该过程有两个方面:
使用令牌ID,密钥和范围创建一个Kubernetes密钥。
将令牌发行到kubelet
从kubelet的角度来看,一个令牌就像另一个令牌,没有特殊意义。从kube-apiserver的角度来看,引导令牌是特殊的。由于它Type,namespace并且name,KUBE-API服务器将其识别为一个特殊的记号,并授予认证的人与该令牌特殊的启动权,尤其是对待他们的成员system:bootstrappers组。这满足了TLS引导的基本要求。
有关创建机密的详细信息,请参见此处。
如果要使用引导令牌,则必须在带有标记的kube-apiserver上启用它:
–enable-bootstrap-token-auth=true
令牌认证文件
kube-apiserver可以接受令牌作为身份验证。这些令牌是任意的,但应至少代表从安全随机数生成器(例如/dev/urandom在大多数现代Linux系统上)派生的至少128位熵。您可以通过多种方式生成令牌。例如:
head -c 16 /dev/urandom | od -An -t x | tr -d ' '
会生成看起来像的令牌
eb5655435772be4af039e1d338cffe5c
令牌文件应类似于以下示例,其中前三个值可以是任何值,并且引用的组名应如下所示:
eb5655435772be4af039e1d338cffe5c,kubelet-bootstrap,10001,"system:bootstrappers"
将
–token-auth-file=FILENAME
标记添加到kube-apiserver命令(可能在您的systemd单元文件中)以启用令牌文件。
增加完命令后的kube-apiserver 为
apiVersion: v1
kind: Pod
metadata:
annotations:
kubeadm.kubernetes.io/kube-apiserver.advertise-address.endpoint: 192.168.178.170:6443
creationTimestamp: null
labels:
component: kube-apiserver
tier: control-plane
name: kube-apiserver
namespace: kube-system
spec:
containers:
- command:
- kube-apiserver
- --token-auth-file=/etc/kubernetes/pki/tokenfile ## 添加授权文件
- --advertise-address=192.168.178.170 ##修改为自己主机地址
- --allow-privileged=true
- --authorization-mode=Node,RBAC
- --client-ca-file=/etc/kubernetes/pki/ca.crt ## 如果没有添加此参数
- --enable-admission-plugins=NodeRestriction
- --enable-bootstrap-token-auth=true ## 如果没有添加此参数
- --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt
- --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt
- --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key
- --etcd-servers=https://127.0.0.1:2379
- --insecure-port=0
- --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt
- --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
- --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt
- --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key
- --requestheader-allowed-names=front-proxy-client
- --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
- --requestheader-extra-headers-prefix=X-Remote-Extra-
- --requestheader-group-headers=X-Remote-Group
- --requestheader-username-headers=X-Remote-User
- --secure-port=6443
- --service-account-key-file=/etc/kubernetes/pki/sa.pub
- --service-cluster-ip-range=10.96.0.0/12
- --tls-cert-file=/etc/kubernetes/pki/apiserver.crt
- --tls-private-key-file=/etc/kubernetes/pki/apiserver.key
image: registry.aliyuncs.com/google_containers/kube-apiserver:v1.19.2
...
授权kubelet创建CSR
现在,自举节点已作为组的一部分进行身份验证system:bootstrappers,因此需要对其进行授权以创建证书签名请求(CSR)并在完成后对其进行检索。幸运的是,Kubernetes附带了ClusterRole具有这些(也就是这些)权限的system:node-bootstrapper。
为此,您只需创建一个ClusterRoleBinding将system:bootstrappers组绑定到集群角色的system:node-bootstrapper。
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的证书请求并对它们进行身份验证时,控制器管理器负责发布实际的签名证书。
控制器管理器通过证书颁发控制回路执行此功能。这采用使用磁盘上资产的cfssl本地签名者的形式 。当前,所有颁发的证书具有一年的有效期和一组默认的密钥用法。
为了使控制器管理员对证书进行签名,需要以下内容:
- 访问您创建和分发的“ Kubernetes CA密钥和证书”
- 启用CSR签名
访问密钥和证书
如前所述,您需要创建一个Kubernetes CA密钥和证书,并将其分发到主节点。控制器管理员将使用这些签名kubelet证书。
由于这些签名的证书将依次由kubelet使用,以作为对kube-apiserver的常规kubelet进行身份验证,因此在此阶段提供给控制器管理器的CA也必须由kube-apiserver信任进行身份验证,这一点很重要。如kube-apiserver配置部分中所述,此标志带有标志–client-ca-file=FILENAME(例如**–client-ca-file=/var/lib/kubernetes/ca.pem**)提供给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"
签名证书的有效期限可以使用以下标志配置:
--experimental-cluster-signing-duration
增加完命令后的kube-controller-manager 为
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
component: kube-controller-manager
tier: control-plane
name: kube-controller-manager
namespace: kube-system
spec:
containers:
- command:
- kube-controller-manager
- --allocate-node-cidrs=true
- --authentication-kubeconfig=/etc/kubernetes/controller-manager.conf
- --authorization-kubeconfig=/etc/kubernetes/controller-manager.conf
- --bind-address=127.0.0.1
- --client-ca-file=/etc/kubernetes/pki/ca.crt
- --cluster-cidr=192.168.0.0/16
- --cluster-name=kubernetes
- --cluster-signing-cert-file=/etc/kubernetes/pki/ca.crt ## 如果没有添加此参数
- --cluster-signing-key-file=/etc/kubernetes/pki/ca.key ## 如果没有添加此参数
- --controllers=*,bootstrapsigner,tokencleaner
- --experimental-cluster-signing-duration=8760h0m0s ##设置证书过期时间
- --kubeconfig=/etc/kubernetes/controller-manager.conf
- --leader-elect=true
- --node-cidr-mask-size=24
- --port=0
- --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
- --root-ca-file=/etc/kubernetes/pki/ca.crt
- --service-account-private-key-file=/etc/kubernetes/pki/sa.key
- --service-cluster-ip-range=10.96.0.0/12
- --use-service-account-credentials=true
image: registry.aliyuncs.com/google_containers/kube-controller-manager:v1.19.2
......
批准
为了批准CSR,您需要告诉控制器经理批准它们是可以接受的。这是通过将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-控制器管理器和默认情况下启用。控制器使用SubjectAccessReview API确定给定用户是否被授权请求CSR,然后根据授权结果进行批准。为了防止与其他批准者发生冲突,内置批准者不会明确拒绝CSR。它仅忽略未授权的请求。控制器还将修剪过期的证书,作为垃圾回收的一部分。
kubelet配置
最后,在正确设置主节点以及所有必要的身份验证和授权之后,我们可以配置kubelet(在需要加入的节点配置)。
配置文件说明
root@node3:~# cat /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
# Note: This dropin only works with kubeadm and kubelet v1.11+
[Service]
Environment="KUBELET_KUBECONFIG_ARGS=--bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf --kubeconfig=/etc/kubernetes/kubelet.conf "
Environment="KUBELET_CONFIG_ARGS=--config=/var/lib/kubelet/config.yaml"
# This is a file that "kubeadm init" and "kubeadm join" generates at runtime, populating the KUBELET_KUBEADM_ARGS variable dynamically
EnvironmentFile=-/var/lib/kubelet/kubeadm-flags.env
# This is a file that the user can use for overrides of the kubelet args as a last resort. Preferably, the user should use
# the .NodeRegistration.KubeletExtraArgs object in the configuration files instead. KUBELET_EXTRA_ARGS should be sourced from this file.
EnvironmentFile=-/etc/default/kubelet
ExecStart=
ExecStart=/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_KUBEADM_ARGS $KUBELET_EXTRA_ARGS
kubelet需要以下配置来引导:
- 存储生成的密钥和证书的路径(可选,可以使用默认值)
- kubeconfig尚不存在的文件的路径;它将引导文件放在这里
- 引导kubeconfig文件的路径,用于提供服务器和引导凭据的URL,例如引导令牌
- 可选:旋转证书的说明
引导程序kubeconfig应位于kubelet可用的路径中,默认为/etc/kubernetes/bootstrap-kubelet.conf
其格式与普通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: eb5655435772be4af039e1d338cffe5c ##我们前面生成的token
要注意的重要元素是:
certificate-authority:CA文件的路径,用于验证kube-apiserver提交的服务器证书
server:kube-apiserver的URL
token:要使用的令牌
令牌的格式无关紧要,只要它与kube-apiserver期望的匹配即可。在上面的示例中,我们使用了引导令牌。如前所述,可以使用任何有效的身份验证方法,而不仅仅是令牌。
由于引导程序kubeconfig 是标准的kubeconfig,因此可以kubectl用来生成它。要创建以上示例文件:
kubectl config --kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf set-cluster bootstrap --server='https://192.168.178.170:6443' --certificate-authority=/etc/kubernetes/pki/ca.crt
kubectl config --kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf set-credentials kubelet-bootstrap --token=eb5655435772be4af039e1d338cffe5c
kubectl config --kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf set-context bootstrap --user=kubelet-bootstrap --cluster=bootstrap
kubectl config --kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf use-context bootstrap
要指示kubelet使用引导程序kubeconfig,请使用以下kubelet标志(默认已添加):
--bootstrap-kubeconfig="/etc/kubernetes/bootstrap-kubelet.conf " --kubeconfig="/etc/kubernetes/kubelet.config"
通过前面的kubelet启动文件可以看到kubelet启动需要一个config.yaml 我们把mater主机上的kubelet的config.yaml拷贝到node3,放到默认的路径 /var/lib/kubelet 下 (如果你的主机master初始化时使用的镜像为阿里云的国内镜像那么需要把主机master的/var/lib/kubelet/kubeadm-flags.env同时拷贝到node3的/var/lib/kubelet 下)
root@master:~# ssh node3 mkdir /var/lib/kubelet
root@master:~# scp /var/lib/kubelet/config.yaml node3:/var/lib/kubelet/config.yaml
config.yaml 100% 861 727.6KB/s 00:00
root@master:~# scp /var/lib/kubelet/kubeadm-flags.env node3:/var/lib/kubelet/kubeadm-flags.env
kubeadm-flags.env 100% 122 124.3KB/s 00:00
然后启动kubelet,启动kubelet时,如果via指定的文件–kubeconfig不存在,–bootstrap-kubeconfig则使用via指定的引导程序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上的相应RotateKubeletClientCertificate和 RotateKubeletServerCertificate功能标志启用,并且默认情况下启用。
RotateKubeletClientCertificate通过在现有凭据过期后创建新的CSR来使kubelet旋转其客户端证书。要启用此功能,请将以下标志传递给kubelet:
--rotate-certificates
RotateKubeletServerCertificate导致kubelet都以自举其客户端凭证后,请求服务的证书和旋转该证书。要启用此功能,请将以下标志传递给kubelet:
--rotate-server-certificates
注意:出于安全原因,在核心Kubernetes中实现的CSR批准控制器不批准节点服务证书。要使用
操作员,需要运行自定义批准控制器,或手动批准提供服务的证书请求。RotateKubeletServerCertificate
其他认证组件
本文档中描述的所有TLS引导都与kubelet有关。但是,其他组件可能需要直接与kube-apiserver通信。值得注意的是kube-proxy,它是Kubernetes控制平面的一部分,在每个节点上运行,但可能还包括其他组件,例如监视或网络。
像kubelet一样,这些其他组件也需要向kube-apiserver进行身份验证的方法。您可以使用几种方法来生成这些凭据:
- 旧方法:以与TLS引导之前对kubelet相同的方式创建和分发证书
- DaemonSet:由于kubelet本身已加载到每个节点上,并且足以启动基本服务,因此您可以将kube-proxy和其他特定于节点的服务运行为独立进程,而不是作为kube-system名称空间中的daemonset运行。由于它将在集群中,因此您可以为其分配一个具有适当权限的适当服务帐户以执行其活动。这可能是配置此类服务的最简单方法。
Kubectl批准
可以在控制器管理员内置的批准流程之外批准CSR。
签名控制器不会立即签名所有证书请求。相反,它将等待,直到有适当特权的用户将其标记为“已批准”状态为止。此流程旨在允许由外部批准控制器或核心控制器管理器中实现的批准控制器处理自动批准。但是,集群管理员还可以使用kubectl手动批准证书请求。管理员可以使用列出CSR,kubectl get csr并使用进行详细描述kubectl describe csr 。管理员可以批准或拒绝与CSRkubectl certificate approve 和kubectl certificate deny 。