一、背景:
旧的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
执行该脚本能生成一下认证文件:
二、k8s鉴权
由于该集群是线上集群,变更操作要谨慎,因此先将证书生成脚本中的所以概念理解清楚。
1、鉴权流程
可以分为认证、鉴权、审计三个阶段 www.cnblogs.com/yangyuliufe…
2、认证阶段——公钥基础设施/CFSSL证书生成
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
.