ljzsdut
GitHubToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeToggle Dark/Light/Auto modeBack to homepage
Edit page

1.0 K8s部署之 Kubernetes集群中https通信

【理论】Kubernetes集群中https通信-概述

【骏马金龙】加密、签名和SSL握手机制细节请参考该文

Kubernetes集群可承载运行用户的完整应用环境,包括多种服务及相关的配置信息等,对这些应用的管理操作需要进行精心的认证及授权编排,以确保其安全性。本文描述了集群各组件间的安全通信时相关的加密通信及认证机制用到的数字证书等话题。

一、etcd集群及相关的数字证书

Kubernetes的API Server将集群的状态数据存储于集群存储服务etcd中,包括那些包含了敏感数据的Secret资源对象。出于提升服务可用性、数据冗余及安全性等目的,生产环境通常应该配置有着3、5或7个节点的etcd集群,集群内各节点间基于https协议进行通信,它们使用Peer类型的数字证书进行彼此间的通信时的身份认证。而且,各etcd节点提供Server类型的数字证书与客户端建立安全连接,并验正其客户端的Client类型的数字证书。Kubernetes各集群组件中,kube-apiserver是惟一一个可直接与集群存储etcd通信的组件,它是etcd服务的客户端。(图中的箭头方向表示客户端请求服务器的方向)

M5WfO0-1605663439379

类型作用证书拥有者备注
Peeretcd集群节点间彼此通信时的身份认证etcd对等节点:相互为服务器端和客户端;peer端口为2380。在etcd的配置文件中使用ETCD_PEER_CERT_FILE和ETCD_PEER_KEY_FILE指定服务间相关通信使用的证书和私钥
Serveretcd与客户端(例如apiserver)通信使用的证书etcd服务器证书:只能用于服务器端。在etcd的配置文件中使用ETCD_CERT_FILE和ETCD_KEY_FILE指定服务端证书和私钥
Client客户端(例如apiserver)与etcd通信使用的证书apiserver客户端证书:只能用于客户端。
CA签署以上3种证书指定CA证书来验正客户端身份(准确讲是获取可靠的公钥)。在etcd的配置文件中使用ETCD_TRUSTED_CA_FILE(对应Server证书的签发CA)和ETCD_PEER_TRUSTED_CA_FILE(对应Peer证书的签发CA)来指定使用的CA证书

二、Kubernetes集群及相关的数字证书

​ API Server通过认证插件完成客户端身份认证,它支持X509客户端证书认证及多种令牌(Token)认证机制,例如静态令牌文件、静态口令文件、引导令牌等等,各认证方式的协同机制为“一票通过”,即任何插件认证成功后即完成认证操作而不会再由后续的其它插件继续检查。而那些未被任何认证插件拒绝的请求将被识别为匿名用户,它以system:anonymous为用户名,并隶属于system:unauthenticated组。另外,API Server在认证成功后还会通过授权插件核验用户的操作权限,例如RBAC等,并在执行写操作时由准入控制器插件施加额外的管控机制。

​ Kubernetes集群的其它各组件均需要通过kube-apiserver访问集群资源,例如各kube-controller-manager、kube-scheduler、kube-proxy和kubelet组件,以及客户端工具程序kubectl等,因此,它们均为API Server的客户端。同样出于安全性等目的,Kubernetes集群的API Server需要借助TLS协议与其客户端通信,并应该基于X509数字证书或令牌认证机制验正各组件的身份。通常,这些客户端会基于kubeconfig配置文件在请求报文中附带认证数据,包括用户名及相关的客户端证书和私钥等。

​ 另外,kubelet自身也通过HTTPS端点暴露了一组API,它们提供了多个不同级别的敏感数据,并支持来自于客户端的请求,在节点和容器上执行不同级别的操作。默认情况下,未被其它身份认证插件拒绝的那些对kubelet的HTTPS端点的请求将被视为匿名请求,并自动赋予其隶属于system:unauthenticated用户组的用户名system:anonymous。不过,kubelet可使用--anonymous-auth=false选项拒绝匿名访问,并设置其通过--client-ca-file选项指定CA以验正客户端身份,它可直接使用kubernetes-ca,同时还应该为kube-apiserver使用--kubelet-client-certificate--kubelet-client-key选项指定用于认证到kubelet的客户端证书和私钥。

​ 补充:因为kubelet自己也有APIs,它们会被apiserver请求来完成某些操作 。而且这些不同的api对应的操作级别不同,每一个api都需要做ssl身份验证。因为这样的api种类太多,所以kubelet自动实现这些身份验证。那怎么自动实现呢?其实每一个node上的kubelet上都有一个二级CA,然后给kubelet的api发证书,并且这个CA是kubenetes-ca的二级CA,这意味着apiserver拿着由kubenetes-ca签署的证书去连接kubelet的api是可以被允许的,因为证书链信任是传递的

1zwu2Q-1624843413930

​ 事实上,启用RBAC授权插件的API Server还会在认证完成后严格检验请求操作的授权,它会把客户端证书中的CN识别为用户名,并将O识别为用户所属的组。而kube-controller-manager、kube-scheduler、kube-proxy和kubelet等组件分别需要获取到特定的授权方能正确运行,它们应该分别使用专有的用户名或者组名以获取默认的授权。这就要求我们在生成证书时,需要指定特定的CN和O以获取特定的权限。

类型作用使用程序备注
Client客户端用于连接kube-apiserver的客户端证书kube-controller-manager
Client客户端用于连接kube-apiserver的客户端证书kube-scheduler
Client客户端用于连接kube-apiserver的客户端证书kube-proxy
Client客户端用于连接kube-apiserver的客户端证书kubelet
Client客户端用于连接kube-apiserver的客户端证书kubectl
Server服务器端用户验证其他客户端的证书kube-apiserver
Clientapiserver作为客户端用来与kubelet的API进行通信认证kube-apiserver使用–kubelet-client-certificate和–kubelet-client-key选项指定用于认证到kubelet的客户端证书和私钥
CA签署以上证书

客户端证书的CN可任意。但如果客户端证书在kubeconfig中使用,CN和O就充当了用户名和组名。

服务器端证书的CN或SAN不能任意,需包含访问服务器的url。

以下几个证书的CN是固定的,它们是k8s的核心组件使用的,具体特定的权限,不能改变:

typesubjextention
kube-controller-manager.crt/CN=system:kube-controller-manager绑定到clusterrolebinding/system:kube-controller-manager
kube-scheduler.crt/CN=system:kube-scheduler绑定到clusterrolebinding/system:kube-scheduler
kube-proxy.crt 可选,可以使用jwt token。用于kube-proxy.conf/CN=system:kube-proxy绑定到clusterrolebinding/system:node-proxier
bootstrap.conf,据此自动生成kubelet.confguser: system:bootstrapper绑定到clusterrolebinding/system:bootstrapper
admin.conf/CN=kubernetes-admin/O=system:masters通过O=system:masters绑定到clusterrolebinding/cluster-admin

三、kube-aggregator(代理)及相关的数字证书

​ 除了API Server提供的核心API,Kubernetes还支持通过聚合层(aggregation layer)对其进行扩展。简单来说,聚合层允许管理员在群集中部署使用额外的Kubernetes风格的API,例如service catalog或用户自定义的API server等。聚合层本身打包在kube-apiserver程序中并作为进程的一部分运行,但仅在管理员通过行定的APIService对象注册扩展资源之后,它才为其代理转发相应的请求。通常,APIService会由运行于Kubernetes集群上的Pod中的extention-apiserver实现。

​ 由kube-aggregator控制器提供服务的组apiregistraion.k8s.io/v1beta1下的APIService资源是用于注册扩展apiserver的API。

​ 创建一个APIService资源时,作为注册和发现过程的一部分,kube-aggregator控制器(位于kube-apiserver内部)将启动与extention-apiserver的基于TLS(若启用了的话)的HTTP2连接,而后经由此连接将经过身份验证的用户请求代理到extention-apiserver上,于是,kube-aggregator被设置为执行RequestHeader客户端认证。

img

一个对kubernetes的请求最终会在kube-aggregator处被分流进api-server自己实现的resource handler或者客制化的api server(Custom API Server)。

不过,只有kube-apiserver在启动时使用了如下选项时才能启用其内建的聚合层:

--requestheader-client-ca-file=<path to aggregator CA cert>
--requestheader-allowed-names=front-proxy-client
--requestheader-extra-headers-prefix=X-Remote-Extra-
--requestheader-group-headers=X-Remote-Group
--requestheader-username-headers=X-Remote-User
--proxy-client-cert-file=<path to aggregator proxy cert>
--proxy-client-key-file=<path to aggregator proxy key>

proxy-client-cert-file和proxy-client-key-file包含kube-aggregator执行客户端证书身份验证的证书/密钥对,它使用requestheader-client-ca-file中指定的CA文件对聚合器证书进行签名。requestheader-allowed-names包含允许充当伪装前端代理的身份/名称列表(客户端证书中使用的CN),而requestheader-username-headers、requestheader-group-headers和requestheader-extraheaders-prefix携带一个HTTP头的列表,用于携带远程用户信息。

类型作用使用程序备注
Clientkube-aggregator用于连接extension-apiserver的客户端证书kube-apiserver中的kube-aggregator使用–proxy-client-cert-file和–proxy-client-key-file选项指定用于认证到extension-apiserver的客户端证书和私钥
CA前端代理CA,kube-apiserver使用–requestheader-client-ca-file指定用于对聚合器证书进行签名

四、Service Account key

As per https://github.com/kubernetes/kubernetes/issues/22351#issuecomment-26082410

The service account token private key (sa.key) given only to the controller manager, is used to sign the tokens.

The masters only need the public key portion (sa.pub) in order to verify the tokens signed by the controller manager.

The service account public key has to be the same for all. In an HA setup that means you need to explicitly give it to each apiserver (recommended) or make all the apiservers use the same serving cert/private TLS key (not recommended).

As a convenience, you can provide a private key to both, and the public key portion of it will be used by the api server to verify token signatures.

As a further convenience, the api server’s private key for it’s serving certificate is used to verify service account tokens if you don’t specify --service-account-key-file

--tls-cert-file and --tls-private-key-file are used to provide the serving cert and key to the api server. If you don’t specify these, the api server will make a self-signed cert/key-pair and store it at apiserver.crt/apiserver.key

翻译:

controller-manager使用–service-account-private-key-file=/etc/kubernetes/pki/sa.key选项指定sa.key私钥文件,用于加密Service Account的Token。

apiserver使用--service-account-key-file=/etc/kubernetes/pki/sa.pub选项指定sa.pub公钥,用于解密ServiceAccount的token。文件中可以包含多个Key,这一参数可以重复指定多个文件。如果没指定的话,会使用–tls-private-key-file替代。

Controller Manager使用私钥签署 Service Account的Token,apiserver用公钥来检验ServiceAccount的token。Controller Manager使用的私钥跟Kubernetes中使用的其他私钥不同的是,这个私钥是不支持同一CA验证的,因此,需要给每个Controller Manager 指定一致的私钥文件。这个私钥文件也不需要什么CA来做签署,生成很容易:openssl genrsa -out private.key 4096,然后分发给每个 Controller Manager 和 API Server 就可以了。

apiserver使用和--tls-private-key-file一致的文件是可以工作的,只要你给每个API Server用的都是同一个 TLS Key(一般都这么做的吧?)。(这里我假设你运行的一个有高可用支持的,多个API Server和多个Controller Manager同时运行的集群)如果两个不同的Controller Manager用了两个不同的Key,那就杯具了,他们会用各自的Key来生成 Token,最终导致无效判定。我觉得这点不太合理,Kubernetes应该和其他方面一样,使用CA 进行管理。通过对源码的阅读,我觉得原因可能是jwt-go不支持CA。

五、相关的数字证书

总结起来,Kubernetes集群至少需要三个CA,分别用于为不同应用场景中的服务端、客户端或对等节点间的通信签署证书。

img

各组件大致要用到如下表格中的证书:

img

以及以下kubeconfig配置文件,它们承载了相关客户端的认证凭据,这些认证凭据通常也来自于数字证书:

img

[root@physerver certs]# tree master-etc/
master-etc/
├── etcd
│   └── pki
│       ├── ca.crt
│       ├── ca.key
│       ├── ca.srl
│       ├── peer.crt  #etcd集群节点间相互认证使用,每个节点既是Client,又是server
│       ├── peer.key
│       ├── server.crt  #etcd使用的server证书,实现客户端来验证etcd的身份。
│       └── server.key
└── kubernetes
    ├── auth
    │   ├── admin.conf  #kubelet使用,通过O=system:masters绑定到clusterrolebinding/cluster-admin
    │   ├── controller-manager.conf  #kube-controller-manage使用,绑定到clusterrolebinding/system:kube-controller-manager
    │   └── scheduler.conf  #kube-scheduler使用,绑定到clusterrolebinding/system:kube-scheduler
    ├── pki
    │   ├── apiserver.crt  #apiserver的server证书,用于各种客户端验证apiserver身份
    │   ├── apiserver-etcd-client.crt  #etcd的ca签发给apiserver的证书,用于etcd认证apiserver身份
    │   ├── apiserver-etcd-client.key
    │   ├── apiserver.key
    │   ├── apiserver-kubelet-client.crt  #apiserver作为客户端用来请求kubelet的api,实现验证apiserver身份
    │   ├── apiserver-kubelet-client.key
    │   ├── ca.crt
    │   ├── ca.key
    │   ├── ca.srl
    │   ├── front-proxy-ca.crt
    │   ├── front-proxy-ca.key
    │   ├── front-proxy-ca.srl
    │   ├── front-proxy-client.crt  #kube-aggregator用于连接extension-apiserver的客户端证书,实现kube-aggregator的身份验证
    │   ├── front-proxy-client.key
    │   ├── sa.key  #controller-manager用于加密Service Account的Token。
    │   └── sa.pub  #apiserver用于解密ServiceAccount的token。
    └── token.csv  #apiserver的token认证:--token-auth-file=/etc/kubernetes/token.csv

5 directories, 27 files
[root@physerver certs]# tree node-etc/
node-etc/
└── kubernetes
    ├── auth
    │   ├── bootstrap.conf   #kubelet使用,绑定到clusterrolebinding/system:bootstrapper
    │   └── kube-proxy.conf  #kube-proxy使用,绑定到clusterrolebinding/system:node-proxier
    └── pki
        └── ca.crt

3 directories, 3 files

六、小结

总结起来,Kubernetes要基于PKI完成如下操作

  • API Server通过客户端证书认证到etcd
  • API Server的服务端证书
  • kubelet使用客户端证书认证到API Server
  • Controller Manager使用客户端证书和kubeconfig文件认证到API Server
  • Scheduler使用客户端证书和kubeconfig文件认证到API Server
  • API Server通过客户端证书认证到kubelet
  • Aggregator的代理服务用到的客户端及服务端证书

参考文章:

https://nixaid.com/deploying-kubernetes-cluster-from-scratch/

https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-authentication-authorization/

【理论】k8s各种证书介绍:


1、API Server证书:

如果用TLS连接API Server,API Server就需要这两个参数来选择使用的证书。

--tls-cert-file=/etc/kubernetes/pki/apiserver.crt
--tls-private-key-file=/etc/kubernetes/pki/apiserver.key

2、客户端认证API Server证书:

验证证书身份,使用的是ca的证书(用于签发该证书的CA的证书)。

设置了apiserer使用的证书之后,各个组件来验证apiserver身份时,需要知道使用哪个ca来验证,k8s的各个组件使用–kubeconfig参数指定kubeconfig文件来获取ca信息。所以需要给各个组件的kubeconfig文件中的cluster中指定以上证书的所属CA,例如:

current-context: my-context
apiVersion: v1
clusters:
- cluster:
    certificate-authority: /path/to/my/ca.crt #签发API Server证书的CA证书
    server: https://horse.org:4443
  name: my-cluster
kind: Config
users:
- name: green-user
  user:
    client-certificate: path/to/my/client/cert
    client-key: path/to/my/client/key

以上操作完成客户端对apiserver的身份认证。


3、API Server认证客户端证书:

--client-ca-file=/etc/kubernetes/pki/ca.crt

首先,所有客户端证书都应该由该CA签发(该CA不需要和签发API Server证书的CA一致)。所有客户端使用该CA签发的证书来请求apiserver时,会使用客户端证书中相应的CN作为用户名进行身份认证。 当客户端使用kubeconfig文件的时候,可以按照如下方式设置它们使用的证书:

kind: Config
users:
- name: green-user
  user:
    client-certificate: path/to/my/client/cert
    client-key: path/to/my/client/key

以上操作,apiserver使用--client-ca-file=/etc/kubernetes/pki/ca.crt对客户端的身份进行认证。


4、认证代理:requestheader

Kubernetes 做了很多用户证书方面的假设(用户名就是证书的Common Name,用户组就是证书的 Organization)。如果这些假设不符合需求,那么就应该停用客户端证书认证,改用认证代理。代理会使用HTTP Header 将用户名和组发送给API Server。这里的代理也就是聚合层(aggregation layer),本身打包在kube-apiserver程序中并作为进程的一部分运行。

如何在http header中设置指定的用户名和用户组?通过--requestheader-group-headers=X-Remote-Group--requestheader-username-headers=X-Remote-User来定义用户名和用户组。

使用认证代理的请求流程:

客户端 ---发起请求---> 代理 ---Add Header:发起请求---> kube-apiserver
                   (客户端证书)                        (服务端证书)

代理使用一个客户端证书表明身份(代理使用--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告知API Server,该证书所属的CA,API Server使用该ca证书来验证认证代理。 --requestheader-username-headers=X-Remote-User中指定的Header中包含用户名,这一参数的列表确定了允许有效的Common Name,如果这一参数的列表为空,则所有通过-–requestheader-client-ca-file校验的都允许通过。


5、service account 私钥(不是 CA 签发的,只是用普通的公钥和私钥)

  • controller-manager:--service-account-private-key-file=/etc/kubernetes/pki/sa.key controller-manager用于加密Service Account Token。
  • apiserver:--service-account-key-file=/etc/kubernetes/pki/sa.pub apiserver用于解密ServiceAccount的 token。文件中可以包含多个Key,这一参数可以重复指定多个文件。如果没指定的话,会使用–tls-private-key-file替代。

Controller Manager使用私钥签署 Service Account Token,apiserver用公钥来检验ServiceAccount的token。Controller Manager使用的私钥跟Kubernetes中使用的其他私钥不同的是,这个私钥是不支持同一CA验证的,只是普通的公钥、私钥,没有经过CA的签名。因此,需要给每个Controller Manager 指定一致的私钥文件。这个私钥文件也不需要什么CA来做签署,生成很容易:openssl genrsa -out private.key 4096,然后分发给每个 Controller Manager 和 API Server 就可以了。

apiserver使用和–tls-private-key-file一致的文件是可以工作的,只要你给每个API Server用的都是同一个 TLS Key(一般都这么做的吧?)。(这里我假设你运行的一个有高可用支持的,多个API Server和多个Controller Manager同时运行的集群)如果两个不同的Controller Manager用了两个不同的Key,那就杯具了,他们会用各自的Key来生成 Token,最终导致无效判定。我觉得这点不太合理,Kubernetes应该和其他方面一样,使用CA 进行管理。通过对源码的阅读,我觉得原因可能是jwt-go不支持CA。

6、总结

使用kubeadm安装k8s时,使用的3种CA和它们签署的证书:

k8s建议使用不同的ca签署不同的证书(下图使用了3套CA),但是为了简单,也可以使用同一个ca签署所有的证书。

k8s证书介绍

参考文档: https://www.kubernetes.org.cn/2540.html https://docs.lvrui.io/2018/09/28/%E8%AF%A6%E8%A7%A3kubeadm%E7%94%9F%E6%88%90%E7%9A%84%E8%AF%81%E4%B9%A6/