网站https访问的SSL证书自动同步到腾讯云cdn

以前ssl证书可以免费申请一年期的,主要是手动维护域名证书,上传到宝塔、在传到腾讯云、阿里云这些服务的cdn,现在好了,各家ssl证书不再提供一年期了,只提供90天的有效期的ssl证书免费使用。

以前一年期还经常忘记换证书,现在三个月一换太痛苦了,这些证书的机构为了收费确实是没法搞了,太烦了就靠自动化脚本来更新吧,正常脚本插件 acme.sh 是支持域名自动申请、续签更换的,宝塔面板也是通过这个插件进行免费证书申请的。但因为前面加了cdn没法这么轻松的搞了。

开始的思路是通过acme.sh 脚本申请证书更新证书来着,后来发现可能会与宝塔面板交互脚本太多,我自己写了一个计划任务,让宝塔自己负责维护服务器上的ssl证书和续签等,不需要特殊配置,只需要配置好域名的解析api就可以自己续签ssl证书了。

宝塔续签的证书按目录存放,我写一个定时任务,每天扫描宝塔目录里的脚本与cdn的脚本对比,如果证书有更新则自动推送到cdn更换证书。具体思路和计划任务脚本如下。

支持配置多个域名证书和目录,只要在一台主机上就可以扫,使用前需要先安装集成的腾讯云接口脚本tccli并配置好。

#!/bin/bash
# 脚本使用宝塔的自动 SSL 申请程序,同步到腾讯云 CDN

# 脚本使用宝塔的自动ssl申请程序,申请到ssl证书后,在计划任务创建一个计划任务,通过脚本配置单独域名ssl证书推送到腾讯云的cdn文件夹,并设置为域名最新证书。
# 如果你没有宝塔面板,可以自己集成测acme.sh,宝塔也是集成的这个程序 https://github.com/acmesh-official/acme.sh
# 同步到tccli :先申请安装tccli,配置好腾讯云的api接口  https://cloud.tencent.com/document/product/440/34012

#思路来源:https://blog.jnn.icu/wildcard-cert-with-cdn-auto-renew/

# 脚本使用宝塔的自动 SSL 申请程序,同步到腾讯云 CDN

# 定义宝塔面板路径
bt_panel_path="/www/server/panel/vhost/cert"

# 定义多个域名列表和对应的证书存放目录,其他域名也可以,会循环。
declare -A domain_cert_paths=(
  ["www.5656t.com"]="$bt_panel_path/5656t.com"
  ["5656t.com"]="$bt_panel_path/5656t.com"
  ["1.5656t.com"]="$bt_panel_path/1.5656t.com"
)

# 循环处理每个域名
for domain in "${!domain_cert_paths[@]}"; do
  cert_path="${domain_cert_paths[$domain]}"

  # 检查域名的证书文件是否存在
  if [ ! -d "$cert_path" ]; then
    echo "SSL certificate for domain $domain not found in Baota panel."
    continue
  fi

  # 读取本地证书文件
  local_fullchain=$(cat "$cert_path/fullchain.pem")
  local_privkey=$(cat "$cert_path/privkey.pem")

  # 查询 CDN 配置信息
  cdn_config=$(tccli cdn DescribeDomainsConfig --cli-unfold-argument --Filters.0.Name domain --Filters.0.Value "$domain" --Filters.0.Fuzzy False)

  # 提取证书 ID
  existing_cert_id=$(echo "$cdn_config" | grep -oP '"CertId":\s*"\K[^"]+' | sed 's/\\n/\n/g')
  echo " $domain CertId is: $existing_cert_id"
  # 检查是否存在现有的证书
  if [ -n "$existing_cert_id" ]; then
    # 获取证书详细信息
    existing_cert_response=$(tccli ssl DescribeCertificateDetail --cli-unfold-argument --CertificateId "$existing_cert_id")
    existing_fullchain=$(echo "$existing_cert_response" | grep -oP '"CertificatePublicKey":\s*"\K[^"]+' | sed 's/\\n/\n/g')
    existing_privkey=$(echo "$existing_cert_response" | grep -oP '"CertificatePrivateKey":\s*"\K[^"]+' | sed 's/\\n/\n/g')
    #echo "$existing_fullchain"
    #echo "$local_fullchain"
    #echo "$existing_privkey"
    #echo "$local_privkey"

    # 比较本地证书和现有证书是否一致,腾讯云返回的证书文件会有多余的换行符,需要去掉。。好坑
    if [ "$(echo "$local_fullchain" | tr -d '\n')" == "$(echo "$existing_fullchain" | tr -d '\n')" ] && [ "$(echo "$local_privkey" | tr -d '\n')" == "$(echo "$existing_privkey" | tr -d '\n')" ]; then
      echo "The local certificate for domain $domain is identical to the existing certificate on Tencent Cloud. No update needed."
    else
      echo "The local certificate for domain $domain is different from the existing certificate on Tencent Cloud. Updating..."
      # 上传证书到腾讯云
      upload_response=$(tccli ssl UploadCertificate --cli-unfold-argument --CertificatePublicKey "$local_fullchain" --CertificatePrivateKey "$local_privkey")
      echo "Upload response: $upload_response"

      # 获取证书ID
      cert_id=$(echo $upload_response | grep -oP '"CertificateId":\s*"\K[^"]+')

      if [ -z "$cert_id" ]; then
        echo "Failed to upload certificate to Tencent Cloud for domain $domain."
        continue
      fi

      echo "Certificate ID for domain $domain: $cert_id"

      # 部署证书到腾讯云 CDN
      deploy_response=$(tccli cdn ModifyDomainConfig --cli-unfold-argument --Domain "$domain" --Route 'Https.CertInfo.CertId' --Value "{\"update\":\"$cert_id\"}")
      echo "Deploy response for domain $domain: $deploy_response"
    fi
  else
    echo "No existing certificate found on Tencent Cloud for domain $domain. Uploading new certificate..."
    
    # 上传证书到腾讯云
    upload_response=$(tccli ssl UploadCertificate --cli-unfold-argument --CertificatePublicKey "$local_fullchain" --CertificatePrivateKey "$local_privkey")
    echo "Upload response: $upload_response"

    # 获取证书ID
    cert_id=$(echo $upload_response | grep -oP '"CertificateId":\s*"\K[^"]+')

    if [ -z "$cert_id" ]; then
      echo "Failed to upload certificate to Tencent Cloud for domain $domain."
      continue
    fi

    echo "Certificate ID for domain $domain: $cert_id"

    # 部署证书到腾讯云 CDN
    deploy_response=$(tccli cdn ModifyDomainConfig --cli-unfold-argument --Domain "$domain" --Route 'Https.CertInfo.CertId' --Value "{\"update\":\"$cert_id\"}")
    echo "Deploy response for domain $domain: $deploy_response"
  fi
done

echo "Done"

这个脚本是与chatgpt一起写的,排查bug,改逻辑,又改了很久才搞好。