apiserver包含三个知识点:
第一,所有接口(APIServer管理所有restful风格的接口,etcd作为分布式数据中心,存储configmap和secret)
第二,所有证书(包含k8s整个架构)
第三,一个外部Request请求发送过来怎么处理
这篇文章讲述 K8S 所有的证书,其中包含了 K8S 内部组件整个架构。
K8S全部证书,其中大多数证书都和APIServer相关,所以证书这个章节的内部放到的 APIServer 组件下面的阐述。
对于每个证书,学会三个:
业务场景、加密解密过程、CA证书颁发这个证书的过程
K8S里面是APIServer提供所有restful风格的接口,这些内部接口都是 HTTPS 的,所以需要客户端服务端双向认证,即客户端要验证服务端,服务端也需要验证客户端,所有客户端服务端都需要证书。
在 kubernetes 的组件之间进行通信时,数字证书的验证是在协议层面的通过 TLS 完成的,除了需要在建立通信时提供相关的证书和密钥外,在应用层面并不需要进行特殊处理。采用TLS进行验证有两种方式:
第一,服务端单向认证:只需要服务端提供证书,客户端通过服务端证书验证服务的身份,但服务端并不验证客户端身份。这种情况一般适用于对 Internet 开放的服务,例如 搜索引擎网站,任意客户端都可以连接到服务端进行访问,但客户端需要验证服务器的身份,以避免连接到伪造的恶意服务器。
第二,双向TLS认证:除了客户端需要验证服务器的证书,服务器也要通过客户端证书验证客户端的身份。这种情况下服务器提供的是敏感信息,只允许特定身份的客户端访问。
在 kubernetes 中,各个组件提供的接口包含了集群内部信息。如果这些接口被非法访问,将影响集群的安全,因此组件之间的通信采用双向 TLS 认证。即客户端和服务端都需要验证对方的身份信息。在两个组件进行双向认证时,会涉及到下面的这些证书相关的文件:
服务端证书:服务端用于证明自身身份的数字证书,里面包含了服务端公钥以及服务端的身份信息;(例如:xx-server.crt)
服务端私钥:服务端证书中包含的公钥所对应的私钥。公钥和私钥是成对出现的,在进行TLS验证时,服务端使用该私钥来向客户端证明自己是服务端证书的拥有者。(例如:xx-server.key)
客户端证书:客户端用于证明自身身份的数字证书,里面主要包含了客户端的公钥以及客户端的身份信息; (例如:xx-client.crt)
客户端私钥:客户端证书中的包含的公钥所对应的私钥。公钥和私钥是成对出现的,在进行TLS验证时,客户端使用私钥来向服务端发送请求证明自己是客户端证书的拥有者。(例如:xxx-client.key) 【客户端用私钥加密,然后发送给服务端】
问题1:服务端私钥是服务端证书中包含的公钥所对应的私钥 / 客户端私钥是客户端证书中的包含的公钥所对应的私钥。
回答:.crt 文件是证书,.pub 文件是公钥,.key 文件是私钥,三者关系是:证书里面包含着公钥
问题2:在进行TLS验证时,服务端使用该私钥来向客户端证明自己是服务端证书的拥有者
回答:非对称加密中,加密算法是公开的,公钥是公开的,私钥是自己持有,完整的请求是:
(1) 客户端使用客户端私钥(即自己的私钥)数据签名,表示这个请求是自己发送的,以后无法抵赖
(2) 客户端使用目的服务端公钥(因为是公开的,所以可以拿到)数据加密,发送给服务端,这个请求中的密文只有目的服务端用私钥才能解开,别的服务端截取了也没用
(3) 服务端使用服务端私钥(即自己的私钥)解密收到的请求
(4) 服务端使用客户端公钥(因为是公开的,所以可以拿到)验证这个请求是某个具体的客户端发送的
即 发送端私钥(使用自己的)数字签名 - 发送端(使用接收端的)公钥加密 - 接收端(使用自己的)私钥解密 - 接收端(使用发送端的)公钥验证签名
问题2中的这句话就是表示 发送端使用私钥数字签名 证明请求是自己发送的
服务端CA根证书:签发服务端证书的CA根证书,客户端使用该CA根证书来验证服务端证书的合法性。
客户端CA根证书:签发客户端证书的CA根证书,服务端使用该CA根证书来验证客户端证书的合法性。
ca.crt 证书有两个,一个是 /etc/kubernetes/pki/ca.crt ,一个是 /etc/kubernetes/pki/etcd/ca.crt
服务端保留 ca 根证书为了签发服务端的其他证书,然后将自己的 ca 证书发给客户端,这样客户端就可以验证收到的服务端请求。
客户端保留 ca 根证书为了签发客户端的其他证书,然后将自己的 ca 证书发给服务端,这样服务端就可以验证收到的客户端请求。
公钥和私钥是成对的,它们互相解密,其实就是 encode 和 decode,有两个应用场景:
场景1:公钥加密,私钥解密。
场景2:私钥数字签名,公钥验证。
假设一下,我找了两个数字,一个是1,一个是2 。我喜欢2这个数字,就保留起来,不告诉你们(私钥),然后我告诉大家,1是我的公钥。
我有一个文件,不能让别人看,我就用1加密了。别人找到了这个文件,但是他不知道2就是解密的私钥啊,所以他解不开,只有我可以用 数字2,就是我的私钥,来解密。这样我就可以保护数据了。我的好朋友x用我的公钥1加密了字符a,加密后成了b,放在网上。别人偷到了这个文件,但是别人解不开,因为别人不知道2就是我的私钥, 只有我才能解密,解密后就得到a。这样,我们就可以传送加密的数据了。
如果我用私钥加密一段数据(当然只有我可以用私钥加密,因为只有我知道2是我的私钥),结果所有的人都看到我的内容了,因为他们都知 道我的公钥是1,那么这种加密有什么用处呢?
假如我的好朋友x说有人冒充我给他发信。怎么办呢?我把我要发的信,内容是c,用我的私钥2,加密,加密后的内容是d,发给x,再告诉他 解密看是不是c。他用我的公钥1解密,发现果然是c。 这个时候,他会想到,能够用我的公钥解密的数据,必然是用我的私钥加的密。只有我知道我的私钥,因此他就可以确认确实是我发的东西。 这样我们就能确认发送方身份了。这个过程叫做数字签名。所以说,用私钥来加密数据,用途就是数字签名。
我们上面的 server.crt 和 client.crt 都是哪里来的,都是 CA 机构生成的。通常我们配置https服务时需要到"权威机构"(CA)申请证书。过程是这样的:
1 网站创建一个密钥对,提供公钥和组织以及个人信息给权威机构
2.权威机构颁发证书
3.浏览网页的朋友利用权威机构的根证书公钥解密签名,对比摘要,确定合法性
4.客户端验证域名信息有效时间等(浏览器基本都内置各大权威机构的CA公钥)
这个从CA权威机构申请下来的网站的证书包含如下内容:
1.申请者公钥 (这里再次验证 server.crt 里面是包含着公钥的)
2.申请者组织和用户名
3.签发机构CA信息,有效时间,序列号等
4.以上信息的签名
server.crt 服务端证书是从服务端从 CA权威机构 申请下来的,里面包含这个服务端的公钥,但是不包含服务端私钥,服务端私钥是另一个文件 server.key
Certificate: # 证书
Data:
Version: 3 (0x2) # 版本
Serial Number: 6844586833713845496 (0x5efcdb7609d454f8) # 序列号
Signature Algorithm: sha256WithRSAEncryption # 签名算法
Issuer: CN=front-proxy-ca # 申请者用户名
Validity # 有效时间
Not Before: Apr 17 08:21:09 2023 GMT
Not After : Apr 16 08:21:09 2024 GMT
Subject: CN=front-proxy-client # 申请者组织和用户名
Subject Public Key Info: # 申请者公钥信息 (公钥算法 + )
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Key Usage: critical
Digital Signature, Key Encipherment
X509v3 Extended Key Usage:
TLS Web Client Authentication
X509v3 Basic Constraints: critical
CA:FALSE
X509v3 Authority Key Identifier:
keyid:9D:59:2F:AD:D1:C9:00:10:33:93:F6:F8:C7:12:E6:02:D8:D4:53:B9
Signature Algorithm: sha256WithRSAEncryption # 以上信息的签名
根证书/CA证书:根证书又名自签名证书,也就是自己给自己颁发的证书。CA(Certificate Authority)被称为证书授权中心,k8s 中的ca.crt 就是CA证书, 就是根证书。APIServer 和 etcd 都有这个 ca.crt,分别是 /etc/kubernetes/pki/ca.crt 和 /etc/kubernetes/pki/etcd/ ca.crt ,但是内容不一样;但是同一集群不同节点的 /etc/kubernetes/pki/ca.crt 内容是一样的。
密钥对:sa.key sa.pub (一个密码对包含一个公钥和一个私钥)
根证书:ca.crt etcd/ca.crt
私钥:ca.key 等 其它证书
生成CA证书和私钥
cfssl gencert -initca ca-csr.json | cfssljson -bare ca
ls | grep ca
ca-config.json
ca.csr
ca-csr.json
ca-key.pem
ca.pem
ll | grep ca
查询到的五个文件中,ca-key.pem是ca的私钥,ca.csr是一个签署请求,ca.pem是CA证书,是后面kubernetes组件会用到的RootCA。
.csr 全称 Certificate Signing Request,证书请求文件
以 kubernetes 里面最重要的一个 /etc/kubernetes/pki/ca.crt证书为例
/etc/kubernetes/pki 目录下的 apiserver ca.key 和 ca.crt 证书
对于 /etc/kubernets/pki/ca.crt 证书
Issuer: Common Name=kubernetes
Subject: Common Name=kubernetes
Issuer 和 Subject 相同,代表这里是本身就是一个 ca 根证书,可以用来 颁发/生成 其他 xxx.crt 证书。
K8S集群中的CA证书包括两个,一个是 /etc/kubernetes/pki/etcd 目录下的 ca.crt 证书,这个证书用来生成 /etc/kubernetes/pki/etcd 目录下的全部证书,这个证书只有在 master 节点上有,因为 etcd 只有在 master 节点上运行;一个是 /etc/kubernetes/pki 目录下的 ca.crt 证书, 这个证书用来生成 /etc/kubernetes/pki 目录下的全部证书和 /var/lib/kubelet/pki 目录下的证书,这个证书在所有节点上都有,而且每个节点上都一样,因为 kubelet 只有在所有节点上都运行。
看一下两个 ca.crt 证书,即 etcd 的ca.crt 和 apiserver 的ca.crt ,如下:
使用openssl 的命令行进行文件的加密与解密过程,主要有两种方式:
方式1:openssl 指定加密/解密算法加密
方式2:openssl 指定公钥/私钥文件加密
k8s里面,.crt 证书文件都是 x509 加密,.key 密钥文件都是 rsa 加密,三类secret都是 base64 加密
主节点证书整个目录结构如下(APIServer和Etcd):
[root@master /etc/kubernetes/pki]# tree
.
├── apiserver.crt #apiserver 服务端证书
├── apiserver-etcd-client.crt #apiserver访问etcd的客户端证书
├── apiserver-etcd-client.key #apiserver访问etcd的客户端证书包含的公钥对应的私钥
├── apiserver.key #apiserver 服务端证书包含的公钥对应的私钥
├── apiserver-kubelet-client.crt #apiserver访问kubelet的客户端证书
├── apiserver-kubelet-client.key #apiserver访问kubelet的客户端证书包含的公钥对应的私钥
├── ca.crt #根证书
├── ca.key #根证书包含的公钥对应的私钥
├── etcd
│ ├── ca.crt #etcd根证书
│ ├── ca.key #etcd根证书包含的公钥对应的私钥
│ ├── healthcheck-client.crt #pod中liveness探针客户端证书
│ ├── healthcheck-client.key #pod中liveness探针客户端证书包含的公钥对应的私钥
│ ├── peer.crt #etcd节点互通证书,由根证书签发
│ ├── peer.key #etcd节点互通证书,由根证书签发包含的公钥对应的私钥
│ ├── server.crt # 由 etcd 根证书签发的 etcd服务端证书
│ └── server.key # 由 etcd 根证书签发的 etcd服务端证书包含的公钥对应的私钥
├── front-proxy-ca.crt #代理根证书
├── front-proxy-ca.key #代理根证书包含的公钥对应的私钥
├── front-proxy-client.crt #由代理根证书签发的客户端证书
├── front-proxy-client.key #由代理根证书签发的客户端证书包含的公钥对应的私钥
├── sa.key # 生成pod sa账号的私钥,kube-controller-manager使用,对应上图中功能12
└── sa.pub # 解密pod sa账号的公钥,kube-apiserver使用,对应上图中功能12
1 directory, 22 files
[root@m kubernetes]# tree
.
├── admin.conf # /etc/kubernetes/admin.conf 这个文件就是复制为 /root/.kube/config 文件,然后供 kubectl 访问 k8s 集群;
├── controller-manager.conf
├── kubelet.conf # /etc/kubernetes/kubelet.conf 这个文件就是 systemctl status kubelet -l 这个守护进程的启动文件,删掉这个文件,kubelet服务无法启动
├── manifests
│ ├── etcd.yaml # etcd 启动yaml,etcd pod所需要的文件和证书都指定了 (存放在 /etc/kubernetes/pki/etcd 目录下)
│ ├── kube-apiserver.yaml # apiserver 启动yaml,apiserver pod所需要的文件和证书都指定了 (存放在 /etc/kubernetes/pki 目录下)
│ ├── kube-controller-manager.yaml # controller-manager 启动yaml,其文件和证书就是/etc/kubernetes/controller-manager.conf
│ └── kube-scheduler.yaml # scheduler启动yaml,其文件和证书就是/etc/kubernetes/scheduler.conf
├── pki (子目录省略,就是一系列的证书)
├── scheduler.conf
我们看到APIServer和etcd两个目录下都有ca根证书,但是同一集群下这两个ca根证书是不一样的 (一样的也就不会是两个文件了)
K8S中所有证书都存放在 /etc/kubernetes/pki 目录下,整个结构如图:
1、etcd集群中各个节点之间相互通信使用的证书。由于一个etcd节点既为其他节点提供服务,又需要作为客户端访问其他节点,因此该证书同时作为服务端证书和客户端证书。【涉及文件:peer.key peer.crt 】
2、etcd集群向外提供服务使用的证书。该证书是服务端证书。【涉及文件:server.key server.crt】
3、kube-apiserver 作为客户端访问 etcd 使用的证书。该证书是客户端证书。 【涉及证书:apiserver 的 apiserver-etcd-client.key 和 apiserver-etcd-client.crt 】
4、kube-apiserver 对外提供服务使用的证书。该证书是服务端证书。【涉及文件:apiserver.crt与apiserver.key】
5、kube-controller-manager 作为客户端访问 kube-apiserver 使用的证书,该证书是客户端证书。【涉及文件:controller-manager.conf】
6、kube-scheduler 作为客户端访问 kube-apiserver 使用的证书,该证书是客户端证书。【涉及文件:scheduler.conf】
7、kube-proxy 作为客户端访问 kube-apiserver 使用的证书,该证书是客户端证书。
8、kubelet 作为客户端访问 kube-apiserver 使用的证书,该证书是客户端证书。【涉及文件:/var/lib/kubelet/pki 目录下的 client.crt client.key,由 /etc/kubernetes/pki/ca.crt 集群根证书生成】
9、管理员用户通过 kubectl 访问 kube-apiserver 使用的证书,该证书是客户端证书。 【涉及的文件:/root/.kube/config 或 /etc/kubernetes/admin.conf】
10、kubelet 对外提供服务使用的证书。该证书是服务器证书。【涉及文件:/var/lib/kubelet/pki 目录下的 kubelet.crt kubelet.key,由 /etc/kubernetes/pki/ca.crt 集群根证书生成】
11、kube-apiserver 作为客户端访问 kubelet 采用的证书。该证书是客户端证书。【涉及文件:apiserver-kubelet-client.key、apiserver-kubelet-client.crt】
12、kube-controller-manager 用于生成和验证 service-account token 的证书。该证书并不会像其他证书一样用户身份认证,而是将证书中的 “公钥/私钥对” 用于 service account token 的生成和验证。 整个过程是 :kube-controller-manager 会用该证书的 私钥 来生成 service account token,然后以 secret 的方式加载到 pod 中。pod 中的应用可以使用该 token 来访问 kube-apiserver,kube-apiserver收到请求后,会使用该证书的 公钥 来验证取出请求中的token 。 【涉及的文件:sa.key私钥与sa.pub公钥,sa就是 serviceAccount 的简称】
从以上12条中,pod和人访问 apiserver,apiserver 访问 etcd, apiserver 和 kubelet 可以相互访问
情况1:pod访问apiserver:5 6 7,不需要证书,直接 serviceAccount
情况2:人访问 apiserver :9 ,需要 kubectl /root/.kube/config
情况3:apiserver 访问 etcd:3 ,需要证书 apiserver-etcd-client.key 和 apiserver-etcd-client.crt
情况4:apiserver 和 kubelet 相互访问:8 11,需要证书(apiserver-kubelet-client.key、apiserver-kubelet-client.crt) 和 证书 ( /etc/kubernetes/ca.crt )
情况5:其他的:1 表示 etcd 直接平等访问证书,2 表示 etcd 作为服务端验证来自apiserver客户端访问证书( server.key server.crt ),4表示 apiserver 作为服务端验证来自 人/Pod/kubelet 对它的访问 (apiserver.key apiserver.crt ),10 表示kubelet作为服务端认证来自 apiserver 的访问证书 ( /var/lib/kubelet/config.yaml ), 12 表示 pod 访问kube-apiserver 的 sa 证书 (sa.key sa.pub)。
因为Pod的创建、销毁是动态的,所以要为它手动生成x509客户端证书就不可行了,K8s使用了Service Account解决Pod 访问API Server的认证问题。所有以Pod运行的服务,如果要访问 kube-apiserver ,直接使用 service account 与 kube-apiserver进行认证。这样的服务包括 kube-proxy 、kube-scheduler、kube-controller-manager、coredns、calico-node、calico-manager (etcd 不需要访问 apiserver,直接apiserver 访问 etcd) 以及自定义的Pod,都是直接使用 serviceAccount 访问,但是 kubelet 是守护进程,不是以 Pod 运行,所以访问 kube-apiserver 需要证书,就是 /etc/kubernetes/kubelet.conf x509客户端证书。
默认情况下,每个 namespace 都会有一个 ServiceAccount,如果 Pod 在创建时没有指定 ServiceAccount就会使用 Pod 所属的namespace 的 ServiceAccount, 默认值/run/secrets/kubernates.io/serviceaccount/.
示例:kube-proxy 是以 pod 形式运行的, 在 pod 中, 直接使用 service account 与 kube-apiserver进行认证,此时就不需要再单独为 kube-proxy 创建证书了, 直接使用token校验。
示例:kube-scheduler 是以 pod 形式运行的, 在 pod 中, 直接使用 service account 与 kube-apiserver进行认证,此时就不需要再单独为 kube-scheduler 创建证书了, 直接使用token校验。
示例:kube-controller-manager 是以 pod 形式运行的, 在 pod 中, 直接使用 service account 与 kube-apiserver进行认证,此时就不需要再单独为 kube-controller-manager 创建证书了, 直接使用token校验。
#随便查看kube-system命名空间下的pod
kubectl get pod -n kube-system
kubectl exec kube-proxy-cmzp6 -n=kube-system -it -- /bin/sh #进入容器
cd /run/secrets/kubernates.io/serviceaccount/
ls #里面有ca.crt(根的证书) namespace token 3个文件
#token是使用 API Server 私钥签名的JWT(json web token),用于Pod访问API Server时,对APIServer端签名认证
#ca.crt,根证书(是k8s中私有的)。用于Client端验证API Server发送的证书
#namespace, 标识这个service-account-token的作用域名空间
从上面的命令可以看到,Pod Container里面的 /run/secrets/kubernates.io/serviceaccount/ 目录下有三个文件,namespace ca.crt(根证书) token,那么,这三个文件的内容都是怎么来的?
(1) namespace:来源Pod所在命名空间;
(2) ca.crt 证书:来源 configmap kube-root-ca.crt 里面的 ca.crt 这个属性,其实就是 /etc/kubernetes/pki/ca.crt 证书
(3) token 来源是使用 API Server 私钥签名的JWT(json web token),用于Pod访问API Server时,对APIServer端签名认证
继续介绍三个文件,namespace不用多说,内容就是 metadata.namespace
看一下 configmap 是如何存放 ca.crt ,就是字符串,如下:
另外, Pod 里面 sa 目录下的 ca.crt 就是 master 节点的 /etc/kubernetes/pki/ca.crt,证据如下:
最后,除了Pod容器里面的三个文件,还有 master 宿主机上的两个文件。service Account 作为服务账号,包含 service Account密钥对,包括私钥 sa.key、公钥 sa.pub 。这个 密钥对 是提供给 kube-controller-manager 使用,这个 kube-controller-manager 被称为 控制器管理者,是用来管理 Pod控制器的。那么,是如何管理的呢?
答案是:kube-controller-manager通过私钥 sa.key 对 token 进行签名,然后 master 节点通过公钥 sa.pub 进行签名的验证。
要彻底搞懂Pod访问APIServer的,就搞懂五个文件之间的关系,即:
(1) master节点上的 /etc/kubernetes/pki 目录下的 ca.crt sa.key sa.pub
(2) Pod Container上的 /run/secrets/kubernates.io/serviceaccount/ 目录下的 ca.crt token
五个文件分成两组:
(1) ca.crt: master节点上的 /etc/kubernetes/pki 目录下的 ca.crt 和 Pod Container 上的 ca.crt 就是同一个文件,就是 APIServer 的 ca 根证书
(2) token: 使用私钥 sa.key 给 token 签名,然后使用公钥 sa.pub 给 token 做签名验证
生成 jwt token : header + payload + sa.key -> jwt token,根据 header payload sa.key 生成 jwt token
解析 jwt token : jwt token -> header + payload ,从jwt token中得到 header 和 payload ,然后用 sa.pub 验证这个 jwt token 是哪个客户端发送过来的,如下图
最后,回答一个问题:JWT 到底是什么?他和五个文件的关系是什么?
JWT 是 JSON WEB TOKEN, JWT包含三部分,分别为: Header,Payload,Signature,之间用"."分隔,所以一般形式为xxx.yyy.zzz。JWT 就是Pod里面的三种文件中的 token 文件,由 sa.key 加密生成,sa.pub 公钥验证,和 Pod里面的其他两个文件 namespace 和 ca.crt 关系不大。唯一的关系就是 jwt token + ca.crt 构成完成 curl 请求,Pod 访问 apiserver 的请求。
JWT工作原理:JWT是服务端发给客户端的一种加密凭证(通过RSA或者密码加密),客户端访问服务端(此不一定是发布凭证的服务端)时携带上这个凭证,服务端解密此凭证,验证通过就可以允许客户端访问。
在k8s中,使用RSA私钥/公钥进行加密和验证,
kube-controller-manager 使用如下参数指定私钥,对token进行签名 --service-account-private-key-file = /etc/kubernets/pki/sa.key
kube-apiserver 使用如下参数指定公钥,对token进行验证 --service-account-key-file = /etc/kubernets/pki/sa.pub
所以,在 kube-controller-manager 和 kube-apiserver 的启动参数中
kube-apiserver
–admission-control=*,serviceaccount [apiserver收到了Pod里面的curl进程发来的请求,Header中包含了"Authoriztion: Bearer ${ServiceAccountToken}"。apiserver作为接收端要想认证这个ServiceAccountToken,必须要开启serviceaccount的准入机制]
–service-account-key-file=/etc/kubernetes/pki/sa.pub [sa.pub用来验证 jwt token 签名]
kube-controller-manager
–service-account-private-key-file=/etc/kubernetes/pki/sa.key [sa.key用来对jwt token进行签名]
–root-ca-file=/etc/kubernetes/pki/ca.crt
整个过程:控制器 controller 是管理和组织 Pod 的,而 controller-manager 又是管理 控制器controller 的,
私钥 sa.key 提供给 kube-controller-manager 使用,对于 Pod 发送给 APIServer 的请求 header + payload ,被 kube-controller-manager 拦截/管理, header + payload + sa.key -> jwt token ,将请求加上 sa.key 构成一个 jwt token ,然后再发送给 apiserver,最优 master 节点的 kube-apsierver 收到这个 jwt token 请求,jwt token -> header +payload ,从jwt token 中取出 header 和 payload,并且通过公钥 sa.pub 进行 jwt 签名进行验证。
Pod访问APIServer同时需要JWT Token和ca.crt两个文件
下面演示:Pod需要访问APIServer的时候,使用是 sa 账号,本质就是 jwt token + ca.crt 两个文件缺一不可
curl -H "Authoriztion: Bearer ${ServiceAccountToken}" \
--cacert ${CaCrt} https://kubernetes:443/api/v1/nodes
curl -H "Authoriztion: Bearer xxx" \
--cacert "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt" \
https://kubernetes:443/api/v1/nodes
APIServer认证:apiserver收到了Pod里面的curl进程发来的请求,Header中包含了"Authoriztion: Bearer ${ServiceAccountToken}"。那么apiserver如何认证这个ServiceAccountToken呢?首先,apiserver必须要开启serviceaccount的准入机制,即在启动参数中声明–admission-control=*,serviceaccount。然后,apiserver的serviceaccount认证器拿到这个ServiceAccountToken后,利用参数–service-account-key-file指定的公钥文件对这个token进行验证。验证通过后,该token就会被认证为subject: { kind: ServiceAccount, name: peng, namesapce: default}以及subject: { kind: User, name: system:serviceaccount:default:peng}
APIServer授权:因为 jwt token就会被认证为subject: { kind: ServiceAccount, name: peng, namesapce: default}以及subject: { kind: User, name: system:serviceaccount:default:peng},所以,只要对两个中的其中一个授权,就可以解决 403 权限不足的问题。
如果认证不通过,报错 401 unauthorized
如果授权不通过,报错 403 没有权限
认证-授权-准入控制-限流都是APIServer的内容
认证包括两种:人访问APIServer、Pod访问APIServer(包括内部组件访问APIServer)
但是认证通过之后,授权只有一个,就是RBAC授权:用户User/用户组UserGroup/ServiceAccount - RoleBinding/ClusterRoleBinding - Role/ClusterRole
方式1:从User入手,对于kubectl使用的User绑定有权限的Role
# 新建binding
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: approve-list-nodes
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: list-nodes
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: system:anonymous # 对于使用的User绑定有权限的Role
# 新建binding需要使用到的Role(Role下面绑定权限)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole # 新建ClusterRole赋予权限
metadata:
name: list-nodes
rules:
- apiGroups:
- ""
resources:
- nodes
verbs:
- list
方式2:从ServiceAccount入手,对于Pod使用的ServiceAccount绑定有权限的Role
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding # 新建ClusterRoleBinding,绑定 Pod使用的sa 和 需要新建的ClusterRole
metadata:
name: approve-list-nodes
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: list-nodes
subjects:
- kind: ServiceAccount
name: peng
namespace: default
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole # 新建ClusterRole赋予权限
metadata:
name: list-nodes
rules:
- apiGroups:
- ""
resources:
- nodes
verbs:
- list
容器里面执行(必须 token+ca.crt ,少了 ca.crt 不可以)
(1) curl 语句中只有 jwt token 作为 Bearer , 没有 ca.crt ,无法成功
(2) curl 语句中有 jwt token 作为 Bearer , 还有 ca.crt ,成功
宿主机上执行
(1) curl 语句中有 jwt token 作为 Bearer , 还有 ca.crt,但是kubernetes域名无法解析(只有容器里面才能解析 serviceName.ns)
(2) curl 语句中有 jwt token 作为 Bearer , 还有 ca.crt,但是443端口不通
(3) curl 语句中有 jwt token 作为 Bearer , 还有 ca.crt ,使用 192.168.100.151:6443 成功
宿主机上执行(必须 token+ca.crt ,少了 ca.crt 不可以)
(1) curl 语句中只有 jwt token 作为 Bearer , 没有 ca.crt ,无法成功
(2) curl 语句中有 jwt token 作为 Bearer , 还有 ca.crt ,成功
(1) 执行 describe secret 取出里面的jwt token
(2) curl 语句中只有 jwt token 作为 Bearer , 没有 ca.crt ,无法成功
(3) curl 语句中有 jwt token 作为 Bearer , 还有 ca.crt ,成功 (curl里面的文件最好写成绝对路径,写成相对路径会报错找不到)
所以,使用 sa 认证的时候,ca.crt 是必须的,无论是在Pod Container里面,还是在宿主机上。
最后,如果是Postman上请求,
(1) 将SSL Certificate 关掉,可以不需要 ca.crt
(2) 将SSL Certificate 关掉,需要配置 ca.crt ,在Setting中配置
Postman情况2:将 ca.crt 证书放到 Postman Settting 中,并配置上匹配的 ip:port
小结:
(1) Pod 使用 sa 访问 APIServer 的这种方式,人(human) 也可以使用这种,就是 bearer token 持有者令牌。
(2) 无论是 Pod 访问 apiserver ,还是 人访问 apiserver ,本质上一样的,都是 jwt token + ca.crt
请求的时候,Pod 发出 header 和 payload 构成消息,但是在 controller-manager 这一层,被加上了四 sa.key
header + payload + sa.key -> jwt token .
(3) apiserver 自带 sa.pub ,用来验证使用 sa.key 加密的 jwt token
(4) jwt token 用于 apiserver 认证,ca.crt 用于 ssl 验证。
(1) sa 等效 bearer token:Pod 使用 sa 访问 等效 人使用 bearer token 访问
(2) controller-manager 带有 sa.key 启动参数:controller-manager 的启动参数中带有私钥 sa.key ,在 /etc/kubernetes/manifests/kube-controller-manager.yaml 文件可以看到,之所以带有这个 sa.key ,就是为了给 Pod 访问 APIServer 的请求体(header+payload) 做数字签名,然后构成 bearer token 发送给 APIServer
(3) apiserver 带有 sa.pub 启动参数:apiserver 的启动参数中带有公钥 sa.pub,在 /etc/kubernetes/manifests/kube-apiserver.yaml 文件可以看到,之所以带有这个 sa.pub ,就是为了收到 jwt token ,解码出 header+payload ,最后使用 sa.pub 验证签名
(4) jwt token + ca.crt 两个文件各有其用,缺一不可:无论是 Pod 还是 人,jwt token + ca.crt 两个文件缺一不可,jwt token 是为了通过 apiserver 认证,否则报错 401 unauthorized,而 ca.crt 为了完成 ssl 认证,因为 apiserver 的接口都是 https 的,当然 postman 访问的时候,也可以加上忽略 ssl 验证。
人访问apiserver使用的是包括三种:匿名方式、x509客户端证书、bearer serviceAccount(ns生成或手动生成)
其中最重要的就是 x509客户端证书 方式,分为 APIServer 服务端和 human(人) 客户端两部分
要连接并访问K8S APIServer成功,要同时满足两个条件:
一是要客户端接入进去(上述三种方式任意一种),
二要是服务端允许的ip列表
这里涉及到五个证书文件(区别和联系)
区别:
服务端:apiserver.key 和 apiserver.crt 是 k8s 服务端,接收 ip 连接的列表
客户端:ca.crt client.key client.crt 是 k8s 客户端,是 /etc/kubernetes/admin.conf 或者 /root/.kube/config 文件构成
联系:/etc/kubernetes/admin.conf 或者 /root/.kube/config 文件里面那个 ip:6443 , 那个 ip 必须在 apiserver.key / apiserver.crt 文件里面
对于常用的可视化页面,dashboard和kuboard,均有两种方式连接到 k8s
dashboard两种:kubeconfig(x509客户端证书)、bearer Token
kuboard两种:kubeconfig(x509客户端证书)、kube-agent
小结:连接k8s三种最重要的方式 dashboard用了两种,kuboard使用了一种,并增加一种kube-agent
etcd交互包括两种情况:apiserver访问etcd、etcd相互访问
对于apiserver访问etcd,apiserver是客户端,etcd是服务端,由于需要双向认证,客户端要验证服务端,服务端也要验证客户端,所以需要客户端证书、客户端密钥、服务端证书、服务端密钥
apiserver端需要访问etcd的证书:apiserver-etcd-client.crt与apiserver-etcd-client.key
etcd端需要验证apiserver的请求:/etc/kubernetes/pki/etcd 目录下的 server.crt 和 server.key
客户端证书 apiserver-etcd-client.crt 和 服务端证书 /etc/kubernetes/pki/etcd 目录下的 server.crt ,都是通过 /etc/kubernetes/pki/etcd 目录下的 ca.crt 根证书来颁发的,这是双向认证成功的关键。
apiserver-etcd-client.key 和 apiserver-etcd-client.crt 证书 (APIServer访问Etcd)
APIServer 访问 Etcd: APIServer 是客户端,Etcd 是服务端,因为是双向认证,客户端要验证服务端确实是 Etcd,服务端也要验证客户端确实 APIServer。
APIServer需要 /etc/kubernetes/pki 目录下 apiserver-etcd-client.key 和 apiserver-etcd-client.crt ,即 客户端证书(包含公钥)、客户端私钥
Etcd 需要 /etc/kubernetes/pki/etcd 目录下的 server.key 和 server.crt ,即 服务端证书(包含公钥)、服务端私钥
对于 /etc/kubernets/pki/apiserver-etcd-client.crt 证书
Issuer: Common Name=etcd-ca 表示颁发这个证书的ca根证书的 Subject: CN 用户名是 etcd-ca,而这样的ca根证书恰好就是 /etc/kubernets/pki/etcd/ca.crt 证书,所以这个证书使用 etcd 的 ca 根证书颁发的,用来 apiserver访问 etcd (访问可以成功的基础是服务端的 /var/kubernetes/pki/etcd/server.crt 证书也是 etcd 的 ca 根证书颁发的)
Subject: Organization=system:masters Common Name=apiserver-etcd-client 表示这个 apiserver-etcd-client.crt 证书的用户组是system:masters 用户名是 apiserver-etcd-client ,可以使用 RBAC 查看其绑定了权限
kubectl get rolebinding -o wide -A | grep "system:masters"
kubectl get clusterrolebinding -o wide -A | grep "system:masters"
kubectl get rolebinding -o wide -A | grep "apiserver-etcd-client"
kubectl get clusterrolebinding -o wide -A | grep "apiserver-etcd-client"
kubectl get clusterrole cluster-admin -o yaml
OpenSSL提供读取不同类型的证书和编码格式。OpenSSL支持RSA、X509、PCKS12等证书格式。一般来说,证书使用 x509 格式,私钥采用 rsa 格式。
/etc/kubernetes/pki/etcd 目录下的 server.key 和 server.crt 证书 (Etcd作为服务端的rsa服务端密钥、x509服务端证书)
对于 /etc/kubernets/pki/etcd/server.crt 证书
Issuer: Common Name=etcd-ca 表示颁发这个证书的ca根证书的 Subject: CN 用户名是 etcd-ca,而这样的ca根证书恰好就是 /etc/kubernets/pki/etcd/ca.crt 证书,所以这个证书使用 etcd 的 ca 根证书颁发的,用来 etcd 作为服务端等待 apiserver 的请求访问(访问可以成功的基础是客户端的 apiserver-etcd-client.crt 证书也是 etcd 的 ca 根证书颁发的)
Subject: CN=w1 表示这个 apiserver-etcd-client.crt 证书的用户名是 w1 ,可以使用 RBAC 查看其绑定了权限(因为是服务端,所以不会有绑定的 role/clusterrole ,因为不需要授权)
对于etcd各个节点之间相互访问,需要节点间相互通信 peer证书:pki/etcd/peer.crt与pki/etcd/peer.key
对于 /etc/kubernets/pki/etcd/server.crt 证书
Issuer: Common Name=etcd-ca 表示颁发这个证书的ca根证书的 Subject: CN 用户名是 etcd-ca,而这样的ca根证书恰好就是 /etc/kubernets/pki/etcd/ca.crt 证书,所以这个证书使用 etcd 的 ca 根证书颁发的,用来 etcd 作为服务端相互访问(访问可以成功的基础是客户端的 apiserver-etcd-client.crt 证书也是 etcd 的 ca 根证书颁发的)
Subject: CN=w1 表示这个 apiserver-etcd-client.crt 证书的用户名是 w1 ,可以使用 RBAC 查看其绑定了权限
Pod中有三种探针:生存性探针、可读性探针、启动性探针。Pod中Liveness探针客户端证书与私钥:pki/etcd/healthcheck-client.crt与pki/etcd/healthcheck-client.key,这个证书是用于Pod中liveness生存性探针健康检查,用于让服务端检查客户端身份使用的。
对于 /etc/kubernets/pki/etcd/healthcheck-client.crt 证书
Issuer: Common Name=etcd-ca 表示颁发这个证书的ca根证书的 Subject: CN 用户名是 etcd-ca,而这样的ca根证书恰好就是 /etc/kubernets/pki/etcd/ca.crt 证书,所以这个证书使用 etcd 的 ca 根证书颁发的,用来 etcd 作为服务端相互访问(访问可以成功的基础是客户端的 apiserver-etcd-client.crt 证书也是 etcd 的 ca 根证书颁发的)
Subject: O=system:masters, CN=kube-etcd-healthcheck-client 表示这个 healthcheck-client.crt 证书的用户组是 system:masters , 用户名是 kube-etcd-healthcheck-client ,可以使用 RBAC 查看其绑定了权限
注意1:因为这个 healthcheck-client.crt 证书只有master节点上有,所以这个证书的用户组是 system:masters
注意2:看名字 healthcheck-client.crt 里面是 client.crt 后缀结尾,这是一个客户端证书,所以这个证书 用户组/用户一定绑定的 RBAC
注意3:既然是x509客户端证书,所以访问的服务端的证书也一定是使用 etcd-ca /etc/kubernetes/pki/etcd/ca.crt 根证书颁发的
etcd所有证书都是根据 ca根证书 生成的,所以需要 etcd根证书与私钥:pki/etcd/ca.crt与pki/etcd/ca.key
来验证一下其他证书是如何通过 ca根证书生成的,开始:
cd /etc/kubernetes/pki/etcd
mkdir test
cp ca.key test/
cp ca.crt test/
cp server.key test/
# 根据 server.key 生成 server.csr, csr是请问文件,所以命令是 openssl req
openssl req -new -key server.key -out server.csr -days 365
# 根据 server.csr 生成 server.crt, crt 是 x509 etcd服务端证书,所以命令是 openssl x509
openssl x509 -req -CA ca.crt -CAkey ca.key -CAcreateserial -in server.csr -out server.crt -days 365
etcd 目录下有八个证书,如下:
根证书(ca.crt,ca.key)
服务端证书(server.crt,server.key):用于让客户端验证服务端身份使用的。
健康检查客户端证书(healthcheck-client.crt, healthcheck-client.key):用于让服务端检查客户端身份使用的
对等端证书(peer.crt, peer.key):用于peer间双向验证
[root@w1 etcd]# ll
总用量 32
-rw-r--r--. 1 root root 1058 4月 17 16:21 ca.crt
-rw-------. 1 root root 1679 4月 17 16:21 ca.key
-rw-r--r--. 1 root root 1159 4月 17 16:21 healthcheck-client.crt
-rw-------. 1 root root 1679 4月 17 16:21 healthcheck-client.key
-rw-r--r--. 1 root root 1180 4月 17 16:21 peer.crt
-rw-------. 1 root root 1679 4月 17 16:21 peer.key
-rw-r--r--. 1 root root 1180 4月 17 16:21 server.crt
-rw-------. 1 root root 1675 4月 17 16:21 server.key
这些证书是怎么来的,大致生成步骤如下:
(1) 首先,先生成 ca.key 和 ca.crt 两个文件
(2) 对于剩下六个文件,先随机生成 xxx.key 密钥文件,然后根据 xxx.key 、 ca.key 和 ca.crt 两个文件,生成 xxx.crt
知道了原理之后,其实,我们自己将这个目录下的证书都生成一遍,如下:
# 步骤1:生成 ca.key
(umask 077;openssl genrsa -out /etc/kubernetes/pki/etcd/test2/ca.key 4096)
# 步骤2:生成 ca.crt
openssl req -new -x509 -key /etc/kubernetes/pki/etcd/test2/ca.key -out /etc/kubernetes/pki/etcd/test2/ca.crt -days 3655
# 步骤3:生成 server.key
openssl genrsa -out /etc/kubernetes/pki/etcd/test2/server.key 2048
# 步骤4:根据 server.key 生成 server.csr, csr是请问文件,所以命令是 openssl req
openssl req -new -key server.key -out server.csr -days 365
# 步骤5:根据 server.csr 生成 server.crt, crt 是 x509 etcd服务端证书,所以命令是 openssl x509
openssl x509 -req -CA ca.crt -CAkey ca.key -CAcreateserial -in server.csr -out server.crt -days 365
(1) APIServer根证书与私钥:ca.crt与ca.key
(2) apiserver服务端的证书与私钥:apiserver.crt与apiserver.key
(3) apiserver访问kubelet的证书与私钥:apiserver-kubelet-client.crt与apiserver-kubelet-client.key
(4) kubelet访问apiserver的x509客户端证书:/etc/kubernetes/kubelet.conf(运行时) 或 /etc/kubernetes/bootstrap-kubelet.conf(启动时)
kubelet要主动访问kube-apiserver,kube-apiserver也需要主动向kubelet发起请求。因为相互访问是双向认证,所以双方都需要有自己的根证书以及使用该根证书签发的服务端证书和客户端证书。
kube-apiserver | kubelet | |
---|---|---|
根证书 | /etc/kubernetes/pki/ca.crt | /etc/kubernetes/pki/ca.crt |
根证书签发的客户端证书 | apiserver-kubelet-client.crt与apiserver-kubelet-client.key | kubelet-client-current.pem(包含kubelet-client.crt和kubelet-client.key) |
根证书签发的服务端证书 | apsierver.key 和 apiserver.crt | kubelet.crt 和 kubelet.key |
在kube-apiserver启动配置中, 一般明确指定用于https访问的服务端证书和带有CN用户名信息的x509客户端证书,都在 /etc/kubernetes/pki 目录下。
而在kubelet的启动配置中, 一般只指定了ca根证书,而没有明确指定用于https访问的服务端证书。(不指定服务端证书的)理由是:在生成服务端证书时, 一般会指定服务端地址或主机名。
kube-apiserver相对变化不是很频繁,所以在创建集群之初就可以预先分配好用作 kube-apiserver的IP 或主机名/域名。但是部署在node节点上的kubelet会因为集群规模的变化而频繁变化,而无法预知node的所有IP信息,所以kubelet上一般不会明确指定服务端证书,而是只指定ca根证书,让kubelet根据本地主机信息自动生成服务端证书并保存到cert-dir文件夹中。
如上图,/var/lib/kubelet/pki 目录, pki 目录就是用来存放证书的 (apiserver的证书也在 /etc/kubernetes/pki 目录下)
apiserver 访问 kubelet :apiserver是客户端,kubelet是服务端。
apiserver 准备 /etc/kubernetes/pki 目录下 apiserver-kubelet-client.crt与apiserver-kubelet-client.key,客户端证书和客户端密钥
kubelet 准备 /va/lib/kubelet/pki 目录下 kubelet.crt 和 kubelet.key,服务端证书和服务端密钥
apiserver-client私钥签名 - kubelet公钥加密 - kubelet私钥加密 - apiserver-client公钥验证
# 首先要根据私钥生成公钥
openssl rsa -in apiserver-kubelet-client.key -pubout -out apiserver-kubelet-client.pub
openssl rsa -in kubelet.key -pubout -out kubelet.pub
vi sign.txt
this is a message from apiserver to kubelet
# apiserver私钥签名
openssl dgst -sha1 -sign /etc/kubernetes/pki/apiserver-kubelet-client.key -out sign.txt.signed sign.txt
# kubelet公钥加密
openssl rsautl -in sign.txt.signed -out sign2.txt.signed -inkey /var/lib/kubelet/pki/kubelet.pub -pubin -encrypt
# kubelet私钥解密
openssl rsautl -in sign2.txt.signed -out sign3.txt.signed -inkey /var/lib/kubelet/pki/kubelet.key -decrypt
# apiserver公钥验签
openssl dgst -sha1 -verify /etc/kubernetes/pki/apiserver-kubelet-client.pub -signature sign3.txt.signed sign.txt
过程是这样的,但是无法演示出来,因为 apiserver.crt 签名后的文件很大,openssl rsautl 无法加密了,这个非对称加密局限性
vi sign.txt
this is a message from apiserver to kubelet
# apiserver私钥签名
openssl dgst -sha1 -sign /etc/kubernetes/pki/apiserver-kubelet-client.key -out sign.txt.signed sign.txt
# apiserver公钥验签
openssl dgst -sha1 -verify /etc/kubernetes/pki/apiserver-kubelet-client.pub -signature sign.txt.signed sign.txt
vi sgin.txt
this is a message from apiserver to kubelet
# kubelet公钥加密
openssl rsautl -in sign.txt -out sign2.txt -inkey /var/lib/kubelet/pki/kubelet.pub -pubin -encrypt
# kubelet私钥解密
openssl rsautl -in sign2.txt -inkey /var/lib/kubelet/pki/kubelet.key -decrypt
这个部分演示了公钥加密私钥解密,私钥签名公钥验证的整个过程,但是无法连起来演示,因为 openssl rsautl 无法处理已经被签名的文件,这个文件太大了。
apiserver-kubelet-client.key 和 apiserver-kubelet-client.crt 证书 (APIServer访问Kubelet)
对于 /etc/kubernets/pki/apiserver-kubelet-client.crt 证书
Issuer: Common Name=kubernetes 表示颁发这个证书的ca根证书的 Subject: CN 用户名是 kubernetes ,而这样的ca根证书恰好就是 /etc/kubernets/pki/ca.crt 证书,所以这个证书使用 apiserver 的 ca 根证书颁发的,用来 apiserver访问 kubelet (访问可以成功的基础是服务端的 /var/lib/kubelet/kubelet.crt 证书也是 apiserver 的 ca 根证书颁发的)
Subject: Organization=system:masters Common Name=apiserver-kubelet-client 表示这个 apiserver-kubelet-client.crt 证书的用户组是system:masters 用户名是 apiserver-kubelet-client ,可以使用RBAC查看权限
kubectl get rolebinding -o wide -A | grep "system:masters"
kubectl get clusterrolebinding -o wide -A | grep "system:masters"
kubectl get rolebinding -o wide -A | grep "apiserver-kubelet-client"
kubectl get clusterrolebinding -o wide -A | grep "apiserver-kubelet-client"
问题1:为什么用户 apiserver-kubelet-client 是属于用户组 system:masters
回答:因为这个证书 apiserver-kubelet-client.crt 只会在 master 节点上有,所以这个用户属于 system:masters 用户组;因为这个证书 apiserver-kubelet-client.crt 是给 apiserver 使用的,所以这个证书只会在主节点有。
问题2:为什么用户组 system:masters 或 用户 apiserver-kubelet-client 可以查询到 RBAC RoleBinding
回答:因为此时是 apiserver 作为客户端访问 kubelet,客户端使用的 用户组/用户 必须绑定 Role 获得权限,否则没有意义。
/var/lib/kubelet/pki 目录下的 kubelet.key 和 kubelet.crt 证书 (Kubelet作为服务端等待ApiServer的访问)
对于 /var/lib/kubelet/pki/kubelet.crt 证书
Issuer: CN=w1-ca@1681719673 表示颁发这个证书的ca根证书的 Subject: CN 用户名是 w1-ca@1681719673 ,而这样的ca根证书恰好就是 /etc/kubernets/pki/ca.crt 证书,所以这个证书使用 kubernetes 的 ca 根证书颁发的,用来 apiserver访问 kubelet (访问可以成功的基础客户端的 apiserver-kubelet-client.crt 证书也是 kubernetes/apiserver 的 ca 根证书颁发的)
Subject: CN=w1@1681719673 表示这个 kubelet.crt 证书用户名是 w1@1681719673 ,可以使用RBAC查看权限(服务端一定查询不到绑定 role/clusterrole,因为服务端不需要授权)
kubectl get rolebinding -o wide -A | grep "w1@1681719673"
kubectl get clusterrolebinding -o wide -A | grep "w1@1681719673"
kubelet 访问 apiserver :kubelet是客户端,apiserver是服务端
kubelet 准备 /va/lib/kubelet/pki 目录下 kubelet-client-current.pem,其实包含 kubelet-client.crt 和 kubelet-client.key,客户端证书和客户端密钥
apiserver 准备 /etc/kubernetes/pki 目录下 apiserver.crt与apiserver.key,服务端证书和服务端密钥
openssl rsa -in apiserver.key -pubout -out apiserver.pub
openssl rsa -in client.key -pubout -out client.pub
注意:
(1) 证书是 .crt/.pem 文件,里面包含了公钥.pub ,而私钥 .key 不在证书里,是单独的文件。
(2) 无论是 apiserver 还是 kubelet ,都依赖 /etc/kubernetes/pki/ca.crt ,其他证书都是ca.crt 根证书生成的。不同节点的 /etc/kubernetes/pki/ca.crt 文件是相同的,但是和 /etc/kubernetes/pki/etcd/ca.crt 文件不相同。
openssl x509 -in kubelet-client-current.pem -out client.crt
openssl rsa -in kubelet-client-current.pem -out client.key
/var/lib/kubelet/pki 目录下的 client.key 和 client.crt 证书
对于 client.crt 证书 ,
Issuer: COMMON NAME=kubernetes 表示颁发这个证书的ca根证书的 Subject: CN 用户名是 kubernetes,而这样的ca根证书恰好就是 /etc/kubernets/pki/ca.crt 证书,所以这个证书使用 kubernetes 的 ca 根证书颁发的,用来 kubelet 访问 apiserver (访问可以成功的基础是服务端的 /etc/kubernetes/pki/apiserver.crt 证书也是 kubernetes的 ca 根证书颁发的)
Subject: Organization=system:nodes, Common Name=system:node:w1
COMMON NAME=kubernetes 表示这个 client.crt 证书的用户组是system:nodes, 用户组是节点组,用户名是 system:node:w1 ,用户名是节点组下一个节点,每个节点的用户名不一样,可以使用 RBAC 查看其绑定了权限(这里 client.crt 是客户端证书,客户端证书里面的 用户组/用户 需要绑定 role/clusterrole 来获得权限,否则无法操作 k8s 里面的资源)
kubectl get rolebinding -o wide -A | grep "system:nodes"
kubectl get clusterrolebinding -o wide -A | grep "system:nodes"
kubectl get rolebinding -o wide -A | grep "system:node:w1"
kubectl get clusterrolebinding -o wide -A | grep "system:node:w1"
apiserver.key 和 apiserver.crt 证书
对于 /etc/kubernets/pki/apiserver.crt 证书
Issuer: Common Name=kubernetes 表示颁发这个证书的ca根证书的 Subject: CN 用户名是 kubernetes,而这样的ca根证书恰好就是 /etc/kubernets/pki/ca.crt 证书,所以这个证书使用 kubernetes 的 ca 根证书颁发的,用来 apiserver作为服务端等待别人的访问(访问可以成功的基础是服务端的 /var/lib/kubelet/pki/client.crt 证书也是 kubernetes的 ca 根证书颁发的)
Subject: Common Name=kube-apiserver 表示这个 apiserver.crt 证书的用户名是 kube-apiserver,可以使用 RBAC 查看其绑定了权限 (这里 apiserver.crt 是服务端证书,服务端证书里面的 用户组/用户 本来就不需要什么权限的)
kubectl get rolebinding -o wide -A | grep "kube-apiserver"
kubectl get clusterrolebinding -o wide -A | grep "kube-apiserver"
根证书是/etc/kubernetes/pki/front-proxy-ca.crt/front-proxy-ca.key,由此根证书签发的证书只有一组:代理端使用的客户端证书 front-proxy-client.crt , 用作 代用户与 kube-apiserver 认证
业务场景:kubectl proxy 新建了一个server服务端,打开了 insecure 端口,curl 请求可以不带证书来访问这个端口
问题:为什么windows上的Postman访问不了?
回答:Local Address的0.0.0.0代表本机上可用的所有任意地址,Foreign Address的0.0.0.0代表任意外部地址,虽然监听的外部地址是任意地址,但是绑定的本地地址是127.0.0.1,而外部机器输入的127.0.0.1不会解析成本机,而是外部机器本身,因此,外部机器是无法连接7777端口的,只有本机的任意ip可以连接到这个7777端口。
(1) 代理根证书与私钥:front-proxy-ca.crt与front-proxy-ca.key
(2) 由代理根证书签发的客户端证书与私钥(front-proxy-client.crt与front-proxy-client.key)。比如使用kubectl proxy代理访问时,kube-apiserver使用这个证书来验证客户端证书是否是自己签发的证书
对于 front-proxy-ca.crt 证书 ,
Issuer: COMMON NAME=front-proxy-ca
Subject: Common Name=front-proxy-ca
这里表示这个 front-proxy-ca.crt 证书自己本身就是一个 CA 证书
对于 front-proxy-client.crt 证书 ,
Issuer: COMMON NAME=front-proxy-ca
Subject: Common Name=front-proxy-client
这里表示这个 front-proxy-client.crt 证书是通过 front-proxy-ca.crt 证书颁发的
对于任何证书,我们都是结合业务场景,客户端、服务端、双向认证,这样才可以最好的理解这个证书的在业务场景中的作用。
从节点上仅仅运行了一个 kubelet 守护进程,没有运行静态Pod,我们看对这个 kubelet 服务一探究竟,这个服务的功能是什么,需要哪些文件和证书,这节全解析整个 kubelet 服务。
从节点目录结构
[root@w1 kubernetes]# tree
.
├── kubelet.conf # 用于 kubelet 访问 apiserver 的 x509 客户端证书 (启动是 /etc/kubernetes/kubelet-bootstrap.conf,稳定运行是 /etc/kubernetes/kubelet.conf)
└── pki
└── ca.crt # 整个集群根证书
1 directory, 2 files
为什么只有这个一个 ca.crt 证书呢?
因为从节点上运行 kubelet,不会运行其他四个静态Pod,而 kubelet 之和 apiserver 打交道,其证书在 /var/lib/kubelet/pki 目录下,对于 /etc/kubernetes/pki 目录下只有一个 ca.crt 集群根证书,这个证书是用来生成其他证书的,即用来生成这个node节点上的 /var/lib/kubelet/pki 目录下的 client 和 server 证书的。
这个证书又是干什么的呢?
这个证书是整个集群的根证书,每个node上都会有一个,是用来生成其他证书的,即用来生成这个node节点上的 /var/lib/kubelet/pki 目录下的 client 和 server 证书的。
这个 ca.crt 证书是如何与 kubelet 关联起来的?
就是在 kubelet 的配置文件里指定的 /var/lib/kubelet/config.yaml ,如下:
cat /var/lib/kubelet/config.yaml # 在config.yaml
x509:
clientCAFile: /etc/kubernetes/pki/ca.crt # 这个整个集群的根证书
kubelet和apiserver相互访问,因为内部组件之间需要双向认证,客户端要认证服务端,服务端也要认证客户端
所以,apiserver 需要 自己作为客户端时的证书和密钥,自己作为服务端时的证书和密钥 在 /etc/kubernetes/pki 目录下
kubelet 需要自己作为客户端时的证书和密钥,自己作为服务端时的证书和密钥 在 /var/lib/kubelet/pki 目录下
apiserver仅在主节点上运行,所以其 客户端/服务端证书和密钥 仅在主节点的 /etc/kubernetes/pki 目录下有
kubelet 在主节点和从节点上均有运行,所以其 客户端/服务端证书和密钥 在主节点和从节点的 /etc/kubernetes/pki 目录和 /var/lib/kubelet/pki 目录下都有
注意1:k8s任意主从节点,每个node上的/etc/kubernetes/pki/ca.crt都是一样的,即:整个集群使用的根证书都是一样的
注意2:k8s 主节点 /etc/kubernetets/pki/ca.crt 和 /etc/kubernetets/pki/etcd/ca.crt 证书是不一样的
systemctl status kubelet -l 命令可以查看 kubelet 的启动
/usr/bin/kubelet --bootstrap-kubeconfig=/etc/kubernetes/bootstrap-kubelet.conf
–kubeconfig=/etc/kubernetes/kubelet.conf
–config=/var/lib/kubelet/config.yaml
–network-plugin=cni
–pod-infra-container-image=k8s.gcr.io/pause:3.4.1
可以看 kubelet 就是通过 /etc/kubernetes/kubelet.conf 来启动的
和 kubelet 相关的配置文件都在 /var/lib/kubelet 目录下,如下:
[root@m ~]# cd /var/lib/kubelet/
[root@m kubelet]# ll
总用量 16
-rw-r--r--. 1 root root 876 3月 9 00:11 config.yaml
-rw-------. 1 root root 62 3月 9 00:12 cpu_manager_state
drwxr-xr-x. 2 root root 45 3月 18 20:33 device-plugins
-rw-r--r--. 1 root root 95 3月 9 00:11 kubeadm-flags.env
drwxr-xr-x. 2 root root 124 3月 9 00:11 pki
drwxr-x---. 2 root root 6 3月 9 00:11 plugins
drwxr-x---. 2 root root 6 3月 9 00:11 plugins_registry
drwxr-x---. 2 root root 26 3月 18 20:33 pod-resources
drwxr-x---. 15 root root 4096 3月 19 23:33 pods
[root@m kubelet]# pwd
/var/lib/kubelet
小结:
/etc/kubernetes/admin.conf 这个文件就是复制为 /root/.kube/config 文件,然后供 kubectl 访问 k8s 集群;
/etc/kubernetes/kubelet.conf 这个文件就是 systemctl status kubelet -l 这个守护进程的启动文件,删掉这个文件,kubelet服务无法启动
报错:tree command not foud
解决:yum -y install tree (能够使用 yum 安装是因为 /etc/yum.repos.d/xxx.repo 目录下配置了,这个 xxx.repo 包括指向 url 和指向本地 iso 包;)
这篇文章学到了K8S内部交互架构和所有证书。
参考资料:
k8s证书原理:各类证书作用
k8s证书浅析
安装高可用集群(手动分发证书)
ServiceAccount
JWT JSON Web Token 原理
使用kubeadm对证书进行管理
kubernets证书详情