Kubernetes过一系列机制来实现集群的安全机制,包括API Server的认证、授权、准入控制机制等。集群的安全性必须考虑以下的几个目标:
这里对认证和授权做出详细阐述,重点关注TLS Bootstrapping、证书自动颁发、证书轮换、认证过程的RBAC授权。
以下简单讲解下https数字证书认证的原理。
kubernetes内部常用的加解密算法为非对称加密算法RSA。
数字证书则是由证书认证机构(CA)对证书申请者真实身份验证之后,用CA的根证书对申请人的一些基本信息以及申请人的公钥进行签名(相当于加盖发证书机 构的公章)后形成的一个数字文件。CA完成签发证书后,会将证书发布在CA的证书库(目录服务器)中,任何人都可以查询和下载,因此数字证书和公钥一样是公开的。实际上,数字证书就是经过CA认证过的公钥
SSL双向认证步骤:
有上文我们可以知道,建立完整TLS加密通信,需要有一个CA认证机构,会向客户端下发根证书、服务端证书以及签名私钥给客户端。ca.pem & ca-key.pem & ca.csr组成了一个自签名的CA机构。
ca.pem | CA根证书文件 |
ca-key.pem | 服务端私钥,用于对客户端请求的解密和签名 |
ca.csr | 证书签名请求,用于交叉签名或重新签名 |
证书名称
|
作用
|
---|
该文件为一个用户的描述文件,基本格式为 Token,用户名,UID,用户组
;这个文件在 apiserver 启动时被 apiserver 加载,然后就相当于在集群内创建了一个这个用户;接下来就可以用 RBAC 给他授权
该文件中内置了 token.csv 中用户的 Token,以及 apiserver CA 证书;kubelet 首次启动会加载此文件,使用 apiserver CA 证书建立与 apiserver 的 TLS 通讯,使用其中的用户 Token 作为身份标识像 apiserver 发起 CSR 请求
这是一个软连接文件,当 kubelet 配置了 --feature-gates=RotateKubeletClientCertificate=true
选项后,会在证书总有效期的 70%~90%
的时间内发起续期请求,请求被批准后会生成一个 kubelet-client-时间戳.pem
;kubelet-client-current.pem
文件则始终软连接到最新的真实证书文件,除首次启动外,kubelet 一直会使用这个证书同 apiserver 通讯
同样是一个软连接文件,当 kubelet 配置了 --feature-gates=RotateKubeletServerCertificate=true
选项后,会在证书总有效期的 70%~90%
的时间内发起续期请求,请求被批准后会生成一个 kubelet-server-时间戳.pem
;kubelet-server-current.pem
文件则始终软连接到最新的真实证书文件,该文件将会一直被用于 kubelet 10250 api 端口鉴权
所有客户端的证书首先要经过集群CA的签署,否则不会被集群认可 .
kubectl只是个go编写的可执行程序,只要为kubectl配置合适的kubeconfig,就可以在集群中的任意节点使用 。kubectl的权限为admin,具有访问kubernetes所有api的权限。
ca.pem | CA根证书 |
admin.pem | kubectl的TLS认证证书,具有admin权限 |
admin-key.pem | kubectl的TLS认证私钥 |
证书名称
|
作用
|
---|
--certificate-authority=/etc/kubernetes/ssl/ca.pem
设置了该集群的根证书路径, --embed-certs为true
表示将--certificate-authority
证书写入到kubeconfig中--client-certificate=/etc/kubernetes/ssl/admin.pem
指定kubectl证书--client-key=/etc/kubernetes/ssl/admin-key.pem
指定kubectl私钥ca.pem | CA根证书 |
kubelet-client.crt | kubectl的TLS认证证书 |
kubelet-client.key | kubectl的TLS认证私钥 |
kubelet.crt | 独立于 apiserver CA 的自签 CA |
kubelet.key | 独立于 apiserver CA 的私钥 |
证书名称
|
作用
|
---|
当成功签发证书后,目标节点的 kubelet 会将证书写入到 --cert-dir= 选项指定的目录中;此时如果不做其他设置应当生成上述除ca.pem以外的4个文件
kube-apiserver是我们在部署kubernetes集群是最需要先启动的组件,也是我们和集群交互的核心组件。
以下是kube-apiserver所使用的证书
ca.pem | CA根证书 |
ca-key.pem | CA端私钥 |
kubernetes.pem | kube-apiserver的tls认证证书 |
kubernetes-key.pem | kube-apiserver的tls认证私钥 |
使用的证书
|
证书作用
|
---|
--token-auth-file=/etc/kubernetes/token.csv
指定了token.csv的位置,用于kubelet 组件 第一次启动时没有证书如何连接 apiserver 。 Token 和 apiserver 的 CA 证书被写入了 kubelet 所使用的 bootstrap.kubeconfig 配置文件中;这样在首次请求时,kubelet 使用 bootstrap.kubeconfig 中的 apiserver CA 证书来与 apiserver 建立 TLS 通讯,使用 bootstrap.kubeconfig 中的用户 Token 来向 apiserver 声明自己的 RBAC 授权身份--tls-cert-file=/etc/kubernetes/ssl/kubernetes.pem
指定kube-apiserver证书地址--tls-private-key-file=/etc/kubernetes/ssl/kubernetes-key.pem
指定kube-apiserver私钥地址--client-ca-file=/etc/kubernetes/ssl/ca.pem
指定根证书地址--service-account-key-file=/etc/kubernetes/ssl/ca-key.pem
包含PEM-encoded x509 RSA公钥和私钥的文件路径,用于验证Service Account的token,如果不指定,则使用--tls-private-key-file指定的文件--etcd-cafile=/etc/kubernetes/ssl/ca.pem
到etcd安全连接使用的SSL CA文件--etcd-certfile=/etc/kubernetes/ssl/kubernetes.pem
到etcd安全连接使用的SSL 证书文件--etcd-keyfile=/etc/kubernetes/ssl/kubernetes-key.pem
到etcd安全连接使用的SSL 私钥文件kubelet 发起的 CSR 请求都是由 kube-controller-manager 来做实际签署的,所有使用的证书都是根证书的密钥对 。由于kube-controller-manager是和kube-apiserver部署在同一节点上,且使用非安全端口通信,故不需要证书
ca.pem | CA根证书 |
ca-key.pem | kube-apiserver的tls认证私钥 |
使用的证书
|
证书作用
|
---|
--cluster-signing-cert-file=/etc/kubernetes/ssl/ca.pem
指定签名的CA机构根证书,用来签名为 TLS BootStrap 创建的证书和私钥--cluster-signing-key-file=/etc/kubernetes/ssl/ca-key.pem
指定签名的CA机构私钥,用来签名为 TLS BootStrap 创建的证书和私钥--service-account-private-key-file=/etc/kubernetes/ssl/ca-key.pem
同上--root-ca-file=/etc/kubernetes/ssl/ca.pem
根CA证书文件路径 ,用来对 kube-apiserver 证书进行校验,指定该参数后,才会在Pod 容器的 ServiceAccount 中放置该 CA 证书文件--kubeconfig
kubeconfig配置文件路径,在配置文件中包括Master的地址信息及必要认证信息kube-scheduler是和kube-apiserver一般部署在同一节点上,且使用非安全端口通信,故启动参参数中没有指定证书的参数可选 。 若分离部署,可在kubeconfig文件中指定证书,使用kubeconfig认证,kube-proxy类似
配置示例:
$ # 设置集群参数
$ kubectl config set-cluster kubernetes --certificate-authority=/etc/kubernetes/ssl/ca.pem --embed-certs=true --server=${KUBE_APISERVER} --kubeconfig=kube-proxy.kubeconfig
$ # 设置客户端认证参数
$ kubectl config set-credentials kube-proxy --client-certificate=/etc/kubernetes/ssl/kube-proxy.pem --client-key=/etc/kubernetes/ssl/kube-proxy-key.pem --embed-certs=true --kubeconfig=kube-proxy.kubeconfig
$ # 设置上下文参数
$ kubectl config set-context default --cluster=kubernetes --user=kube-proxy --kubeconfig=kube-proxy.kubeconfig
$ # 设置默认上下文
$ kubectl config use-context default --kubeconfig=kube-proxy.kubeconfig
$ mv kube-proxy.kubeconfig /etc/kubernetes/
每个 Kubernetes 集群都有一个集群根证书颁发机构(CA)。 集群中的组件通常使用 CA 来验证 API server 的证书,由API服务器验证 kubelet 客户端证书等。为了支持这一点,CA 证书包被分发到集群中的每个节点,并作为一个 secret 附加分发到默认 service account 上 。
第一次启动时没有证书如何连接 apiserver ?
这个问题实际上可以去查看一下 bootstrap.kubeconfig
和 token.csv
得到答案: 在 apiserver 配置中指定了一个 token.csv 文件,该文件中是一个预设的用户配置;同时该用户的 Token 和 apiserver 的 CA 证书被写入了 kubelet 所使用的 bootstrap.kubeconfig 配置文件中;这样在首次请求时,kubelet 使用 bootstrap.kubeconfig 中的 apiserver CA 证书来与 apiserver 建立 TLS 通讯,使用 bootstrap.kubeconfig 中的用户 Token 来向 apiserver 声明自己的 RBAC 授权身份,如下图所示
在首次启动时,可能与遇到 kubelet 报 401 无权访问 apiserver 的错误;这是因为在默认情况下,kubelet 通过 bootstrap.kubeconfig 中的预设用户 Token 声明了自己的身份,然后创建 CSR 请求;但是不要忘记这个用户在我们不处理的情况下他没任何权限的,包括创建 CSR 请求;所以需要如下命令创建一个 ClusterRoleBinding,将预设用户 kubelet-bootstrap 与内置的 ClusterRole system:node-bootstrapper 绑定到一起,使其能够发起 CSR 请求
kubectl create clusterrolebinding kubelet-bootstrap --clusterrole=system:node-bootstrapper --user=kubelet-bootstrap
kubelet 发起的 CSR 请求都是由 controller manager 来做实际签署的,对于 controller manager 来说,TLS bootstrapping 下 kubelet 发起的 CSR 请求大致分为以下三种
O=system:nodes
和 CN=system:node:(node name)
形式发起的 CSR 请求nodeclient | 仅在kubelet第一次启动时会产生 |
selfnodeclient | kubelet renew 自己作为 client 跟 apiserver 通讯时使用的证书产生的 |
selfnodeserver | kubelet 首次申请或后续 renew 自己的 10250 api 端口证书时产生的 |
CSR 请求类型
|
作用
|
---|
在 kubelet 首次启动后,如果用户 Token 没问题,并且 RBAC 也做了相应的设置,那么此时在集群内应该能看到 kubelet 发起的 CSR 请求 ,必须通过后kubernetes 系统才会将该 Node 加入到集群。查看未授权的CSR 请求
$ kubectl get csr
NAME AGE REQUESTOR CONDITION
node-csr--k3G2G1EoM4h9w1FuJRjJjfbIPNxa551A8TZfW9dG-g 2m kubelet-bootstrap Pending
$ kubectl get nodes
No resources found.
通过CSR 请求:
$ kubectl certificate approve node-csr--k3G2G1EoM4h9w1FuJRjJjfbIPNxa551A8TZfW9dG-g
certificatesigningrequest "node-csr--k3G2G1EoM4h9w1FuJRjJjfbIPNxa551A8TZfW9dG-g" approved
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
172.20.95.174 Ready <none> 48s v1.10.0
自动生成了kubelet kubeconfig 文件和公私钥:
$ ls -l /etc/kubernetes/kubelet.kubeconfig
-rw------- 1 root root 2280 Nov 7 10:26 /etc/kubernetes/kubelet.kubeconfig
$ ls -l /etc/kubernetes/ssl/kubelet*
-rw-r--r-- 1 root root 1046 Nov 7 10:26 /etc/kubernetes/ssl/kubelet-client.crt
-rw------- 1 root root 227 Nov 7 10:22 /etc/kubernetes/ssl/kubelet-client.key
-rw-r--r-- 1 root root 1115 Nov 7 10:16 /etc/kubernetes/ssl/kubelet.crt
-rw------- 1 root root 1675 Nov 7 10:16 /etc/kubernetes/ssl/kubelet.key
当成功签发证书后,目标节点的 kubelet 会将证书写入到 --cert-dir= 选项指定的目录中;注意此时如果不做其他设置应当生成四个文件 .kubelet 与 apiserver 通讯所使用的证书为 kubelet-client.crt,剩下的 kubelet.crt 将会被用于 kubelet server(10250) 做鉴权使用;注意,此时 kubelet.crt 这个证书是个独立于 apiserver CA 的自签 CA,并且删除后 kubelet 组件会重新生成它
上面提到,kubelet首次启动时会发起CSR请求,如果我们未做任何配置,则需要手动签发,若集群庞大,那么手动签发的请求就会很多,来了解一下自动签发
kubelet 所发起的 CSR 请求是由 controller manager 签署的;如果想要是实现自动签发,就需要让 controller manager 能够在 kubelet 发起证书请求的时候自动帮助其签署证书;那么 controller manager 不可能对所有的 CSR 证书申请都自动签署,这时候就需要配置 RBAC 规则,保证 controller manager 只对 kubelet 发起的特定 CSR 请求自动批准即可;针对上面 提出的 3 种 CSR 请求分别给出了 3 种对应的 ClusterRole,如下所示
# A ClusterRole which instructs the CSR approver to approve a user requesting
# node client credentials.
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: approve-node-client-csr
rules:
- apiGroups: ["certificates.k8s.io"]
resources: ["certificatesigningrequests/nodeclient"]
verbs: ["create"]
---
# A ClusterRole which instructs the CSR approver to approve a node renewing its
# own client credentials.
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: approve-node-client-renewal-csr
rules:
- apiGroups: ["certificates.k8s.io"]
resources: ["certificatesigningrequests/selfnodeclient"]
verbs: ["create"]
---
# A ClusterRole which instructs the CSR approver to approve a node requesting a
# serving cert matching its client cert.
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: approve-node-server-renewal-csr
rules:
- apiGroups: ["certificates.k8s.io"]
resources: ["certificatesigningrequests/selfnodeserver"]
verbs: ["create"]
RBAC 中 ClusterRole 只是描述或者说定义一种集群范围内的能力,这三个 ClusterRole 在 1.7 之前需要自己手动创建,在 1.8 后 apiserver 会自动创建前两个;以上三个 ClusterRole 含义如下
approve-node-client-csr | 具有自动批准 nodeclient 类型 CSR 请求的能力 |
approve-node-client-renewal-csr | 具有自动批准 selfnodeclient 类型 CSR 请求的能力 |
approve-node-server-renewal-cs | 具有自动批准 selfnodeserver 类型 CSR 请求的能力 |
ClusterRole
|
作用
|
---|
所以,如果想要 kubelet 能够自动签发,那么就应当将适当的 ClusterRole 绑定到 kubelet 自动续期时所所采用的用户或者用户组身上
要实现自动签发,创建的 RBAC 规则,则至少能满足四种情况:
基于以上2种情况,实现自动签发需要创建 2个 ClusterRoleBinding,创建如下 :
# 自动批准 kubelet 的首次 CSR 请求(用于与 apiserver 通讯的证书)
kubectl create clusterrolebinding node-client-auto-approve-csr --clusterrole=approve-node-client-csr --group=system:bootstrappers
# 自动批准 kubelet 发起的用于 10250 端口鉴权证书的 CSR 请求(包括后续 renew)
kubectl create clusterrolebinding node-server-auto-renew-crt --clusterrole=approve-node-server-renewal-csr --group=system:nodes
从以上流程我们可以看出,实现证书轮换创建 的RBAC 规则,则至少能满足四种情况:
基于以上四种情况,我们只需在开启了自动签发的基础增加一个ClusterRoleBinding:
# 自动批准 kubelet 后续 renew 用于与 apiserver 通讯证书的 CSR 请求
kubectl create clusterrolebinding node-client-auto-renew-crt --clusterrole=approve-node-client-renewal-csr --group=system:nodes
kubelet 启动时增加 --feature-gates=RotateKubeletClientCertificate=true,RotateKubeletServerCertificate=true
选项,则 kubelet 在证书即将到期时会自动发起一个 renew 自己证书的 CSR 请求;增加--rotate-certificates 参数,kubelet 会自动重载新证书
同时 controller manager 需要在启动时增加 --feature-gates=RotateKubeletServerCertificate=true
参数,再配合上面创建好的 ClusterRoleBinding,kubelet client 和 kubelet server 证才书会被自动签署;
TLS bootstrapping 时的证书实际是由 kube-controller-manager 组件来签署的,也就是说证书有效期是 kube-controller-manager 组件控制的;kube-controller-manager 组件提供了一个 --experimental-cluster-signing-duration
参数来设置签署的证书有效时间;默认为 8760h0m0s
,将其改为 87600h0m0s
即 10 年后再进行 TLS bootstrapping 签署证书即可。
bootstrap.kubeconfig
中的用户 Token 和 apiserver CA 证书发起首次 CSR 请求,这个 Token 被预先内置在 apiserver 节点的 token.csv 中,其身份为 kubelet-bootstrap
用户和 system:bootstrappers
用户组;想要首次 CSR 请求能成功(成功指的是不会被 apiserver 401 拒绝),则需要先将 kubelet-bootstrap
用户和 system:node-bootstrapper
内置 ClusterRole 绑定;system:bootstrappers
用户组与 approve-node-client-csr
ClusterRole 绑定实现自动签发(1.8 之前这个 ClusterRole 需要手动创建,1.8 后 apiserver 自动创建,并更名为 system:certificates.k8s.io:certificatesigningrequests:nodeclient
)--experimental-cluster-signing-duration
参数实现,该参数默认值为 8760h0m0s
--feature-gates=RotateKubeletClientCertificate=true,RotateKubeletServerCertificate=true
来实现;第二,想要让 controller manager 自动批准续签的 CSR 请求需要在 controller manager 启动时增加 --feature-gates=RotateKubeletServerCertificate=true
参数,并绑定对应的 RBAC 规则;同时需要注意的是 1.7 版本的 kubelet 自动续签后需要手动重启 kubelet 以使其重新加载新证书,而 1.8 后只需要在 kublet 启动时附带 --rotate-certificates 选项就会自动重新加载新证书apiserver 预先放置 token.csv,内容样例如下
6df3c701f979cee17732c30958745947,kubelet-bootstrap,10001,"system:bootstrappers"
允许 kubelet-bootstrap 用户创建首次启动的 CSR 请求 和RBAC授权规则
kubectl create clusterrolebinding kubelet-bootstrap --clusterrole=system:node-bootstrapper --user=kubelet-bootstrap
kubectl create clusterrolebinding kubelet-nodes \
--clusterrole=system:node \
--group=system:nodes
配置 kubelet 自动续期,RotateKubeletClientCertificate 用于自动续期 kubelet 连接 apiserver 所用的证书(kubelet-client-xxxx.pem),RotateKubeletServerCertificate 用于自动续期 kubelet 10250 api 端口所使用的证书(kubelet-server-xxxx.pem),--rotate-certificates 选项使得 kubelet 能够自动重载新证书
KUBELET_ARGS="--cgroup-driver=cgroupfs \ --cluster-dns=10.254.0.2 \ --resolv-conf=/etc/resolv.conf \ --experimental-bootstrap-kubeconfig=/etc/kubernetes/bootstrap.kubeconfig \ --feature-gates=RotateKubeletClientCertificate=true,RotateKubeletServerCertificate=true \ --rotate-certificates \ --kubeconfig=/etc/kubernetes/kubelet.kubeconfig \ --fail-swap-on=false \ --cert-dir=/etc/kubernetes/ssl \ --cluster-domain=cluster.local. \ --hairpin-mode=promiscuous-bridge \ --serialize-image-pulls=false \ --pod-infra-container-image=gcr.io/google_containers/pause-amd64:3.0"
配置 controller manager 自动批准相关 CSR 请求,如果不配置 --feature-gates=RotateKubeletServerCertificate=true 参数,则即使配置了相关的 RBAC 规则,也只会自动批准 kubelet client 的 renew 请求
KUBE_CONTROLLER_MANAGER_ARGS="--address=0.0.0.0 \ --service-cluster-ip-range=10.254.0.0/16 \ --cluster-name=kubernetes \ --cluster-signing-cert-file=/etc/kubernetes/ssl/k8s-root-ca.pem \ --cluster-signing-key-file=/etc/kubernetes/ssl/k8s-root-ca-key.pem \ --service-account-private-key-file=/etc/kubernetes/ssl/k8s-root-ca-key.pem \ --feature-gates=RotateKubeletServerCertificate=true \ --root-ca-file=/etc/kubernetes/ssl/k8s-root-ca.pem \ --leader-elect=true \ --experimental-cluster-signing-duration 10m0s \ --node-monitor-grace-period=40s \ --node-monitor-period=5s \ --pod-eviction-timeout=5m0s"
创建自动批准相关 CSR 请求的 ClusterRole,1.8 的 apiserver 自动创建了前两条 ClusterRole,所以只需要创建一条就行了
# A ClusterRole which instructs the CSR approver to approve a node requesting a
# serving cert matching its client cert.
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: system:certificates.k8s.io:certificatesigningrequests:selfnodeserver
rules:
- apiGroups: ["certificates.k8s.io"]
resources: ["certificatesigningrequests/selfnodeserver"]
verbs: ["create"]
将 ClusterRole 绑定到适当的用户组,以完成自动批准相关 CSR 请求
# 自动批准 system:bootstrappers 组用户 TLS bootstrapping 首次申请证书的 CSR 请求
kubectl create clusterrolebinding node-client-auto-approve-csr --clusterrole=system:certificates.k8s.io:certificatesigningrequests:nodeclient --group=system:bootstrappers
# 自动批准 system:nodes 组用户更新 kubelet 自身与 apiserver 通讯证书的 CSR 请求
kubectl create clusterrolebinding node-client-auto-renew-crt --clusterrole=system:certificates.k8s.io:certificatesigningrequests:selfnodeclient --group=system:nodes
# 自动批准 system:nodes 组用户更新 kubelet 10250 api 端口证书的 CSR 请求
kubectl create clusterrolebinding node-server-auto-renew-crt --clusterrole=system:certificates.k8s.io:certificatesigningrequests:selfnodeserver --group=system:nodes
原文:https://www.cnblogs.com/wangz-/p/13034746.html