腾讯云最近证书中心,申请免费证书总是遇到问题,申请慢,容易失败,需要设置 CAA.

一、1panel申请证书

  1. 设置 acme账户
  2. 设置 dns 账号
  3. 申请证书时保存到本地文件夹
1
/opt/certs.d

二、申请后push

官方文档

  1. 安装命令行工具,tccli
1
sudo pip install tccli
  1. 验证
1
tccli --version
  1. 设置 AKSK
1
tccli configure
  1. 验证密钥正确与否
1
tccli ssl DescribeCertificates --Limit 1 --output json
  1. 修改 push 脚本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#!/usr/bin/env bash
#
# 1panel 续签后:上传新证书 + 逐个部署到所有 COS 实例(全地域,串行+轮询确认)
# 用法: push.sh [fullchain路径] [privkey路径]
# 密钥请用 tccli configure 预先配置,不要写在脚本里
#
set -uo pipefail

# ========= 配置 =========
API_REGION="ap-beijing"
CERT_PATH="${1:-/opt/certs.d/fullchain.pem}"
KEY_PATH="${2:-/opt/certs.d/privkey.pem}"
ALIAS_PREFIX="1panel-push-brcn"
COS_REGIONS=("ap-hongkong" "ap-singapore" "ap-beijing")
CLEANUP_OLD=false # 全部成功后才生效;稳定后可改 true 自动删旧证书
WAIT_MAX=20 # 单个实例最多轮询次数(每次间隔4秒,即最多等80秒)
# ========================

fail() { echo "!!! $1"; exit 1; }

# 轮询:某域名在某地域是否已变成新证书
wait_deployed() {
local region="$1" domain="$2" newid="$3" n=0 cur=""
while [ $n -lt $WAIT_MAX ]; do
cur=$(tccli ssl DescribeHostCosInstanceList --region "$region" --CertificateId "$newid" --output json 2>/dev/null \
| python3 -c "
import sys,json
d='$domain'
try: data=json.load(sys.stdin)
except: sys.exit(0)
for i in data.get('InstanceList',[]):
if i.get('Domain')==d:
print(i.get('CertId') or ''); break
" 2>/dev/null)
[ "$cur" = "$newid" ] && return 0
n=$((n+1)); sleep 4
done
return 1
}

# ---- 1. 上传新证书 ----
echo ">>> 1. 上传新证书..."
[ -f "$CERT_PATH" ] || fail "找不到证书文件: $CERT_PATH"
[ -f "$KEY_PATH" ] || fail "找不到私钥文件: $KEY_PATH"

UPLOAD_RAW=$(tccli ssl UploadCertificate \
--region "$API_REGION" \
--CertificatePublicKey "$(cat "$CERT_PATH")" \
--CertificatePrivateKey "$(cat "$KEY_PATH")" \
--Alias "$ALIAS_PREFIX-$(date +%Y%m%d-%H%M%S)" \
--CertificateType SVR --output json 2>&1) || fail "上传调用失败: $UPLOAD_RAW"

NEW_ID=$(echo "$UPLOAD_RAW" | python3 -c "import sys,json;print(json.load(sys.stdin)['CertificateId'])" 2>/dev/null)
[ -z "$NEW_ID" ] && fail "未解析到新证书ID,返回: $UPLOAD_RAW"
echo ">>> 新证书 ID: $NEW_ID"

# ---- 2. 遍历各地域,逐个部署 + 确认落地 ----
OLD_IDS=""
OK=0; SKIP=0; ERR=0
for region in "${COS_REGIONS[@]}"; do
echo ">>> [$region] 查询 COS 实例..."
LIST_RAW=$(tccli ssl DescribeHostCosInstanceList --region "$region" \
--CertificateId "$NEW_ID" --output json 2>&1)

ROWS=$(echo "$LIST_RAW" | python3 -c "
import sys,json
try: data=json.load(sys.stdin)
except: sys.exit(0)
for i in data.get('InstanceList',[]):
inst=i.get('Region','')+'|'+i.get('Bucket','')+'|'+i.get('Domain','')
print(inst+'\t'+(i.get('CertId') or '')+'\t'+i.get('Domain',''))
" 2>/dev/null)

[ -z "$ROWS" ] && { echo " ($region 无 COS 实例,跳过)"; continue; }

while IFS=$'\t' read -r inst oldcert domain; do
[ -z "$inst" ] && continue

if [ "$oldcert" = "$NEW_ID" ]; then
echo " [跳过] $domain 已是新证书"; SKIP=$((SKIP+1)); continue
fi

JSON_LIST=$(python3 -c "import json;print(json.dumps(['$inst']))")
RESP=$(tccli ssl DeployCertificateInstance --region "$region" \
--CertificateId "$NEW_ID" \
--InstanceIdList "$JSON_LIST" \
--ResourceType cos --Status 1 --output json 2>&1)
RECID=$(echo "$RESP" | python3 -c "import sys,json;print(json.load(sys.stdin).get('DeployRecordId',''))" 2>/dev/null)

if [ -z "$RECID" ]; then
echo " [失败] $domain : $RESP"; ERR=$((ERR+1)); continue
fi

echo -n " [$domain] 已提交(记录ID $RECID),等待生效..."
if wait_deployed "$region" "$domain" "$NEW_ID"; then
echo " OK"
OK=$((OK+1))
[ -n "$oldcert" ] && OLD_IDS="$OLD_IDS $oldcert"
else
echo " 超时未确认(可能仍在后台处理,请稍后手动复查)"
ERR=$((ERR+1))
fi
done <<< "$ROWS"
done

echo ">>> 部署完成: 成功 $OK / 跳过 $SKIP / 失败或未确认 $ERR"

# ---- 3. 可选:清理旧证书(仅当全部成功时)----
if [ "$CLEANUP_OLD" = true ] && [ "$ERR" -eq 0 ] && [ -n "$OLD_IDS" ]; then
UNIQ_OLD=$(echo "$OLD_IDS" | tr ' ' '\n' | sort -u | grep -v "^$")
echo ">>> 3. 清理旧证书..."
for oid in $UNIQ_OLD; do
[ "$oid" = "$NEW_ID" ] && continue
echo " 删除 $oid"
tccli ssl DeleteCertificate --region "$API_REGION" --CertificateId "$oid" --output json 2>&1 \
|| echo " ($oid 删除失败,可能仍被其他资源占用)"
done
fi

echo ">>> 全部完成。新证书 ID: $NEW_ID"