kubernetes 的证书
了解证书的基本原理之后,接下去我们了解 kubernetes 的证书情况。在这之前,对 kubernetes 证书中使用比较多、比较直观的就是 kubeconfig 文件。在这个文件中,可以看到配置了好几个证书,但是不清楚这些证书的作用。然后去翻翻核心组件中,发现启动参数中,又配置了好多证书。接着继续看看 pki 目录,发现又有很多证书,但是对这些证书的情况并不是特别了解,主要有以下几个疑问:
- kubernetes 的证书都有哪些?
- 核心组件启动参数中的那些证书配置,到底是什么作用?
- kubeconfig 文件中的内容有哪些?配置的证书到底是什么?
- 在使用 kubeconfig 访问 apiserver 的情况下,修改了 apiserver 的地址,这个地址是可以 ping 通的。但是,使用改了 IP 地址之后的 kubeconfig 访问时会访问不了,会告诉你
tls: failed to verify certificate: x509: certificate is valid for 10.32.0.1, ********, 172.30.172.96, not 127.0.0.1
,这个是怎么回事? - kubernetes 是怎么通过证书就知道使用该证书的用户是谁?从而赋予相应的权限。很显然是证书中的某个字段,带了相应的用户信息,那么是哪个字段呢?
证书列表
在介绍证书之前,先了解一个前提条件:Kubernetes 为了安全性,都是采用双向认证的。双向认证和单向认证的区别如下:
- 服务器单向认证:只需要服务器端提供证书,客户端通过服务器端证书验证服务的身份,但服务器并不验证客户端的身份。这种情况一般适用于对 Internet 开放的服务,例如搜索引擎网站,任何客户端都可以连接到服务器上进行访问,但客户端需要验证服务器的身份,以避免连接到伪造的恶意服务器。
- 双向 TLS 认证:除了客户端需要验证服务器的证书,服务器也要通过客户端证书验证客户端的身份。这种情况下服务器提供的是敏感信息,只允许特定身份的客户端访问。
接下去我们继续,在使用 kubeadm 安装 kubernetes 的时候会自动生成集群所需要的证书,生成的证书通常位于 /etc/kubernetes/pki/
目录。下面是 kubeadm 安装完集群之后生成的证书:
$ tree /etc/kubernetes/pki/
/etc/kubernetes/pki/
├── apiserver.crt
├── apiserver-etcd-client.crt
├── apiserver-etcd-client.key
├── apiserver.key
├── apiserver-kubelet-client.crt
├── apiserver-kubelet-client.key
├── ca.crt
├── ca.key
├── etcd
│ ├── ca.crt
│ ├── ca.key
│ ├── healthcheck-client.crt
│ ├── healthcheck-client.key
│ ├── peer.crt
│ ├── peer.key
│ ├── server.crt
│ └── server.key
├── front-proxy-ca.crt
├── front-proxy-ca.key
├── front-proxy-client.crt
├── front-proxy-client.key
├── sa.key
└── sa.pub
-
ca.crt 和 ca.key 这两个是签发 kubernetes 中其他证书的 CA(certification authority) 证书和私钥,相当于认证机构的根证书和私钥,是 kubernetes 中的主 CA。
-
apiserver-etcd-client.crt 和 apiserver-etcd-client.key ,这两个是 kube-apiserver 访问 etcd 时使用的客户端证书。其中 apiserver-etcd-client.crt 是由 etcd 的 CA 证书签发的。
-
apiserver.crt 和 apiserver.key 是 kube-apiserver 对外提供服务的服务端证书及私钥,由上述的 CA 的证书签发。如果按照我们之前讲述的话,上述的 ca.crt 是证书机构的根证书,apiserver.crt 则是证书机构签发的证书。
-
apiserver-kubelet-client.crt 和 apiserver-kubelet-client.key 是 kube-apiserver 访问 kubelet 时需要的客户端证书及私钥。
-
front-proxy-ca.crt、front-proxy-ca.key、front-proxy-client.crt 和 front-proxy-client.key 分别是聚合层(aggregator)的 CA 根证书及私钥(用于验证请求的客户端证书是否可信),访问聚合层的客户端证书及私钥。
当 API Server 需要与聚合层的 API 服务器通信时,它会通过前端代理发送请求。这时,聚合层的 API 服务器需要验证发起请求的 API Server 是否可信。这个验证过程就会用到
front-proxy-ca.crt
。因此,访问聚合层的客户端证书需要由该 CA 根证书签名。 -
etcd/ca.crt 和 etcd/ca.key 这两个签发 etcd 相关证书的 CA 证书和私钥。
-
etcd/server.crt 和 etcd/server.key 这两个是 etcd 对外服务的服务器证书和私钥。
-
etcd/peer.crt 和 etcd/peer.key 这两个是 etcd 节点之间相互进行认证的证书和私钥。
-
etcd/healthcheck-client.crt 和 etcd/healthcheck-client.key 这两个是 etcd 访问其他服务的客户端证书和私钥。
使用下述命令分别查看相应证书的 Issuer 和 Subject 来查看相应的签名关系,
openssl x509 -in /etc/kubernetes/pki/apiserver.crt -text
-
ca.crt 的 Issuer 和 Subject 都是 kubernetes,因为它是认证机构的根证书,所以是自签名的。
Issuer: CN=kubernetes Subject: CN=kubernetes
-
apiserver-etcd-client.crt 的 Issuer 是 etcd-ca
Issuer: CN=etcd-ca Subject: O=system:masters, CN=kube-apiserver-etcd-client
-
apiserver.crt 的 Issuer 是 kubernetes
Issuer: CN=kubernetes Subject: CN=kube-apiserver
-
apiserver-kubelet-client.crt 的 Issuer 是 kubernetes
Issuer: CN=kubernetes Subject: O=system:masters, CN=kube-apiserver-kubelet-client
-
front-proxy-ca.crt 的 Issuer 和 Subject 都是 front-proxy-ca,因为它是认证机构的根证书,所以是自签名的
Issuer: CN=front-proxy-ca Subject: CN=front-proxy-ca
-
front-proxy-client.crt 的 Issuer 是 front-proxy-ca
Issuer: CN=front-proxy-ca Subject: CN=front-proxy-client
-
etcd/ca.crt 的 Issuer 和 Subject 都是 etcd-ca
Issuer: CN=etcd-ca Subject: CN=etcd-ca
-
etcd/server.crt 的 Issuer 是 etcd-ca,Subject 是节点名
Issuer: CN=etcd-ca Subject: CN=izbp1bndrgrf******z
-
etcd/peer.crt 的 Issuer 是 etcd-ca,Subject 是节点名
Issuer: CN=etcd-ca Subject: CN=izbp1bndrgrf******z
-
etcd/healthcheck-client.crt 的 Issuer 是 etcd-ca
Issuer: CN=etcd-ca Subject: O=system:masters, CN=kube-etcd-healthcheck-client
各组件配置的证书情况
Etcd
containers:
- command:
- etcd
# etcd 对外提供服务的服务器证书
- --cert-file=/etc/kubernetes/pki/etcd/server.crt
# etcd 服务器证书对应的私钥
- --key-file=/etc/kubernetes/pki/etcd/server.key
# peer 证书,用于 etcd 节点之间的相互访问
- --peer-cert-file=/etc/kubernetes/pki/etcd/peer.crt
# peer 证书对应的私钥
- --peer-key-file=/etc/kubernetes/pki/etcd/peer.key
# 用于验证 peer 证书的 CA 根证书,上述 peer 证书都是由该证书签发
- --peer-trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt
# 用于验证访问 etcd 服务器的客户端证书的 CA 根证书,因此访问 etcd 的客户端证书都需要由该 CA 根证书签名。
# 在 kubernetes 中,访问 etcd 的只有 apiserver,因此上述提到的 apiserver-etcd-client.crt 证书是由 etcd 的 CA 根证书签名的。
- --trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt
kube-apiserver
containers:
- command:
- kube-apiserver
# kube-apiserver 对外提供服务的服务器证书
- --tls-cert-file=/etc/kubernetes/pki/apiserver.crt
# 服务器证书对应的私钥
- --tls-private-key-file=/etc/kubernetes/pki/apiserver.key
# 用于访问 kubelet 的客户端证书
- --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt
# 用于访问 kubelet 的客户端证书的私钥
- --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key
# 用于访问 etcd 的客户端证书
- --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt
# 用于访问 etcd 的客户端证书对应的私钥
- --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key
# 用于验证访问 kube-apiserver 的客户端的证书的 CA 根证书,因此访问 kube-apiserver 的客户端证书需要由该证书签名。如果采用其他 CA 证书签名的话是没有用的,只能使用 kubernetes 主 CA 根证书签名的证书才有用。
- --client-ca-file=/etc/kubernetes/pki/ca.crt
# 用于验证 etcd 服务器证书的 CA 根证书
- --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt
# 用于访问聚合层的客户端证书
- --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt
- --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key
# 聚合层用于验证访问自己的客户端的证书是否可信,因此访问聚合层的客户端证书需要由该证书签名。
- --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
# 用于验证 service account token 的公钥
- --service-account-key-file=/etc/kubernetes/pki/sa.pub
- --service-account-signing-key-file=/etc/kubernetes/pki/sa.key
kube-controller-manager
containers:
- command:
- kube-controller-manager
# 访问 kube-apiserver 时使用的 kubeconfig 文件。
- --kubeconfig=/etc/kubernetes/controller-manager.conf
# kubeconfig 中使用的用户,需要具有创建 tokenreviews.authentication.k8s.io 的权限。
- --authentication-kubeconfig=/etc/kubernetes/controller-manager.conf
# 同上,具有创建 subjectaccessreviews.authorization.k8s.io 的权限。
- --authorization-kubeconfig=/etc/kubernetes/controller-manager.conf
# 用于验证访问 kube-controller-manager 的客户端的证书。
- --client-ca-file=/etc/kubernetes/pki/ca.crt
# 为其他组件签发证书时需要使用的 CA 证书和私钥。
- --cluster-signing-cert-file=/etc/kubernetes/pki/ca.crt
- --cluster-signing-key-file=/etc/kubernetes/pki/ca.key
# 将这个证书作为集群中 Pod Service Account 的 ca.crt 字段的来源,为 Pod 内运行的服务提供验证 kube-apiserver 的证书。
- --root-ca-file=/etc/kubernetes/pki/ca.crt
# 对 service account token 进行加密的私钥文件的路径。kube-controller-manager 使用这个私钥生成相应 service account token,Pod 访问 kube-apiserver 的时候会使用这些 token,然后这些 token 会被 kube-apiserver 验证,由于 kube-apiserver 启动的时候指定了相应的公钥,因此它可以进行验证。
- --service-account-private-key-file=/etc/kubernetes/pki/sa.key
通过上述的配置,我们可以发现在访问 kube-apiserver 的时候,kube-controller-manager 使用的是 kubeconfig 文件中配置的信息,而不是直接使用某个证书。其实,在 kubernetes 中 kube-controller-mananger、kube-scheduler、kubelet 等组件,都会只配置一个kubeconfig 文件来访问 kube-apiserver,文件中包含了 kube-apiserver 的地址,验证 kube-apiserver 服务器证书的 CA 证书,自己的客户端证书和私钥等访问信息。关于 kubeconfig,我们会抽出来单独介绍。
SubjectAccessReview
(subjectaccessreviews.authorization.k8s.io)和TokenReview
(tokenreviews.authentication.k8s.io)是两种特殊的 API 资源。它们不是由 Kubernetes 控制器或核心组件主动创建的,而是由集群管理员、开发者或第三方应用程序在需要进行认证或授权检查时创建。
- subjectaccessreviews 是查询某个用户或 service account 是否有相应的权限,也就是某个用户或 service account 是否有权限执行特定的操作(如创建、查看、删除资源等)。详细见 https://kubernetes.io/zh-cn/docs/reference/kubernetes-api/authorization-resources/subject-access-review-v1/。
- tokenreviews 是对某个 token 进行身份验证,返回令牌是否有效,以及有效令牌所关联的用户信息,为身份验证决策提供依据。详细见https://kubernetes.io/zh-cn/docs/reference/kubernetes-api/authentication-resources/token-review-v1/。
kube-scheduler
kube-scheduler 和 kube-controller-manager 类似,都会配置了一个 kubeconfig 文件。
containers:
- command:
- kube-scheduler
- --authentication-kubeconfig=/etc/kubernetes/scheduler.conf
- --authorization-kubeconfig=/etc/kubernetes/scheduler.conf
- --bind-address=127.0.0.1
- --kubeconfig=/etc/kubernetes/scheduler.conf
- --leader-elect=true
kubelet
我们通过 systemctl cat kubelet
命令可以查看 kubelet.service 及其所有相关 drop-in 文件的内容,如下所示。可以看到 kubelet 也使用了 kubeconfig 文件,一个是 bootstrap-kubelet.conf,另一个是 kubelet.conf 文件。
# /usr/lib/systemd/system/kubelet.service
[Unit]
Description=kubelet: The Kubernetes Node Agent
Documentation=https://kubernetes.io/docs/
Wants=network-online.target
After=network-online.target
[Service]
ExecStart=/usr/bin/kubelet
Restart=always
StartLimitInterval=0
RestartSec=10
[Install]
WantedBy=multi-user.target
# /usr/lib/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/sysconfig/kubelet
ExecStart=
ExecStart=/usr/bin/kubelet $KUBELET_KUBECONFIG_ARGS $KUBELET_CONFIG_ARGS $KUBELET_KUBEADM_ARGS $KUBELET_EXTRA_ARGS --node-ip=********
kubelet.service 和 kubelet.service.d/10-kubeadm.conf 文件的区别,
kubelet.service 文件是一个 systemd 单元文件,它定义了如何启动 kubelet 服务。这个文件包括一些基本的服务描述、依赖关系和如何执行 kubelet 进程。通常,这个文件不会包含太多的启动参数,因为这些参数可能会根据不同的安装和配置方式而变化。
10-kubeadm.conf 是一个 systemd "drop-in" 配置文件。它位于 kubelet.service.d/ 目录下,这个目录通常用于存放可以补充或覆盖主服务文件 (kubelet.service) 中设置的特定配置。
这种设计允许 kubelet.service 主服务文件保持相对标准化,而所有特定于 kubeadm 的配置都可以通过 drop-in 文件 (10-kubeadm.conf) 来管理。当 systemd 启动 kubelet 服务时,它会合并主服务文件和所有相关的 drop-in 文件,以形成 kubelet 的最终运行配置。
由于 kubeadm 可以通过这些额外的配置来管理 kubelet,因此用户在大多数情况下不需要直接编辑 kubelet.service 文件。任何与 kubeadm 集群管理相关的 kubelet 配置更改应该通过修改 10-kubeadm.conf 或添加新的 drop-in 文件来进行。
kubeconfig
上文提到 Kubernetes 中的各个组件,比如 kube-controller-mananger、kube-scheduler、kubelet 等,都依赖 kubeconfig 文件来访问 kube-apiserver。
kubeconfig 文件格式
kubeconfig 是 Kubernetes 的一个配置文件,它包含了访问 Kubernetes 集群所需的信息,主要由以下三部分组成。
- 集群(clusters):定义了要连接的 Kubernetes 集群的信息,包括集群的名称、服务器的地址和验证服务器证书的 CA 证书。
- 用户(users):定义了用户的认证信息,包括用户的名称、客户端的证书和密钥。
- 上下文(contexts):定义了用户和集群之间的关系。一个上下文包含了一个用户和一个集群,以及用户在该集群中的默认命名空间。通过在 kubeconfig 文件中定义不同的上下文,可以轻松地在不同的 Kubernetes 集群和命名空间之间切换。
kubeconfig 文件举例
上文已经提到了较多的 kubeconfig 文件,下面我们拿 /etc/kubernetes/config
中的 admin.conf
文件为例进行讲解(其中具体的证书内容和私钥由相应的 <>
替代了)。
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: <ca.crt + base64>
server: https://***.***.***.***:***
name: kubernetes
contexts:
- context:
cluster: kubernetes
user: kubernetes-admin
name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes
kind: Config
preferences: {}
users:
- name: kubernetes-admin
user:
client-certificate-data: <client.crt>
client-key-data: <client.key>
-
clusters 中的 certificate-authority-data 是验证服务器证书的 CA 证书 base64 之后的内容。
可以看到 certificate-authority-data 的内容,其实就是 kubernetes 主证书的内容。
# 1. 首先将 certificate-authority-data 的内容使用 base64 进行解码 $ echo <ca.crt + base64> | base64 -d > k-ca.crt # 2. 使用 diff 进行比较,是完全一样的 $ diff k-ca.crt /etc/kubernetes/pki/ca.crt # 3. 查看 k-ca.crt 的内容 $ openssl x509 -in k-ca.crt -text Issuer: CN=kubernetes Subject: CN=kubernetes
-
users 中的 client-certificate-data 是客户端的证书 base64 之后的内容。
可以看到 client-certificate-data 证书的内容中,Issuer 是 kubernetes,也就表明该客户端证书是由 kubernetes 的 CA 证书机构签名的。上文提到 kube-apiserver 的参数 client-ca-file 配置的是验证访问 kube-apiserver 的客户端的证书的 CA 根证书,因此这里客户端的证书也必须由 CA 证书机构签名。
# 1. 首先将 client-certificate-data 的内容使用 base64 进行解码 $ echo <client.crt> | base64 -d > client.crt # 2. 查看 client.crt 的内容 $ openssl x509 -in client.crt -text Issuer: CN=kubernetes Subject: O=system:masters, CN=kubernetes-admin
证书签发
证书签发是指为 kubernetes 的客户端签发相应的证书,客户端访问 kube-apiserver 时使用签发的证书。
通常有两种方式,
-
使用 /etc/kubernetes/pki/ca.key 和 /etc/kubernetes/pki/ca.crt 生成相应的证书,生成的方式可参考之前讲数字证书时提到的方式。
# 生成私钥 $ openssl genrsa -out dawnguo.key 2048 # 生成证书请求 $ openssl req -new -key dawnguo.key -out dawnguo.csr -subj "/CN=dawnguo/O=system:masters" # 对证书请求进行签名,并生成相应的客户端证书 $ openssl x509 -req -in dawnguo.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out dawnguo.crt -days 10000 -extensions v3_ext # 对证书进行 base64 编码 $ cat dawnguo.crt | base64 | tr -d '\n' # 对 key(密钥)进行编码 $ cat dawnguo.key | base64 | tr -d '\n'
-
使用
certificates.k8s.io
API 创建证书,这个本质上其实还是上述的流程,对于我们来说只需要提供一个 csr 就可以了。
可参考以下两个链接,
另外,需要注意的是上述,由于我们使用了 system:master 组(/O 指定的内容),所以这个用户名可以随意,因为 RBAC 主要是根据这个组来进行判断。但是假如新增了一个用户,而这个用户没有任何组的话,我们则需要创建相应的 role 和 rolebinding 资源。
其他问题
解决上述遗留的几个问题,
-
在使用 kubeconfig 访问 apiserver 的情况下,修改了 apiserver 的地址,这个地址是可以 ping 通的。但是,使用改了 IP 地址之后的 kubeconfig 访问时会访问不了,会告诉你
tls: failed to verify certificate: x509: certificate is valid for 10.32.0.1, ********, 172.30.172.96, not 127.0.0.1
,这个是怎么回事?这里需要查看 kube-apiserver 的服务端证书,也就是 /etc/kubernetes/pki/apiserver.crt。该证书的 X509v3 extensions 字段,指定了该服务可正常访问的域名和 IP 地址。
X509v3 extensions: X509v3 Subject Alternative Name: DNS:izbp1bndrgrf54****zf1bz, DNS:kubernetes, DNS:kubernetes.default, DNS:kubernetes.default.svc, DNS:kubernetes.default.svc.cluster.local, IP Address:10.32.0.1, IP Address:********, IP Address:172.30.172.96
-
kubernetes 是怎么通过证书就知道使用该证书的用户是谁?从而赋予相应的权限。很显然是证书中的某个字段,带了相应的用户信息,那么是哪个字段呢?
用户信息由证书中 Subject 字段提供,其中 O 表示用户归属的组,CN 表示用户名。之后 kube-apiserver 通过 RBAC 机制确定该用户拥有的权限,从而限制使用该证书访问时的权限。
相关链接
kube-controller-manager:https://kubernetes.io/zh-cn/docs/reference/command-line-tools-reference/kube-controller-manager/
PKI 证书和要求:https://kubernetes.io/zh-cn/docs/setup/best-practices/certificates/
关于 Kubernetes 证书的那点事:https://cloud.tencent.com/developer/article/1744610
用户认证:https://kubernetes.io/zh-cn/docs/reference/access-authn-authz/authentication/