二进制部署的k8s集群更换到期证书

2,519 阅读7分钟

一、背景:

旧的k8s集群认证证书有效期即将过期,需要更换新的认证证书。该k8s集群采用的是二进制方式部署。

生成证书脚本:

#!/bin/bash

#if [ -z $GOPATH ]; then 
#    echo "Please set GOPATH to start the cluster :)"
#    exit 1
#fi

#K8S_HOME=$GOPATH/src/k8s.io/kubernetes
#VC_HOME=$GOPATH/src/volcano.sh/volcano
VC_HOME=/home/work/kubeconfig

CERT_DIR=/home/work/certs
mkdir -p $CERT_DIR

LOCALHOST="127.0.0.1"
#LOCALHOST="${MASTER_IP}"
API_PORT="6443"

ROOT_CA=
ROOT_CA_KEY=

SERVICE_ACCOUNT_KEY=/home/work/certs/service-account.key

function install_tools {
    for d in work logs certs config static-pods
    do
        mkdir -p ${VC_HOME}/volcano/$d
    done 

    #go get -u github.com/cloudflare/cfssl/cmd/...
}

function create_certkey {
    local name=$1
    local cn=$2
    local org=$3

    local hosts=""
    local SEP=""

    shift 3
    while [ -n "${1:-}" ]; do
        hosts+="${SEP}\"$1\""
        SEP=","
        shift 1
    done

    echo '{"CN":"'${cn}'","hosts":['${hosts}'],"key":{"algo":"rsa","size":2048},"names":[{"O":"'${org}'"}]}' \
        | cfssl gencert -ca=${CERT_DIR}/root.pem -ca-key=${CERT_DIR}/root-key.pem -config=${CERT_DIR}/root-ca-config.json - \ #-ca:指明ca的证书   -ca-key:指明ca的私钥文件   -config:指明请求证书的json文件
        | cfssljson -bare ${CERT_DIR}/$name 
}

function generate_cert_files {
    openssl genrsa -out "${SERVICE_ACCOUNT_KEY}" 2048 2>/dev/null  #生成service_accout_key,需要使用–service-account-private-key-file 参数选项将Service Account 密匙(key)文件传递给controller-manager中的Token controller。key用于 Service Account Token签名。
    #同样,也需要使用–service-account-key-file 参数选项将相应的(public key)公匙传递给kube-apiserver ,公钥用于在认证期间验证Token。
    #例:/home/work/zhanglei335/k8s_key_test/certs/service-account.key


    echo '{"signing":{"default":{"expiry":"876000h","usages":["signing","key encipherment","server auth","client auth"]}}}' \ 
        > ${CERT_DIR}/root-ca-config.json  #请求证书的json文件 expiry代表过期时间  后面创建key的时候是按照这个config的一些规则生成
 
    echo '{"CN":"kubernetes","key":{"algo":"rsa","size":2048},"names":[{"O":"kubernetes"}]}' | cfssl gencert -initca - \  #初始化创建CA认证中心,将会生成 root-key.pem(私钥)  root.pem(公钥)
        | cfssljson -bare ${CERT_DIR}/root
    #例:生成:/home/work/zhanglei335/k8s_key_test/certs/root.csr  root-key.pem  root.pem
    
    {MASTER_IP_STR}
    create_certkey "admin" "system:admin" "system:masters"   #生成admin的证书 /home/work/zhanglei335/k8s_key_test/certs/admin.csr  admin-key.pem  admin.pem
    create_certkey "kube-proxy" "system:kube-proxy" "kubernetes"
    create_certkey "kubelet" "system:node:127.0.0.1" "system:nodes"
    create_certkey "controller-manager" "system:kube-controller-manager" "kubernetes"
    create_certkey "scheduler" "system:scheduler" "kubernetes"

    write_kube_config "controller-manager"
    write_kube_config "scheduler"
    write_kube_config "kubelet"
    write_kube_config "admin"
    write_kube_config "kube-proxy"
}

function write_kube_config {
    local name=$1

    kubectl config set-cluster local --server=https://${LOCALHOST}:6443 --certificate-authority=${CERT_DIR}/root.pem \  #set-cluster 将集群信息添加到配置文件中
            --kubeconfig ${VC_HOME}/${name}.config

    kubectl config set-credentials myself --client-key=${CERT_DIR}/${name}-key.pem \   #用户详细信息添加到配置文件中
            --client-certificate=${CERT_DIR}/${name}.pem --kubeconfig ${VC_HOME}/${name}.config

    kubectl config set-context local --cluster=local --user=myself --kubeconfig ${VC_HOME}/${name}.config  #上下文详细信息添加到配置文件中
    kubectl config use-context local --kubeconfig ${VC_HOME}/${name}.config

    # kubectl --kubeconfig ./controller-manager.config config view --minify --flatten > ${TOP_DIR}/volcano/config/controller-manager.config
}

generate_cert_files

执行该脚本能生成一下认证文件:

image.png

二、k8s鉴权

由于该集群是线上集群,变更操作要谨慎,因此先将证书生成脚本中的所以概念理解清楚。

1、鉴权流程

可以分为认证、鉴权、审计三个阶段 www.cnblogs.com/yangyuliufe…

2、认证阶段——公钥基础设施/CFSSL证书生成

blog.51cto.com/liuzhengwei…

2.1 基本概念
  • ca证书 指权威机构给我们颁发的证书
  • 密钥 用来加解密用的文件或者字符串。密钥在非对称加密的领域里,指的是私钥和公钥,他们总是成对出现,其主要作用是加密和解密。常用的加密强度是2048bit。
  • 证书的编码格式
    • PEM

    • DER 与PEM不同之处在于其使用二进制而不是Base64编码的ASCII。扩展名为.der,但也经常使用.cer用作扩展名,所有类型的认证证书和私钥都可以存储为DER格式。Java使其典型使用平台。

  • 证书签名请求CSR 它是向CA机构申请数字×××书时使用的请求文件。在生成请求文件前,我们需要准备一对对称密钥。私钥信息自己保存,请求中会附上公钥信息以及国家,城市,域名,Email等信息,CSR中还会附上签名信息。当我们准备好CSR文件后就可以提交给CA机构,等待他们给我们签名,签好名后我们会收到crt文件,即证书。 通常用于数字证书认证机构(Certificate Authorities,CA),扩展名为.pem, .crt, .cer, 和 .key。内容为Base64编码的ASCII码文件,有类似"-----BEGIN CERTIFICATE-----" 和 "-----END CERTIFICATE-----"的头尾标记。服务器认证证书,中级认证证书和私钥都可以储存为PEM格式(认证证书其实就是公钥)。 注意:CSR并不是证书。而是向权威证书颁发机构获得签名证书的申请。 把CSR交给权威证书颁发机构,权威证书颁发机构对此进行签名,完成。保留好CSR,当权威证书颁发机构颁发的证书过期的时候,你还可以用同样的CSR来申请新的证书,key保持不变.
  • 数字签名 数字签名就是"非对称加密+摘要算法",其目的不是为了加密,而是用来防止他人篡改数据。 常用的摘要算法有MD5、SHA1、SHA256。 使用私钥对需要传输的文本的摘要进行加密,得到的密文即被称为该次传输过程的签名。
  • 数字证书 数字证书则是由证书认证机构(CA)对证书申请者真实身份验证之后,用CA的根证书对申请人的一些基本信息以及申请人的公钥进行签名(相当于加盖发证书机 构的公章)后形成的一个数字文件。实际上,数字证书就是经过CA认证过的公钥,除了公钥,还有其他的信息,比如Email,国家,城市,域名等。
2.2 CFSSL工具

是CloudFlare开源的一款PKI/TLS工具。 CFSSL 包含一个命令行工具 和一个用于 签名,验证并且捆绑TLS证书的 HTTP API 服务。 使用Go语言编写。 项目地址: github.com/cloudflare/…

下载地址: pkg.cfssl.org/

参考链接: blog.cloudflare.com/how-to-buil…

总结:在k8s中,客户端和apiserver之间的通信是通过ca签发的证书来认证的。

2.3 Service Account

zhuanlan.zhihu.com/p/344984329

  • 概念 ServiceAccount是一种账号,给运行在Pod里面的进程提供必要的身份证明

  • 创建 Controller Manager创建了ServiceAccount Controller和Token Controller这两个安全相关的控制器。其中ServiceAccount Controller一直监听Service Account和Namespace的事件,如果在一个Namespace中没有default Service Account,那么Service Account会给Namespace创建一个默认(default)的Service Account。 如果Controller manager进程在启动时指定API Service私钥(service-accountprivate-key-file参数),那么Controller manager会创建Token Controller,Token Controller也监听Service Account事件。

  • pod与apiserver之间的认证

    为了确保k8s集群的安全,API Server都会对客户端进行安全认证。在Pod中访问API Server服务时,是以Service方式访问名为Kubernetes这个服务的,是以类似HTTP Token的新认证方式:Service Account Auth,Pod在调用API Server时,在Http Header中传递了一个Token字符串,类似于之前提到的Http Token认证方式,有以下几处不同:

    • (1)Token内容来自Pod指定路径下的一个文件(文件名为token),由Kubernetes Controller进程用API Server的私钥(--service-account-private-zkey-file指定的私钥)签名指定生成的一个JWT Secret。
    • (2)通过HTTPS方式与API Server建立连接后,会用Pod里指定路径下的一个CA证书(文件名为ca.crt)验证API Server发来的证书,验证是否为CA证书签名的合法证书。
    • (3)API Server收到Token后,采用自身私钥(service-accountkey-file指定,如果没有指定,则默认采用tls-private-key-file指定的参数)对Token进行合法性验证。   在认证过程中会涉及到三个文件: token、ca.crt、namespace,这三个文件存在与k8s Secret中,Secret从属于Service Account资源对象,一个Service Account对象里面可以包括多个不同的Secret对象,分别用于不同目的的认证活动。   在每个Namespace下都有一个名为default的默认Service Account对象,在这个ServiceAccount里面有一个名为Tokens的可以当做Volume被挂载到Pod里的Secret,当Pod启动时,这个Secret会自动被挂载到Pod的指定目录下,用来协助完成Pod中的进程访问API Server时的身份鉴权。

三、具体操作

根据证书生成脚本,客户端和apiserver之间的通信是通过ca签发的证书来认证的,而ca的有效期是5年,此时ca是没有过期的,但是其签发的证书过期了,因此我们只需要根据原有的ca重新签发一个证书替换即将过期的证书即可。

  • 最初测试更换ca遇到的问题: kubelet报错,且网络插件和原有服务会重启:
`Failed create pod sandbox: rpc error: code = Unknown desc = failed to set up sandbox container "397ec0b226efa247a42e42a62c41973a0ae5d96600695e4fa628ad13342c84e2" network for pod "zl-kaldijob-test1-xxw77": networkPlugin cni failed to set up pod "zl-kaldijob-test1-xxw77_default" network: Get https://[11.1.0.1]:443/api/v1/namespaces/default/pods/zl-kaldijob-test1-xxw77: x509: certificate signed by unknown authority (possibly because of "crypto/rsa: verification error" while trying to verify candidate authority certificate "kubernetes"`
  • 更换service_account导致的问题 kube_scheduler报错:
E1104 10:01:06.160324   20114 reflector.go:123] k8s.io/client-go/informers/factory.go:134: Failed to list *v1.Node: Get https://127.0.0.1:6443/api/v1/nodes?limit=500&resourceVersion=0: net/http: TLS handshake timeout

因此需要将原脚本中的以下部分注释掉,不更换ca和service_account:

openssl genrsa -out "${SERVICE_ACCOUNT_KEY}" 2048 2>/dev/null  #生成service_accout_key,需要使用–service-account-private-key-file 参数选项将Service Account 密匙(key)文件传递给controller-manager中的Token controller。key用于 Service Account Token签名。

    echo '{"signing":{"default":{"expiry":"876000h","usages":["signing","key encipherment","server auth","client auth"]}}}' \ 
        > ${CERT_DIR}/root-ca-config.json  #请求证书的json文件 expiry代表过期时间  后面创建key的时候是按照这个config的一些规则生成
 
    echo '{"CN":"kubernetes","key":{"algo":"rsa","size":2048},"names":[{"O":"kubernetes"}]}' | cfssl gencert -initca - \  #初始化创建CA认证中心,将会生成 root-key.pem(私钥)  root.pem(公钥)
        | cfssljson -bare ${CERT_DIR}/root

总结操作流程: 1.使用注释后的脚本生成新的认证证书;

2.worker节点:新的认证证书替换原certs目录下面的kubelet、kube-proxy认证证书,重启worknode中的kube_let、kube_nginx、kube_proxy;

3.master节点:新的认证证书替换原certs目录下面的admin、controller-manager、kube-apiserver、scheduler、kubelet、kube-proxy认证证书,重启kube-apiserver、kube_controller_manager、kube_scheduler 重启worknode中的kube_let、kube_nginx、kube_proxy.