tls 证书在 k8s 集群上大量使用的话,当到期时会存在批量替换的难度,比如说每个名称空间,多个业务的使用,在这篇博文中,将尝试批量替换整个集群的证书(前提,在没有使用 vault, cert-manager这类组件的集群之上)。

基本操作

步骤1:首先不知道有多少个名称空间使用了这个证书,所以需要遍历所有的名称空间,这里使用 kubectl 的 json path 实现

bash
1
$ kubectl get ns -o jsonpath='{range $.items[*]}{.metadata.name}{"\n"}{end}'

步骤2:拿到名称空间的名字后,就需要循环这个名称空间下所有的 secret

bash
1
2
3
4
for ns in `kubectl get ns -o jsonpath='{range $.items[*]}{.metadata.name}{"\n"}{end}'`
do
  kubectl get secret -n $ns -o jsonpath='{range $.items[*]}{.metadata.name}{"\n"}{end}'
done

步骤3:找到与这个匹配的证书进行替换

把步骤3拆解为下面几个步骤:

  • 拿去到符合要求的secret的名字
  • 匹配名称是否为修改的secret
  • 做替换操作

由于步骤2使用的是 jsonpath 拿到的 secret name,由于 kubectl 并不支持高级jsonpath语法,官方推荐使用jq,那么使用jq获取名字

bash
1
kubectl get secret -n $ns -o json|jq -r .items[].metadata.name

使用 awk 做字符串匹配,匹配是否包含对应的字符串关键词

bash
1
awk '$1 ~ /xxxx/ { print }'

最后使用 kubectl replace 替换现有的 secret

bash
1
kubectl create secret tls $secertName -n $ns --cert=xxx.crt --key=xxx.key --dry-run -o yaml| kubectl replace -f -

三个步骤整个为一个命令,如下所示:

bash
1
2
3
4
5
6
7
for ns in `kubectl get ns -o jsonpath='{range $.items[*]}{.metadata.name}{"\n"}{end}'`
do
  for secertName in `kubectl get secret -n $ns -o json|jq -r .items[].metadata.name|awk '$1 ~ /xxxx/ { print }';
  do
    kubectl create secret tls $secertName -n $ns --cert=xxx.crt --key=xxx.key --dry-run -o yaml| kubectl replace -f -
  done
done

注意:

  • ingress 使用的证书会立即更新
  • 如果是作为 secret 挂在到 Pod 中需要重启,这是因为 kubelet volume 机制导致的。

高级用法

查询证书内容,并根据域名进行替换

查看证书域名的命令

bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
ns=xxx
secret_name=xxx
keywords=xxx
_command=xxxx
openssl x509 -in <(
    ${_command} get secret -n $ns $secret_name -ojson | \
    jq --arg secret_key $(
        ${_command} get secret -n ${ns} ${secret_name} -ojson | \
        jq -r '.data | keys[]' | \
        awk -v keywords=${keywords} '$0 ~ keywords { print }'
    ) -r '.data | .[$secret_key]' | base64 -d
) -text -noout | \
grep -i "subject: "

步骤拆解

bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 替换的名称空间
ns=kube-system
# 替换的secret_name
secret_name=test
# 替换key的关键词,通常secret作为tls存在时,为xxx.crt
keywords=crt
# kubectl命令
_command=kubectl --kubeconfig=kubeconfig

# shell语法,将一个命令的输出作为openssl的输入
openssl x509 -in <(
  ${_command} get secret -n $ns $secret_name -ojson | \
  # jq --arg secret_key 是 jq语法,将 $() 命名为secret_key作为变量传入到下个命令使用
  jq --arg secret_key $(
    ${_command} get secret -n ${ns} ${secret_name} -ojson | \
    jq -r '.data | keys[]' | \
    # awk -v keywords=${keywords} 是awk语法,将keyworkd作为变量传递到awk内部
    # '$0 ~ keywords { print }' 打印出符合条件的行
    awk -v keywords=${keywords} '$0 ~ keywords { print }'
  ) -r '.data | .[$secret_key]' | base64 -d
) -text -noout | \
grep -i "subject: "

更新前没出意外需要备份下对应资源,批量备份集群内指定域名的 secret 命令

bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
```bash
# 指定域名的secret
keywords=xxx
_command=xxxx
for ns in `${_command} get ns -o jsonpath='{range $.items[*]}{.metadata.name}{"\n"}{"end"}'`
do
  for secret_name in `${_command} get secret -n $ns -o json|jq -r '.items[] | select( .type == "kubernetes.io/tls") | .metadata.name'`
  do  
    openssl x509 -in <(
      ${_command} get secret -n ${ns} ${secret_name} -ojson | \
      jq --arg secret_key $(
        ${_command} get secret -n ${ns} ${secret_name} -ojson | 
        jq -r '.data | keys[]' | \
        awk '$0 ~ /crt/ { print }'
      ) -r '.data|.[$secret_key]'|base64 -d
    ) -text -noout | \
    grep -i "subject: "| \
    awk -v ns=${ns} -v secret_name=${secret_name} -v cmd=${_command} -v kw=${keywords} \
      '$0 ~ kw { system(
        cmd" get secret -n "ns" "secret_name" -oyaml > primetive/"ns"."secret_name".yaml"
      )}'
  done
done

批量替换命令,查询 tls 签发域名,并进行替换

bash
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
keywords=xxx
_command=xxxx
for ns in `${_command} get ns -o jsonpath='range $.items[*]}{.metadata.name}{"\n"}{"end"}'
do
  for secret_name in `${_command} get secret -n $ns -o json|jq -r '.items[] | select( .type == "kubernetes.io/tls") | .metadta.name'`
  do  
    openssl x509 -n <(
      ${_command} get secret -n ${ns} ${secret_name} -ojson | \
      jq --arg secret_key $(${_command} get secret -n ${ns} ${secret_name} -ojson | \
      jq -r '.data | .keys[]' | \
      awk '$0 ~ /crt/ { print }' -r '.data|.[$secret_key]'|base64 -d
    ) -text -noout | \
    grep -i "subject: "| \
    awk -v ns=${ns} -v secret_name=${secret_name} -v commond=${_command}\
      '$0 ~ ${keywords} { system(
        command" create secret tls -n "ns" "secret_name" --cert=ca.crt --key=key.key --dry-run -oyaml|command" replace -f -"
      )}'
  done
done