完整脚本如下:
#!/bin/bash
set -euo pipefail
# 配置信息
Domains=("") # 域名列表
CertBaseDir="/usr/local/nginx/cert" # 证书存储目录
TempDir="/usr/local/nginx/tmp" # 临时目录
DaysBeforeExpire=7 # 到期前7天更新
NginxBin="/usr/local/nginx/sbin/nginx" # Nginx二进制路径
# 创建临时目录
mkdir -p "$TempDir"
# 函数:输出错误并退出当前域名处理流程
error_exit() {
echo "[错误] $1" >&2
exit 1 # 终止当前子流程
}
# 函数:从域名中提取主机记录
get_host_record() {
local domain="$1"
echo "$domain" | awk -F'.' '{print $1}'
}
# 函数:检查证书过期时间
check_cert_expiry() {
local domain="$1"
local host_record=$(get_host_record "$domain")
local cert_file="$CertBaseDir/${host_record}.pem"
if [ ! -f "$cert_file" ]; then
echo "证书文件不存在,触发新证书申请。"
return 0
fi
local expire_date expire_timestamp current_timestamp seconds_left days_left
expire_date=$(openssl x509 -enddate -noout -in "$cert_file" 2>/dev/null) || error_exit "无法读取证书有效期"
expire_date=${expire_date#*=}
expire_timestamp=$(date -d "$expire_date" +%s 2>/dev/null) || error_exit "日期转换失败"
current_timestamp=$(date +%s)
seconds_left=$((expire_timestamp - current_timestamp))
days_left=$((seconds_left / 86400))
[ $days_left -le $DaysBeforeExpire ] && return 0 || return 1
}
# 函数:处理单个域名更新流程
process_domain() {
local domain="$1"
local host_record=$(get_host_record "$domain")
echo "===== 开始处理域名: $domain (主机记录: $host_record) ====="
# 申请证书(使用DNS_AUTO自动验证)
echo "申请新证书(DNS自动验证)..."
cert_response=$(tccli ssl ApplyCertificate \
--DomainName "$domain" \
--DvAuthMethod DNS_AUTO 2>&1) || error_exit "证书申请失败:$cert_response"
cert_id=$(jq -r '.CertificateId' <<< "$cert_response") || error_exit "无法解析证书ID"
# 等待证书签发(最多等待10分钟)
echo "等待证书签发..."
local status
for _ in {1..20}; do
status_response=$(tccli ssl DescribeCertificate \
--CertificateId "$cert_id" 2>&1) || error_exit "证书状态查询失败:$status_response"
status=$(jq -r '.StatusName' <<< "$status_response")
[ "$status" = "已颁发" ] && break
echo "下一轮尝试"
sleep 30
done
[ "$status" = "已颁发" ] || error_exit "证书签发超时(最终状态:$status)"
# 下载证书(处理Base64内容)
echo "下载证书..."
download_response=$(tccli ssl DownloadCertificate \
--CertificateId "$cert_id" 2>&1) || error_exit "证书下载失败:$download_response"
# 提取Base64内容并解码为ZIP文件
echo "解码证书文件..."
zip_file="$TempDir/$domain.zip"
content=$(jq -r '.Content' <<< "$download_response") || error_exit "无法解析证书内容"
echo "$content" | base64 -d > "$zip_file" || error_exit "Base64解码失败"
[ -s "$zip_file" ] || error_exit "生成的ZIP文件为空" # 检查文件是否有效
# 解压证书
echo "解压证书..."
unzip -oq "$zip_file" -d "$TempDir/$domain" 2>&1 || error_exit "解压失败"
# 部署证书
echo "部署证书..."
mkdir -p "$CertBaseDir" || error_exit "无法创建证书目录"
\cp -f "$TempDir/$domain/$domain.pem" "$CertBaseDir/${host_record}.pem" || error_exit "证书复制失败"
\cp -f "$TempDir/$domain/$domain.key" "$CertBaseDir/${host_record}.key" || error_exit "私钥复制失败"
# 清理临时文件
echo "清理临时文件..."
rm -rf "$TempDir/$domain.zip" "$TempDir/$domain" || error_exit "临时文件清理失败"
# 检查Nginx配置并重载
echo "执行Nginx配置测试..."
nginx_test_output=$($NginxBin -t 2>&1) # 捕获标准输出和错误
echo "$nginx_test_output" # 打印测试结果
if echo "$nginx_test_output" | grep -q "successful"; then
echo "配置验证通过,执行重载..."
$NginxBin -s reload || error_exit "Nginx重载失败"
else
error_exit "Nginx配置测试失败,跳过重载" # 终止当前域名流程,但不影响其他域名
fi
}
# 主流程:逐个处理域名
for domain in "${Domains[@]}"; do
(
if check_cert_expiry "$domain"; then
process_domain "$domain"
echo "===== 域名 $domain 更新成功 ====="
else
echo "===== 域名 $domain 无需更新 ====="
fi
) || echo "===== 域名 $domain 更新失败 =====" # 错误已在前序流程中输出
done
echo "所有域名处理完成!"