从图中可以看到,Kubernetes 控制平面中包含了 etctd,kube-api-server,kube-scheduler,kube-controller-manager 等组件,这些组件会相互进行远程调用,例如 kube-api-server 会调用 etcd 接口存储数据,kube-controller-manager 会调用 kube-api-server 接口查询集群中的对象状态;同时,kube-api-server 也会和在工作节点上的 kubelet 和 kube-proxy 进行通信,以在工作节点上部署和管理应用。
Kubernetes 中使用到的主要证书
上图中使用序号对证书进行了标注。图中的箭头表明了组件的调用方向,箭头所指方向为服务提供方,另一头为服务调用方。为了实现 TLS 双向认证,服务提供方需要使用一个服务器证书,服务调用方则需要提供一个客户端证书,并且双方都需要使用一个 CA 证书来验证对方提供的证书。为了简明起见,上图中只标注了证书使用方提供的证书,并没有标注证书的验证方验证使用的 CA 证书。图中标注的这些证书的作用分别如下:
通过这张图,对证书机制比较了解的读者可能已经看出,我们其实可以使用多个不同的 CA 来颁发这些证书。只要在通信的组件中正确配置用于验证对方证书的 CA 根证书,就可以使用不同的 CA 来颁发不同用途的证书。但我们一般建议采用统一的 CA 来颁发 kubernetes 集群中的所有证书,这是因为采用一个集群根 CA 的方式比采用多个 CA 的方式更容易管理,可以避免多个CA 导致的复杂的证书配置、更新等问题,减少由于证书配置错误导致的集群故障。
对于 kubeadm 用户:
kube-etcd
、kube-etcd-peer
和 kube-etcd-healthcheck-client
证书。1、tls证书
默认 CN | 建议的密钥路径 | 建议的证书路径 | 命令 | 密钥参数 | 证书参数 |
etcd-ca | etcd/ca.key | etcd/ca.crt | kube-apiserver | --etcd-cafile | |
kube-apiserver-etcd-client | apiserver-etcd-client.key | apiserver-etcd-client.crt | kube-apiserver | --etcd-keyfile | --etcd-certfile |
kubernetes-ca | ca.key | ca.crt | kube-apiserver | --client-ca-file | |
kubernetes-ca | ca.key | ca.crt | kube-controller-manager | --cluster-signing-key-file | --client-ca-file, --root-ca-file, --cluster-signing-cert-file |
kube-apiserver | apiserver.key | apiserver.crt | kube-apiserver | --tls-private-key-file | --tls-cert-file |
kube-apiserver-kubelet-client | apiserver-kubelet-client.key | apiserver-kubelet-client.crt | kube-apiserver | --kubelet-client-key | --kubelet-client-certificate |
front-proxy-ca | front-proxy-ca.key | front-proxy-ca.crt | kube-apiserver | --requestheader-client-ca-file | |
front-proxy-ca | front-proxy-ca.key | front-proxy-ca.crt | kube-controller-manager | --requestheader-client-ca-file | |
front-proxy-client | front-proxy-client.key | front-proxy-client.crt | kube-apiserver | --proxy-client-key-file | --proxy-client-cert-file |
etcd-ca | etcd/ca.key | etcd/ca.crt | etcd | --trusted-ca-file, --peer-trusted-ca-file | |
kube-etcd | etcd/server.key | etcd/server.crt | etcd | --key-file | --cert-file |
kube-etcd-peer | etcd/peer.key | etcd/peer.crt | etcd | --peer-key-file | --peer-cert-file |
etcd-ca | etcd/ca.crt | etcdctl | --cacert | ||
kube-etcd-healthcheck-client | etcd/healthcheck-client.key | etcd/healthcheck-client.crt | etcdctl | --key | --cert |
私钥路径 | 公钥路径 | 命令 | 参数 |
sa.key | kube-controller-manager | --service-account-private-key-file | |
sa.pub | kube-apiserver | --service-account-key-file |
为用户帐户配置证书
文件名 | 凭据名称 | 默认 CN | O (位于 Subject 中) |
admin.conf | default-admin | kubernetes-admin | system:masters |
kubelet.conf | default-auth | system:node: |
system:nodes |
controller-manager.conf | default-controller-manager | system:kube-controller-manager | |
scheduler.conf | default-scheduler | system:kube-scheduler |
生成用户证书的配置命令
1.对于每个配置,请都使用给定的 CN 和 O 生成 x509 证书/密钥偶对。
2.为每个配置运行下面的 kubectl
命令:
KUBECONFIG= kubectl config set-cluster default-cluster --server=https://:6443 --certificate-authority --embed-certs
KUBECONFIG= kubectl config set-credentials --client-key .pem --client-certificate .pem --embed-certs
KUBECONFIG= kubectl config set-context default-system --cluster default-cluster --user
KUBECONFIG= kubectl config use-context default-system
1.ETCD证书配置
需要在 etcd 的启动命令行中配置以下证书相关参数:
/usr/local/bin/etcd \\
--cert-file=/etc/etcd/kube-etcd.pem \\ # 对外提供服务的服务器证书
--key-file=/etc/etcd/kube-etcd-key.pem \\ # 服务器证书对应的私钥
--peer-cert-file=/etc/etcd/kube-etcd-peer.pem \\ # peer 证书,用于 etcd 节点之间的相互访问
--peer-key-file=/etc/etcd/kube-etcd-peer-key.pem \\ # peer 证书对应的私钥
--trusted-ca-file=/etc/etcd/cluster-root-ca.pem \\ # 用于验证访问 etcd 服务器的客户端证书的 CA 根证书
--peer-trusted-ca-file=/etc/etcd/cluster-root-ca.pem\\ # 用于验证 peer 证书的 CA 根证书
...
本人的二进制安装的集群中的配置如下
name: 'k8s-master1'
data-dir: /var/lib/etcd
wal-dir: /var/lib/etcd/wal
snapshot-count: 5000
heartbeat-interval: 100
election-timeout: 1000
quota-backend-bytes: 0
listen-peer-urls: 'https://10.10.20.201:2380'
listen-client-urls: 'https://10.10.20.201:2379,http://127.0.0.1:2379'
max-snapshots: 3
max-wals: 5
cors:
initial-advertise-peer-urls: 'https://10.10.20.201:2380'
advertise-client-urls: 'https://10.10.20.201:2379'
discovery:
discovery-fallback: 'proxy'
discovery-proxy:
discovery-srv:
initial-cluster: 'k8s-master1=https://10.10.20.201:2380,k8s-master2=https://10.10.20.202:2380,k8s-master3=https://10.10.20.203:2380'
initial-cluster-token: 'etcd-k8s-cluster'
initial-cluster-state: 'new'
strict-reconfig-check: false
enable-v2: true
enable-pprof: true
proxy: 'off'
proxy-failure-wait: 5000
proxy-refresh-interval: 30000
proxy-dial-timeout: 1000
proxy-write-timeout: 5000
proxy-read-timeout: 0
client-transport-security:
cert-file: '/etc/kubernetes/pki/etcd/etcd.pem' # 对外提供服务的服务器证书
key-file: '/etc/kubernetes/pki/etcd/etcd-key.pem' # 服务器证书对应的私钥
client-cert-auth: true
trusted-ca-file: '/etc/kubernetes/pki/etcd/etcd-ca.pem' # 用于验证访问 etcd 服务器的客户端证书的 CA 根证书
auto-tls: true
peer-transport-security:
cert-file: '/etc/kubernetes/pki/etcd/etcd.pem' # peer 证书,用于 etcd 节点之间的相互访问
key-file: '/etc/kubernetes/pki/etcd/etcd-key.pem' # peer 证书对应的私钥
peer-client-cert-auth: true
trusted-ca-file: '/etc/kubernetes/pki/etcd/etcd-ca.pem' # 用于验证 peer 证书的 CA 根证书
auto-tls: true
debug: false
log-package-levels:
log-outputs: [default]
force-new-cluster: false
2kube-apiserver 证书配置
需要在 kube-apiserver 中配置以下证书相关参数:
/usr/local/bin/kube-apiserver \\
--tls-cert-file=/var/lib/kubernetes/kube-apiserver.pem \\ # 用于对外提供服务的服务器证书
--tls-private-key-file=/var/lib/kubernetes/kube-apiserver-key.pem \\ # 服务器证书对应的私钥
--etcd-certfile=/var/lib/kubernetes/kube-apiserver-etcd-client.pem \\ # 用于访问 etcd 的客户端证书
--etcd-keyfile=/var/lib/kubernetes/kube-apiserver-etcd-client-key.pem \\ # 用于访问 etcd 的客户端证书的私钥
--kubelet-client-certificate=/var/lib/kubernetes/kube-apiserver-kubelet-client.pem \\ # 用于访问 kubelet 的客户端证书
--kubelet-client-key=/var/lib/kubernetes/kube-apiserver-kubelet-client-key.pem \\ # 用于访问 kubelet 的客户端证书的私钥
--client-ca-file=/var/lib/kubernetes/cluster-root-ca.pem \\ # 用于验证访问 kube-apiserver 的客户端的证书的 CA 根证书
--etcd-cafile=/var/lib/kubernetes/cluster-root-ca.pem \\ # 用于验证 etcd 服务器证书的 CA 根证书
--kubelet-certificate-authority=/var/lib/kubernetes/cluster-root-ca.pem \\ # 用于验证 kubelet 服务器证书的 CA 根证书
--service-account-key-file=/var/lib/kubernetes/service-account.pem \\ # 用于验证 service account token 的公钥
...
本人的二进制安装的集群中的配置如下
[Unit]
Description=Kubernetes API Server
Documentation=https://github.com/kubernetes/kubernetes
After=network.target
[Service]
ExecStart=/usr/local/bin/kube-apiserver \
--v=2 \
--logtostderr=true \
--allow-privileged=true \
--bind-address=0.0.0.0 \
--secure-port=6443 \
--insecure-port=0 \
--advertise-address=10.10.20.201 \
--service-cluster-ip-range=10.96.0.0/12 \
--service-node-port-range=30000-32767 \
--etcd-servers=https://10.10.20.201:2379,https://10.10.20.202:2379,https://10.10.20.203:2379 \
--etcd-cafile=/etc/etcd/ssl/etcd-ca.pem \ # 用于验证 etcd 服务器证书的 CA 根证书
--etcd-certfile=/etc/etcd/ssl/etcd.pem \ # 用于访问 etcd 的客户端证书
--etcd-keyfile=/etc/etcd/ssl/etcd-key.pem \ # 用于访问 etcd 的客户端证书的私钥
--client-ca-file=/etc/kubernetes/pki/ca.pem \ # 用于验证访问 kube-apiserver 的客户端的证书的 CA 根证书
--tls-cert-file=/etc/kubernetes/pki/apiserver.pem \ # 用于对外提供服务的服务器证书
--tls-private-key-file=/etc/kubernetes/pki/apiserver-key.pem \ # 服务器证书对应的私钥
--kubelet-client-certificate=/etc/kubernetes/pki/apiserver.pem \ # 用于访问kubelet 的客户端证书
--kubelet-client-key=/etc/kubernetes/pki/apiserver-key.pem \ #用于访问 kubelet 的客户端证书的私钥
--service-account-key-file=/etc/kubernetes/pki/sa.pub \ # 用于验证 service account token 的公钥
--service-account-signing-key-file=/etc/kubernetes/pki/sa.key \ # 用于验证 service account token 的私钥
--service-account-issuer=https://kubernetes.default.svc.cluster.local \
--kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname \
--enable-admission-plugins=NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,NodeRestriction,ResourceQuota \
--authorization-mode=Node,RBAC \
--enable-bootstrap-token-auth=true \
--requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.pem \ #聚合证书签署客户端证书文件的 CA 证书
--proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.pem \ #聚合证书签名的客户端证书文件
--proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client-key.pem \ #聚合证书指定私钥文件
--requestheader-allowed-names=aggregator \ #向扩展 apiserver 指示任何CN是aggregator
--requestheader-group-headers=X-Remote-Group \ # 标明用来保存 group 的头部
--requestheader-extra-headers-prefix=X-Remote-Extra- \ #标明用来保存拓展信息前缀的头部
--requestheader-username-headers=X-Remote-User #在签署的客户证书中有效的公用名(CN),标明用来保存拓展信息前缀的头部
# --token-auth-file=/etc/kubernetes/token.csv
Restart=on-failure
RestartSec=10s
LimitNOFILE=65535
[Install]
WantedBy=multi-user.target
Kubernetes 中的各个组件,包括kube-controller-mananger、kube-scheduler、kube-proxy、kubelet等,采用一个kubeconfig 文件中配置的信息来访问 kube-apiserver。该文件中包含了 kube-apiserver 的地址,验证 kube-apiserver 服务器证书的 CA 证书,自己的客户端证书和私钥等访问信息。
在一个使用 minikube 安装的集群中,生成的 kubeconfig 配置文件如下所示,这四个文件分别为 admin 用户, kube-controller-mananger、kubelet 和 kube-scheduler 的kubeconfig配置文件。
$ ls /etc/kubernetes/ admin.conf controller-manager.conf kubelet.conf scheduler.conf
[Unit]
Description=Kubernetes Scheduler
Documentation=https://github.com/kubernetes/kubernetes
After=network.target
[Service]
ExecStart=/usr/local/bin/kube-scheduler \
--v=2 \
--logtostderr=true \
--address=127.0.0.1 \
--leader-elect=true \
--kubeconfig=/etc/kubernetes/scheduler.kubeconfig
Restart=always
RestartSec=10s
[Install]
WantedBy=multi-user.target
[Unit]
Description=Kubernetes Controller Manager
Documentation=https://github.com/kubernetes/kubernetes
After=network.target
[Service]
ExecStart=/usr/local/bin/kube-controller-manager \
--v=2 \
--logtostderr=true \
--address=127.0.0.1 \
--root-ca-file=/etc/kubernetes/pki/ca.pem \
--cluster-signing-cert-file=/etc/kubernetes/pki/ca.pem \
--cluster-signing-key-file=/etc/kubernetes/pki/ca-key.pem \
--service-account-private-key-file=/etc/kubernetes/pki/sa.key \
--kubeconfig=/etc/kubernetes/controller-manager.kubeconfig \
--leader-elect=true \
--use-service-account-credentials=true \
--node-monitor-grace-period=40s \
--node-monitor-period=5s \
--pod-eviction-timeout=2m0s \
--controllers=*,bootstrapsigner,tokencleaner \
--allocate-node-cidrs=true \
--cluster-cidr=172.16.0.0/12 \
--requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.pem \ #聚合证书签署客户端证书文件的 CA 证书
--node-cidr-mask-size=24
Restart=always
RestartSec=10s
[Install]
WantedBy=multi-user.target
kube-proxy的配置文件
apiVersion: kubeproxy.config.k8s.io/v1alpha1
bindAddress: 0.0.0.0
clientConnection:
acceptContentTypes: ""
burst: 10
contentType: application/vnd.kubernetes.protobuf
kubeconfig: /etc/kubernetes/kube-proxy.kubeconfig
qps: 5
clusterCIDR: 10.244.0.0/16
configSyncPeriod: 15m0s
conntrack:
max: null
maxPerCore: 32768
min: 131072
tcpCloseWaitTimeout: 1h0m0s
tcpEstablishedTimeout: 24h0m0s
enableProfiling: false
healthzBindAddress: 0.0.0.0:10256
hostnameOverride: ""
iptables:
masqueradeAll: false
masqueradeBit: 14
minSyncPeriod: 0s
syncPeriod: 30s
ipvs:
masqueradeAll: true
minSyncPeriod: 5s
scheduler: "rr"
syncPeriod: 30s
kind: KubeProxyConfiguration
metricsBindAddress: 127.0.0.1:10249
mode: "ipvs"
nodePortAddresses: null
oomScoreAdj: -999
portRange: ""
udpIdleTimeout: 250ms
Kubernetes 中有两类用户,一类为 user account,一类为 service account。service account 主要被 pod 用于访问 kube-apiserver。在为一个 pod 指定了 service account 后,kubernetes 会为该 service account 生成一个 JWT token,并使用 secret 将该 service account token 加载到 pod 上。pod 中的应用可以使用 service account token 来访问 api server。service account 证书被用于生成和验证 service account token。该证书的用法和前面介绍的其他证书不同,因为实际上使用的是其公钥和私钥,而并不需要对证书进行验证。
我们可以看到 service account 证书的公钥和私钥分别被配置到了 kube-apiserver 和 kube-controller-manager 的命令行参数中,如下所示:
/usr/local/bin/kube-apiserver \\
--service-account-key-file=/var/lib/kubernetes/service-account.pem \\ # 用于验证 service account token 的公钥
...
/usr/local/bin/kube-controller-manager \\
--service-account-private-key-file=/var/lib/kubernetes/service-account-key.pem # 用于对 service account token 进行签名的私钥
...
下图展示了 kubernetes 中生成、使用和验证 service account token 的过程。
认证方法:客户端证书还是 token ?
从前面的介绍中我们可以看到,Kubernetes 提供了两种客户端认证的方法,控制面组件采用的是客户端数字证书;而在集群中部署的应用则采用了 service account token 的方式。为什么 Kubernetes 不为 service account 也生成一个证书,并采用该证书进行身份认证呢?实际上 Istio 就是这样做的,Istio 会自动为每个 service account 生成一个证书,并使用该证书来在 pod 中的应用之间建立双向 tls 认证。我没有找到 Kubernetes 这个设计决策的相关说明,如果你知道原因,欢迎和我联系探讨。
Kubernetes 提供了一个 certificates.k8s.io
API,可以使用配置的 CA 根证书来签发用户证书。该 API 由 kube-controller-manager 实现,其签发证书使用的根证书在下面的命令行中进行配置。我们希望 Kubernetes 采用集群根 CA 来签发用户证书,因此在 kube-controller-manager 的命令行参数中将相关参数配置为了集群根 CA。
/usr/local/bin/kube-controller-manager \\
--cluster-signing-cert-file=/var/lib/kubernetes/cluster-root-ca.pem # 用于签发证书的 CA 根证书
--cluster-signing-key-file=/var/lib/kubernetes/cluster-root-ca-key.pem # 用于签发证书的 CA 根证书的私钥
...
在安装 Kubernetes 时,我们需要为每一个工作节点上的 Kubelet 分别生成一个证书。由于工作节点可能很多,手动生成 Kubelet 证书的过程会比较繁琐。为了解决这个问题,Kubernetes 提供了 TLS bootstrapping (TLS 启动引导 | Kubernetes) 的方式来简化 Kubelet 证书的生成过程。其原理是预先提供一个 bootstrapping token,kubelet 通过该 kubelet 调用 kube-apiserver 的证书签发 API 来生成 自己需要的证书。要启用该功能,需要在 kube-apiserver 中启用 --enable-bootstrap-token-auth
,并创建一个 kubelet 访问 kube-apiserver 使用的 bootstrap token secret。如果使用 kubeadmin 安装,可以使用 kubeadm token create
命令来创建 token。
采用TLS bootstrapping 生成证书的流程如下:
--bootstrap-kubeconfig
启动参数将 bootstrap token 传递给 kubelet 进程。参考文档
1:一文带你彻底厘清 Kubernetes 中的证书工作机制-赵化冰的博客 | Zhaohuabing Blog
2.PKI 证书和要求 | Kubernetes