详细分析Kubernetes管理面服务中用到的所有TLS证书

1. TLS认证

在 Kubernetes 的组件之间进行通信时,数字证书的验证是在协议层面通过 TLS 完成的,Client/Server 建立通信时在应用层面并不需要进行特殊处理。

采用 TLS 进行验证有两种方式:

  • 服务器单向认证:只需要服务器端提供证书(公钥),客户端通过服务器端证书验证服务的身份,但服务器并不验证客户端的身份
  • 双向 TLS 认证:除了客户端需要验证服务器的证书,服务器也要通过客户端证书验证客户端的身份。这种情况下服务器只允许特定身份的客户端访问。
    • 双向 TLS 认证时有一种特殊场景,即Client跳过对服务器的认证,只Server验证Client的身份
    • 一次双向认证中,涉及了以下证书:
      • 服务器端证书/私钥:证书中包含了服务器信息和公钥,会在网络中传递
      • 客户端证书/私钥:客户端用于证明自身身份的数字证书,里面主要包含了客户端的公钥以及客户端的身份信息。
      • 服务器端 CA 根证书:签发服务器端证书的 CA 根证书(客户端要能获取)。
      • 客户端端 CA 根证书:签发客户端证书的 CA 根证书(服务端要能获取)。
    • 如果使用kubeadm部署,通常服务端客户端是共用自签发CA(/etc/kubernetes/pki/ca.crt,key),只要获取这个文件就可以任意的签发访问k8s集群

1. PKI证书

Kubernetes 中使用PKI证书进行TLS双向认证的场景无处不在,

  • apiserver访问etcd
  • apiserver / kubelet 双向访问
  • controller-manager 、scheduler、proxy 访问apiserver
  • 其他服务访问 apiserver
  • controller-manager生成各种 service account token

比较重要的包括以下:

  • 在**/etc/kubernetes/pki目录下的ROOT CA**,集群中所有的证书文件都是通过以下文件生成的:

    • ca.crt,key(CN=kubernetes)
    • etcd/ca.crt,key(CN=etcd-ca)
    • front-proxy-ca.crt,key(CN=front-proxy-ca)
    • sa.key/sa.pub
  • 集群中比较重要的证书文件包括以下(基本都是apiserver使用的)

    证书文件名称 Parent CA Default CN O (in Subject) 备注
    apiserver.crt/key kubernetes kube-apiserver 服务端证书 Rest API
    apiserver-kubelet-client.crt/key kubernetes kube-apiserver-kubelet-client system:masters 客户端证书 **访问Kubelet **
    apiserver-etcd-client.crt/key etcd-ca kube-apiserver-etcd-client system:masters 客户端证书 etcd目录
    front-proxy-client.crt/key front-proxy-ca front-proxy-client 客户端证书 ??这个不知道访问啥的
    kubelet.crt/key -ca@ @ /var/lib/kubelet/pki**
    Kubelet的服务端证书?TLS bootstrapping方式自动生成并滚动**

    PS:其余ETCD目录还有证书文件

    证书文件名称 Parent CA Default CN O (in Subject)
    healthcheck-client.crt/key etcd-ca kube-etcd-healthcheck-client system:masters
    peer.crt/key etcd-ca 当前节点ETCD节点名称
    server.crt/key etcd-ca 当前节点ETCD节点名称 system:masters

2. Kubeconfig文件

kubeconfig 同样包含了认证信息,这些文件被作为 客户端认证文件(主要作为外部服务访问k8s集群时使用 来使用。

这些信息被Base64编码后保存在以下字段:

  • certificate-authority-data:根证书的公钥(ca.crt文件base64编码后的值)
  • client-certificate-data:客户端的公钥
  • client-key-data:客户端的Key
证书文件名称 Parent CA Default CN O (in Subject) 备注
admin.conf kubernetes kubernetes-admin system:masters kubectl管理员帐号
controller-manager.conf kubernetes system:kube-controller-manager controller使用
scheduler.conf kubernetes system:kube-scheduler scheduler使用
kubelet.conf kubernetes system:node:节点名称 system:nodes kubelet(访问API的客户端证书)

3. Sercet

Sercet本质上和conf文件是等价的(一些文档中称为Token方式认证),主要用于集群内访问k8s时使用。

2. Kubeadm

1. 证书相关操作

大部分操作位于 kubeadm init phase certs 目录下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 生成相关证书
certs                      Certificate generation
  /ca                        Generate the self-signed Kubernetes CA to provision identities for other Kubernetes components
  /apiserver                 Generate the certificate for serving the Kubernetes API
  /apiserver-kubelet-client  Generate the certificate for the API server to connect to kubelet
  /front-proxy-ca            Generate the self-signed CA to provision identities for front proxy
  /front-proxy-client        Generate the certificate for the front proxy client
  /etcd-ca                   Generate the self-signed CA to provision identities for etcd
  /etcd-server               Generate the certificate for serving etcd
  /etcd-peer                 Generate the certificate for etcd nodes to communicate with each other
  /etcd-healthcheck-client   Generate the certificate for liveness probes to healthcheck etcd
  /apiserver-etcd-client     Generate the certificate the apiserver uses to access etcd
  /sa                        Generate a private key for signing service account tokens along with its public key

PS:相关Config配置可以参考源码 cmd/kubeadm/app/apis/kubeadm/types.go 文件(以及 cmd/kubeadm/app/apis/kubeadm/v1beta2/types.go,俩个文件看着似乎差不多)

2. 更新证书

由于默认Kubeadm证书有效期只有1年,需要定期更新。

更新流程如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
```shell
#生成集群配置yaml
kubeadm config view > /root/cluster.yaml
# 将ETCD的证书文件软连接到 /etc/kubernetes/pki 目录下
rm -rf /etc/kubernetes/pki/etcd
ln -sf /etc/etcd /etc/kubernetes/pki/etcd
ln -sf /etc/etcd/apiserver-etcd-client.crt /etc/kubernetes/pki/apiserver-etcd-client.crt
ln -sf /etc/etcd/apiserver-etcd-client.key /etc/kubernetes/pki/apiserver-etcd-client.key

# 更新集群证书(这个命令会同时更新kubeconfig)
kubeadm alpha certs renew all

# 更新kubeconfig
kubeadm init phase kubeconfig kubelet

3. 修改证书超期时间

需要重新编译Kubeadm,通过Docker环境编译Kubernetes非常的便利!!

  • 获取基础镜像(build/build-image/Dockerfile),默认为 k8s.gcr.io/build-image/kube-cross:tag (PS:tag 信息保存在 build/build-image/cross/VERSION 文件)

  • 编译命令如下:

1
2
# 表示只编译kubeadm模块
KUBE_BASE_IMAGE_REGISTRY="registry.lqingcloud.cn/k8s.gcr.io/build-image" build/run.sh make kubeadm KUBE_BUILD_PLATFORMS=linux/amd64 

构建文档参考:https://github.com/kubernetes/kubernetes/tree/master/build

Kubeadm生成pki文件的Code为:cmd/kubeadm/app/util/pkiutil包中的NewSignedCert方法,其中CertificateValidity变量写死了超期时间

3. Kubelet 证书

1. Kubelet访问APIServer的证书配置

Kubelet 通过配置文件 kubelet.conf 来访问中的信息访问APIServer,新节点加入K8S集群时如何自动获取改文件,在官方文档**Kubelet TLS Bootstrapping**有非常细致的叙述,简单流程如下:

  • Kubelet通过Bootstrap tokensToken authentication file机制作为system:bootstrappers组的成员连接API Server
  • 创建CSR为自己申请一个Client证书
  • Controller自动批准该证书
  • Kubelet取回证书信息,生成kubelet.conf文件
  • 使用system:nodes组成员的身份访问API

使用kubeadm安装k8s集群时,会自动创建以下三个ClusterRoleBinding:

ClusterRoleBinding 绑定的Group 绑定的ClusterRole
kubeadm:kubelet-bootstrap system:bootstrappers system:node-bootstrapper(创建CSR)
kubeadm:node-autoapprove-bootstrap system:bootstrappers system:certificates.k8s.io:certificatesigningrequests:nodeclient(签发证书)
kubeadm:node-autoapprove-certificate-rotation system:nodes system:certificates.k8s.io:certificatesigningrequests:selfnodeclient(更新证书)

2. APIServer访问Kubelet的证书配置

Kubelet 通过10250提供对外HTTPS访问,10255 提供工HTTP访问,默认情况下,Kubelet认证部分的配置文件如下(1.16.x):

1
2
3
4
5
6
7
8
authentication:
  anonymous: 
    enabled: false   # 禁止匿名访问
  webhook:           # WebHook认证
    cacheTTL: 2m0s
    enabled: true
  x509:				# x509 认证
    clientCAFile: /etc/kubernetes/pki/ca.crt

同时开启时,WebHook和x509认证依次进行,有一个通过即可

  • 服务端证书和客户端证书都从集群CA签发
    • 从CA生成服务端证书kubelet-server.pem、kubelet-server-key.pem
    • 从CA生产APIServer用的客户端证书kubelet-client.pem、kubelet-client-key.pem(CN为kubelet-client)
    • 修改Kubelet和ApiServer的配置,指定证书
      • Kubelet:authentication.x509.clientCAFile/tlsCertFile/tlsPrivateKeyFile
      • ApiServer:kubelet-certificate-authority/kubelet-client-certificate/kubelet-client-key
  • Kubelet的服务端证书使用自签名证书,API Server 从集群CA签发客户端证书
  • Kubelet的服务端证书是Kubelet自己生成的自签名证书,保存在cert-dir 目录中
  • 这种情况下ApiServer没法确认Kubelet是否是合法的Server,但是Kubelet却可以认证ApiServer是否是合法的Client!!
  • 通过TLS bootstrap机制分配服务端证书,API Server 从集群CA签发客户端证书,这种方式可以自动更新服务端证书
    • APIServer Client分发过程类似,但是同时请求服务端和客户端证书,默认情况下这个配置是关闭的(参考kubelet的ServerTLSBootstrap配置

3. Kubelet证书自动更新

使用kubeadm安装时,Kubelet证书的更新策略为:

  • 客户端证书:自动更新

  • 服务端证书使用上述第二种方式来生成,因此实际上服务端证书是会过期的!!!用户要手动更新!!!,但是实际生产中发现:即使服务端证书过期APIServer依然可以正常访问Kubelet!!??

    • 上述场景执行以下操作,将自签证书替换成通过CSR签发的证书
    1
    2
    3
    4
    5
    6
    
    cd /var/lib/kubelet/pki && tar -cf kubelet_server.tar kubelet.crt kubelet.key
    rm -rf  kubelet.crt kubelet.key
    cd -
    echo "serverTLSBootstrap: true" >> /var/lib/kubelet/config.yaml
    systemctl restart kubelet
    kubectl certificate approve {csr_name} ## controller 默认不会自动签发服务端证书
    
    • 生成的证书为 /var/lib/kubelet/pki/kubelet-server-current.pem (crt和key都在一个文件中)

4. 监控证书超期时间

  • 方式一:kubeadm alpha certs check-expiration

2. 其他认证方式

1. Bootstrap Tokens

Bootstrap Tokens 是一种比较简易的认证方式,官方文档中建议仅用来初始化Kubelet节点,以及ConfigMap 签名的场景,并且高版本的K8S还对 Bootstrap Tokens 进行了很多限制(参考)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
apiVersion: v1
kind: Secret
metadata:
  # Name MUST be of form "bootstrap-token-<token id>"
  name: bootstrap-token-07401b
  namespace: kube-system # 1.18文档中说明,只能在kube-system中创建

# Type MUST be 'bootstrap.kubernetes.io/token'
type: bootstrap.kubernetes.io/token
stringData:
  # Human readable description. Optional.
  description: "The default bootstrap token generated by 'kubeadm init'."

  # Token ID and secret. Required.
  token-id: 07401b
  token-secret: f395accd246ae52d

  # Expiration. Optional.
  expiration: 2017-03-10T03:22:11Z

  # Allowed usages.
  usage-bootstrap-authentication: "true"
  usage-bootstrap-signing: "true"

  # Extra groups to authenticate the token as. Must start with "system:bootstrappers:"
  auth-extra-groups: system:bootstrappers:worker,system:bootstrappers:ingress

2. WebHook

附录1 :x509证书结构

  • Serial Number :证书序列号
  • Issuer:签发证书的CN(颁发者的CN)
  • Subject:本证书的CN(使用者的CN)
  • Subject Public Key Info (使用者公钥信息)

以下是 openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -text 的输出

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 7183422665212995921 (0x63b0a4dac782a951)
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN=kubernetes
        Validity
            Not Before: Jun 30 10:21:40 2020 GMT
            Not After : Oct 23 09:13:44 2021 GMT
        Subject: CN=kube-apiserver
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    略
                    cd:3f
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature, Key Encipherment
            X509v3 Extended Key Usage: 
                TLS Web Server Authentication
            X509v3 Subject Alternative Name: 
                DNS:172.24.135.44, DNS:kubernetes, DNS:kubernetes.default, DNS:kubernetes.default.svc, DNS:kubernetes.default.svc.cluster.local, IP Address:10.188.0.1, IP Address:172.24.135.44, IP Address:172.24.135.245
    Signature Algorithm: sha256WithRSAEncryption
         略

附录2 :生成APIServer的客户端证书

以下步骤生成一个只读的客户端证书文件用于访问API Server

  • 创建只读ClusterRole并且绑定到User
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: sdp-reader
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "watch", "list"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: read-pods
  namespace: kube-system
subjects:
- kind: User
  name: sdp-reader
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: sdp-reader
  apiGroup: rbac.authorization.k8s.io
  • 创建x509证书CSR和Key(可以用openssl也可以用cfssl,甚至可以通过Web工具
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
cat << EOF | cfssl genkey - | cfssljson -bare server
{
  "CN": "sdp-reader", # 用户名信息
  "hosts": [],        # 服务端用户名信息,作为客户端证书时可以不填
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "Fujian",
      "L": "Fuzhou",
      "O": "ND",        # O:OU 被K8S当作用户的Group信息
      "OU": "SDP"
    }
  ]
}
EOF
  • 创建CSR(注意:以下CSR配置是1.16版本的,高版本的CSR细节可以参考官方网站
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
cat << EOF > CSR.yml
apiVersion: certificates.k8s.io/v1beta1
kind: CertificateSigningRequest
metadata:
  name: sdp-reader-csr
spec:
  request: $(cat server.csr | base64 | tr -d '\n')
  usages:
  - client auth
EOF

kubectl get csr sdp-reader-csr -o jsonpath='{.status.certificate}' | base64 --decode > server.crt
  • 批准CSR,并且生成conf文件
1
2
3
4
kubectl certificate approve sdp-reader-csr
kubectl config set-credentials sdp-reader --client-key=/root/lq/tmp/server-key.pem --client-certificate=/root/lq/tmp/server.crt --embed-certs=true
kubectl config set-context sdp-reader --cluster=kubernetes --user=sdp-reader
kubectl config use-context sdp-reader

参考

一文带你彻底厘清 Kubernetes 中的证书工作机制

certificates

TLS证书最佳实践

证书签名请求