卖兜搞IT

Kubernetes API Server和SSL Certificate

最近在重新录制《Certified Kubernetes Administrator (CKA) 考试完全指南》的课程,没太有时间搞公众号。这周正好搞到Security这一章,关于用户的认证和授权这一部分,其中涉及到了PKI公钥私钥数字签名认证相关的内容,和我之前录制的一起学加密系列视频版有关联,所以就顺便写一篇。

如果有打算购买《Certified Kubernetes Administrator (CKA) 考试完全指南》课程的朋友,可以使用这个链接 (或者点击阅读原文) https://www.udemy.com/course/k8s-chinese/?couponCode=F74B2C1A1B1485788862

注:我们的实验是以kubeadm搭建的集群为例

Image

Kubernetes API Server

Kubernetes的API Server对外是通过HTTPs提供服务的,服务器的地址和端口可以通过很多方式获取到,比如:

$ kubectl cluster-info
Kubernetes control plane is running at https://192.168.56.10:6443

或者:

$ kubectl config view -o jsonpath='{.clusters[0].cluster.server}'
https://192.168.56.10:6443

CA根证书

客户端通过如kubectl 要访问API Server,要信任HTTPs服务器提供的证书,则必须有这个签名这个证书的CA根证书。

这个CA根证书在集群初始化的时候生成,放到了control plane节点的 /etc/kubernetes/pki/ 目录里,名为 ca.crt. api server的服务器证书和私钥也在这个目录里,分别是 apiserver.crt 和 apiserver.key.

如果是kubeadm搭建的集群,这个根证书也会放到control plane节点的 ~/.kube/config 文件里,然后字符串是经过base64编码后的,要获取原始证书,要么直接查看/etc/kubernetes/pki/ca.crt文件,要么查看kubeconfig文件。

$ kubectl config view --raw -o jsonpath='{.clusters[0].cluster.certificate-authority-data}' | base64 --decode

通过curl测试一下:

$ curl https://192.168.56.10:6443
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.haxx.se/docs/sslcerts.html

curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.

curl默认无法验证证书的有效性,返回错误,这时候要么我们忽略掉证书验证:(--insecure 或者直接 -k)

$ curl https://192.168.56.10:6443 --insecure
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {},
  "status": "Failure",
  "message": "forbidden: User \"system:anonymous\" cannot get path \"/\"",
  "reason": "Forbidden",
  "details": {},
  "code": 403

要么提供CA根证书文件:(--cacert /etc/kubernetes/pki/ca.crt)

$ curl https://192.168.56.10:6443 --cacert /etc/kubernetes/pki/ca.crt
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {},
  "status": "Failure",
  "message": "forbidden: User \"system:anonymous\" cannot get path \"/\"",
  "reason": "Forbidden",
  "details": {},
  "code": 403

这样HTTPS这关过了,但是我们还是无法正在访问API,forbidden: User \"system:anonymous\" cannot get path 这是因为Kubernetes使用SSL双向认证的过程,要通过验证客户端(也就是kubectl)的SSL证书才验证用户的合法性。

SSL客户端证书

API Server如何判定访问它的客户端具有合法的“用户名和密码”呢,它是通过SSL的客户端验证实现的,而不是通过真正的用户名密码。

这就要说到客户端用户是如何产生的了。客户端用户实际上是拥有一个服务器api server的CA根证书签名过的证书,这个证书里包括客户端的用户名信息。过程如下:

  • • 客户端生成一对RSA公钥私钥

  • • 客户端通过自己的公钥,生成一个CSR(证书签名请求Certificate Signing Request),注意这个CSR中的CN的值是自己的用户名

  • • 客户端把CSR发给API Server,请求对其签名,通过CA根证书对其签名,获取到证书,发给客户端

  • • 客户端拿到证书后,就可以通过这个证书和API server通信了。

kubeadm搭建集群的初始,会生成的一个kubernetes-admin的用户,过程就是如上所示,然后把收到的签名证书和自己的私钥放到了 ~/.kube/config 文件里。

$ kubectl config view  -o jsonpath='{.users[0]}' | python3 -m json.tool
{
    "name": "kubernetes-admin",
    "user": {
        "client-certificate-data": "REDACTED",
        "client-key-data": "REDACTED"
    }
}

client-certificate-data 证书数据,base64编码

client-key-data 私钥,base64加密

通过下面的命令可以把证书和私钥还原并存为文件:

$ kubectl config view  -o jsonpath='{.users[0].user.client-certificate-data}' --raw | base64 --decode > client.crt
$ kubectl config view  -o jsonpath='{.users[0].user.client-key-data}' --raw | base64 --decode > client.key

然后就可以提供证书和私钥去访问API Server了:

curl https://192.168.56.10:6443 \
  --cacert /etc/kubernetes/pki/ca.crt \
  --cert client.crt \
  --key client.key