前言

我之前已经写过一篇certbot申请ssl证书的文章,为什么这次还要再写一遍呢? 原因在于,certbot 虽能实现服务器内部的 SSL 证书申请与自动更新,但在使用 CDN 的场景下,无法自动将证书上传并配置到腾讯云 CDN(或许有方法但我未曾深入研究)。相比之下,acme.sh 在这一场景下表现更为出色,不仅灵活性强,还能满足我的定制化需求。

acme.sh与certbot的横向对比

特性维度 acme.sh certbot
核心设计 纯 Shell 脚本编写 由 Let’s Encrypt 官方推荐,使用 Python 编写
安装与复杂度 安装简单,配置相对复杂,尤其在使用 DNS API 时 安装与配置较为简单,但对复杂或特殊环境的支持较弱
自动化与续期 自动设置定时任务,自动化程度高 更新证书需要手动设置 cron 任务
验证方式 支持多种验证方式(如 DNS、HTTP),尤其擅长通过 DNS API 进行验证,适合通配符证书 支持 HTTP 和 DNS 验证
灵活性 非常灵活,支持众多 DNS 服务商 API,可指定证书存放路径 与主流 Web 服务器(如 Apache、Nginx)集成较好,但路径定制相对不灵活
适用场景 需要通配符证书多域名管理、与云平台/CDN 集成无 GUI 的服务器环境希望高度自动化的场景 适合单机部署快速为现有 Web 服务器(如 Apache, Nginx)配置证书初学者命令行经验较少的用户

核心概念解析:

1.证书文件格式

申请SSL证书的过程中,通常会接触到如下几种文件格式:

文件后缀 通常格式 通常包含的内容 说明
.pem PEM 非常灵活。可以是: 1. 证书 2. 私钥 3. 证书链 4. 其他 PKI 对象 一个“万能”容器。看到这个后缀,你需要查看文件内容来确定它到底是什么。
.crt.cer 多为 PEM,也可能是 DER 证书 这两个后缀几乎可以互换,都表示“证书”。在 Linux/Unix 世界中更常用 .crt,在 Windows 中更常用 .cer
.key PEM 私钥 专门用于存放私钥的文件。必须严加保护!
.csr PEM 证书签名请求 在向 CA 申请证书时生成的文件,包含你的公钥和公司信息。

2.证书颁发机构(CA)选择

CA是Certificate Authority的缩写,也就是证书颁发机构,常见的证书颁发机构有ZeroSSL 和 Let’s Encrypt,二者的对比如下

特性维度 ZeroSSL Let’s Encrypt
验证方式 支持 DNS、HTTP 文件、邮箱验证 主要通过 DNS 和 HTTP 文件验证(通过 ACME 协议)
通配符证书 免费版不支持,付费版支持 支持免费通配符证书
多域名证书 免费版支持(最多3个域名),付费版支持更多 支持(单证书最多100个域名)
IP证书 支持为纯IP地址签发证书 不支持
API速率限制 相对宽松,免费账户有一定限制但“无同一IP多次申请被限制的问题” 有明确的速率限制(如每周同一主域名下最多签发50个证书)
管理界面 提供友好的Web管理后台,可可视化查看、申请、管理证书 无官方Web管理界面,完全通过命令行或第三方工具(如acme.sh, Certbot)管理
自动化与集成 支持 ACME 协议自动化,但免费版自动化功能可能不如Let’s Encrypt生态丰富 自动化生态极其成熟,与Certbot、acme.sh等工具无缝集成,非常适合自动化部署和续签
默认CA变化 acme.sh v3.0及以上版本默认CA已从Let’s Encrypt切换为ZeroSSL 曾是许多自动化工具(如acme.sh旧版)的默认选择
适用场景 需要IP证书多域名证书(≤3个),偏好图形化界面操作,或遇到Let’s Encrypt速率限制的用户 需要通配符证书,追求极致的自动化和丰富的集成生态,有大量证书管理需求的技术用户

从 v3.0 版本开始,acme.sh的默认CA已从Let’s Encrypt改为ZeroSSL,你可以通过以下命令切换:

1
acme.sh --set-default-ca --server letsencrypt

实战环节

服务器本地证书更新自动化

1. 安装 acme.sh

安装很简单,一条命令:

1
curl https://get.acme.sh | sh -s email=my@example.com

2.验证域名所有权(DNS自动验证方式)

以 腾讯云DNSPod.cn 为例,先登录DNSPod.cn,获取 DNSPod API Key 和 ID 并执行下面的命令

1
2
export DP_Id="1234"
export DP_Key="sADDsdasdgdsf"

命令执行后配置文件会存储在用户目录下的:~/.acme.sh/account.conf

3.签发ssl证书

以本站为例,执行以下命令,申请cyanfish.site以及包含所有子域名的通配符证书:

1
acme.sh --issue --dns dns_dp -d cyanfish.site -d *.cyanfish.site

这里报错了,提示

1
acme.sh: command not found

询问AI老师,得知重新加载shell配置应用环境变量,执行如下命令,先查看shell类型

1
echo $SHELL
  • 如果输出 /bin/bash→ 你使用的是 Bash

  • 如果输出 /bin/zsh→ 你使用的是 Zsh

然后执行对应的重新加载配置命令

1
2
3
source ~/.bashrc
#或者你是zsh,就执行下面的:
source ~/.zshrc

再次尝试签发证书,成功后输出如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
ubuntu@VM-16-3-ubuntu:~/.acme.sh$ acme.sh --issue --dns dns_dp -d cyanfish.site -d *.cyanfish.site
[Mon Sep 8 12:27:14 PM CST 2025] Using CA: https://acme.zerossl.com/v2/DV90
[Mon Sep 8 12:27:14 PM CST 2025] Multi domain='DNS:cyanfish.site,DNS:*.cyanfish.site'
[Mon Sep 8 12:27:22 PM CST 2025] Getting webroot for domain='cyanfish.site'
[Mon Sep 8 12:27:22 PM CST 2025] Getting webroot for domain='*.cyanfish.site'
[Mon Sep 8 12:27:22 PM CST 2025] Adding TXT value: J8kRNAtgSArI9g-lCo8FyOzLjYu7WnO7JeSscjJFpBg for domain: _acme-challenge.cyanfish.site
[Mon Sep 8 12:27:23 PM CST 2025] Adding record
[Mon Sep 8 12:27:24 PM CST 2025] The TXT record has been successfully added.
[Mon Sep 8 12:27:24 PM CST 2025] Adding TXT value: fWxb3zLS3embDHwjLf-M0cjMTaE5i9VmuCgIl09THHs for domain: _acme-challenge.cyanfish.site
[Mon Sep 8 12:27:24 PM CST 2025] Adding record
[Mon Sep 8 12:27:25 PM CST 2025] The TXT record has been successfully added.
[Mon Sep 8 12:27:26 PM CST 2025] Let's check each DNS record now. Sleeping for 20 seconds first.

4.安装证书到nginx

先创建要存放证书和key的目录:

1
mkdir /etc/nginx/ssl/cyanfish.site

然后执行如下命令安装证书:

1
2
3
4
acme.sh --install-cert -d cyanfish.site \
--key-file /etc/nginx/ssl/cyanfish.site/key.pem \
--fullchain-file /etc/nginx/ssl/cyanfish.site/fullchain.cer \
--reloadcmd "service nginx reload"

💡重要提示:

Nginx 的配置项 ssl_certificate 需要使用 /etc/nginx/ssl/fullchain.cer ,而非 /etc/nginx/ssl/<domain>.cer ,否则 SSL Labs 的测试会报证书链问题(Chain issues Incomplete)。

默认情况下,证书每 60 天更新一次(可自定义)。更新证书后,Apache 或者 Nginx 服务会通过 reloadcmd 传递的命令自动重载配置。

注意:reloadcmd 非常重要。证书会自动申请续签,但是如果没有正确的 reloadcmd 命令,证书可能无法被重新应用到 Apache 或者 Nginx,因为配置没有被重载。

我又遇到问题了,执行时提示权限不足,无法创建文件;尝试用管理员用户执行则提示找不到acme.sh的命令

1
2
3
4
5
6
7
ubuntu@VM-16-3-ubuntu:/etc/nginx$ acme.sh --install-cert -d cyanfish.site --key-file       /etc/nginx/ssl/cyanfish.site/key.pem  --fullchain-file /etc/nginx/ssl/cyanfish.site/fullchain.cer --reloadcmd     "service nginx reload"
[Tue Sep 9 11:22:02 AM CST 2025] The domain 'cyanfish.site' seems to already have an ECC cert, let's use it.
[Tue Sep 9 11:22:02 AM CST 2025] Installing key to: /etc/nginx/ssl/cyanfish.site/key.pem
touch: cannot touch '/etc/nginx/ssl/cyanfish.site/key.pem': Permission denied
ubuntu@VM-16-3-ubuntu:/etc/nginx$ sudo acme.sh --install-cert -d cyanfish.site --key-file /etc/nginx/ssl/cyanfish.site/key.pem --fullchain-file /etc/nginx/ssl/cyanfish.site/fullchain.cer --reloadcmd "service nginx reload"
sudo: acme.sh: command not found
ubuntu@VM-16-3-ubuntu:/etc/nginx$

问题解决:将ubuntu用户安装的acme.sh脚本的环境变量链接给root用户,这样root用户就可以执行acme.sh命令了,链接命令如下:

1
sudo ln -s /home/ubuntu/.acme.sh/acme.sh /usr/local/bin/acme.sh

使用–version命令验证,成功的输出如下:

1
2
3
4
ubuntu@VM-16-3-ubuntu:/etc/nginx$ sudo ln -s /home/ubuntu/.acme.sh/acme.sh /usr/local/bin/acme.sh
ubuntu@VM-16-3-ubuntu:~$ sudo acme.sh --version
https://github.com/acmesh-official/acme.sh
v3.1.2

回到主线,继续尝试执行安装证书命令,仍然报错😅,说检测到你在用sudo 执行命令,让你看一下文档:

1
2
3
ubuntu@VM-16-3-ubuntu:/etc/nginx$ sudo acme.sh --install-cert -d cyanfish.site --key-file       /etc/nginx/ssl/cyanfish.site/key.pem  --fullchain-file /etc/nginx/ssl/cyanfish.site/fullchain.cer --reloadcmd     "service nginx reload"
It seems that you are using sudo, please read this page first:
https://github.com/acmesh-official/acme.sh/wiki/sudo

遵照提示看了下文档内容,说是不推荐使用sudo执行,如果权限配置不正确可能会导致后续自动更新脚本执行失败,那么我们转变思路,为ubuntu用户授予ssl目录的读写权限,这样ubuntu用户就可以创建和写文件了:

1
2
3
4
5
6
7
8
9
# 将 ubuntu 用户加入 www-data 组(Nginx默认组)
sudo usermod -aG www-data ubuntu

# 设置目录属组为 www-data,并允许组内用户读写
sudo chown -R root:www-data /etc/nginx/ssl
sudo chmod -R 775 /etc/nginx/ssl

# 重新登录或刷新组权限
newgrp www-data

再次尝试执行安装证书命令,又遇到了新的问题😅——提示ubuntu用户没有权限执行service nginx reload命令,并且提示输入root用户的密码:

1
2
3
4
5
6
7
8
9
10
11
12
ubuntu@VM-16-3-ubuntu:/etc/nginx/ssl/cyanfish.site$ acme.sh --install-cert -d cyanfish.site --key-file       /etc/nginx/ssl/cyanfish.site/key.pem  --fullchain-file /etc/nginx/ssl/cyanfish.site/fullchain.cer --reloadcmd     "service nginx reload"
[Tue Sep 9 11:48:43 AM CST 2025] The domain 'cyanfish.site' seems to already have an ECC cert, let's use it.
[Tue Sep 9 11:48:43 AM CST 2025] Installing key to: /etc/nginx/ssl/cyanfish.site/key.pem
[Tue Sep 9 11:48:43 AM CST 2025] Installing full chain to: /etc/nginx/ssl/cyanfish.site/fullchain.cer
[Tue Sep 9 11:48:43 AM CST 2025] Running reload cmd: service nginx reload
==== AUTHENTICATING FOR org.freedesktop.systemd1.manage-units ===
Authentication is required to reload 'nginx.service'.
Authenticating as: qcloud (ubuntu)
Password: Failed to reload nginx.service: Connection timed out
See system logs and 'systemctl status nginx.service' for details.
polkit-agent-helper-1: pam_authenticate failed: Authentication failure
[Tue Sep 9 11:49:08 AM CST 2025] Reload error for: cyanfish.site

那么我们现在需要给ubuntu用户免密执行该命令的权限,给reloadcmd加一个sudo试试:

1
2
3
4
acme.sh --install-cert -d cyanfish.site \
--key-file /etc/nginx/ssl/cyanfish.site/key.pem \
--fullchain-file /etc/nginx/ssl/cyanfish.site/fullchain.cer \
--reloadcmd "sudo service nginx reload"

执行成功😆,输出如下:

1
2
3
4
5
6
7
8
9
ubuntu@VM-16-3-ubuntu:/etc/nginx/ssl/cyanfish.site$ acme.sh --install-cert -d cyanfish.site \
--key-file /etc/nginx/ssl/cyanfish.site/key.pem \
--fullchain-file /etc/nginx/ssl/cyanfish.site/fullchain.cer \
--reloadcmd "sudo service nginx reload"
[Tue Sep 9 11:56:47 AM CST 2025] The domain 'cyanfish.site' seems to already have an ECC cert, let's use it.
[Tue Sep 9 11:56:47 AM CST 2025] Installing key to: /etc/nginx/ssl/cyanfish.site/key.pem
[Tue Sep 9 11:56:47 AM CST 2025] Installing full chain to: /etc/nginx/ssl/cyanfish.site/fullchain.cer
[Tue Sep 9 11:56:47 AM CST 2025] Running reload cmd: sudo service nginx reload
[Tue Sep 9 11:56:47 AM CST 2025] Reload successful

5.查看已安装的证书信息:

执行如下命令,验证证书是否安装成功:

1
acme.sh --info -d cyanfish.site

输出如下,说明证书已安装成功,且证书每 60 天自动更新,你无需任何操作!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
ubuntu@VM-16-3-ubuntu:/etc/nginx/ssl/cyanfish.site$ acme.sh --info -d cyanfish.site
[Tue Sep 9 11:58:11 AM CST 2025] The domain 'cyanfish.site' seems to already have an ECC cert, let's use it.
DOMAIN_CONF=/home/ubuntu/.acme.sh/cyanfish.site_ecc/cyanfish.site.conf
Le_Domain=cyanfish.site
Le_Alt=*.cyanfish.site
Le_Webroot=dns_dp
Le_PreHook=
Le_PostHook=
Le_RenewHook=
Le_API=https://acme.zerossl.com/v2/DV90
Le_Keylength=ec-256
Le_OrderFinalize=https://acme.zerossl.com/v2/DV90/order/dNSnMfE8Co8UsJDEUOWRdQ/finalize
Le_RealCertPath=
Le_RealCACertPath=
Le_RealKeyPath=/etc/nginx/ssl/cyanfish.site/key.pem
Le_ReloadCmd=sudo service nginx reload
Le_RealFullChainPath=/etc/nginx/ssl/cyanfish.site/fullchain.cer
Le_LinkOrder=https://acme.zerossl.com/v2/DV90/order/dNSnMfE8Co8UsJDEUOWRdQ
Le_LinkCert=https://acme.zerossl.com/v2/DV90/cert/AIS4u6H7W6dZjVE1EebE5Q
Le_CertCreateTime=1757389672
Le_CertCreateTimeStr=2025-09-09T03:47:52Z
Le_NextRenewTimeStr=2025-11-07T03:47:52Z
Le_NextRenewTime=1762487272

6.配置nginx中网站的ssl证书信息:

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
# HTTPS 配置(新增)
server {
listen 443 ssl http2;
server_name cyanfish.site www.cyanfish.site; #网站域名

# SSL 证书配置
ssl_certificate /etc/nginx/ssl/cyanfish.site/fullchain.pem;
ssl_certificate_key /etc/nginx/ssl/cyanfish.site/key.pem;

# SSL 协议和加密配置
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;

# 安全头部
add_header X-Frame-Options "SAMEORIGIN";
add_header X-XSS-Protection "1; mode=block";
add_header Cache-Control "public, max-age=3600";

# 网站内容配置
location / {
root /var/www/blog/public; #网站内容路径
index index.html index.htm;
try_files $uri $uri.html $uri/ =404;
expires 7d;
}
}

至此,服务器本地的ssl证书更新自动化已完成!😋

证书上传及CDN配置自动化

如果你的网站使用了CDN加速,那么还需要上传服务器本地的证书到CDN服务商,并配置绑定到CDN加速的域名,具体实践步骤如下:

1.工具安装

本过程全程依赖于腾讯云提供的证书上传及CDN配置接口,都需要通过腾讯官方命令行工具tccli来调用,先执行如下安装命令:

1
sudo pip install tccli-intl-en

安装成功的输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
root@VM-16-3-ubuntu:/home/ubuntu# sudo pip install tccli-intl-en
Looking in indexes: http://mirrors.tencentyun.com/pypi/simple
Collecting tccli-intl-en
Downloading http://mirrors.tencentyun.com/pypi/packages/12/37/ea374b123a0bb01c60011538ad3a5f526e7846165657b3bfa2a1de1250e5/tccli_intl_en-3.0.1255.1-py2.py3-none-any.whl (5.6 MB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 5.6/5.6 MB 9.3 MB/s eta 0:00:00
Collecting tencentcloud-sdk-python-intl-en>=3.0.1255
Downloading http://mirrors.tencentyun.com/pypi/packages/6e/bb/b5647cfecc7063fa6978de742abba1c4783d244c67c6e2513481bcef22d1/tencentcloud_sdk_python_intl_en-3.0.1273-py2.py3-none-any.whl (6.7 MB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 6.7/6.7 MB 9.4 MB/s eta 0:00:00
Collecting jmespath==0.10.0
Downloading http://mirrors.tencentyun.com/pypi/packages/07/cb/5f001272b6faeb23c1c9e0acc04d48eaaf5c862c17709d20e3469c6e0139/jmespath-0.10.0-py2.py3-none-any.whl (24 kB)
Requirement already satisfied: six==1.16.0 in /usr/lib/python3/dist-packages (from tccli-intl-en) (1.16.0)
Requirement already satisfied: requests>=2.16.0 in /usr/lib/python3/dist-packages (from tencentcloud-sdk-python-intl-en>=3.0.1255->tccli-intl-en) (2.25.1)
Installing collected packages: tencentcloud-sdk-python-intl-en, jmespath, tccli-intl-en
Successfully installed jmespath-0.10.0 tccli-intl-en-3.0.1255.1 tencentcloud-sdk-python-intl-en-3.0.1273
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv

root@VM-16-3-ubuntu:/home/ubuntu# tccli --version
3.0.1255.1

然后配置tccli,使其获得操作权限

1
2
tccli configure set secretId "你的腾讯云secretId"
tccli configure set secretKey "你的腾讯云secretKey"

腾讯云API的secretId和sercretKey可以从腾讯云官网-访问管理中获取,建议添加子用户,并配置CDN及SSL的全读写权限

image-20250909224114778

image-20250909224422630

2.证书上传

tccli配置完成后执行如下命令,测试能否上传证书:

1
2
3
4
5
tccli ssl UploadCertificate \
--CertificatePublicKey "$(cat /etc/nginx/ssl/cyanfish.site/fullchain.cer)" \
--CertificatePrivateKey "$(cat /etc/nginx/ssl/cyanfish.site/key.pem)" \
--CertificateType "SVR" \
--Alias "cyanfish-site-cert-$(date +%Y%m%d)" # 可选:给证书起个可识别的别名

成功的输出如下:

1
2
3
4
5
6
7
8
9
10
ubuntu@VM-16-3-ubuntu:~$ tccli ssl UploadCertificate \
--CertificatePublicKey "$(cat /etc/nginx/ssl/cyanfish.site/fullchain.cer)" \
--CertificatePrivateKey "$(cat /etc/nginx/ssl/cyanfish.site/key.pem)" \
--CertificateType "SVR" \
--Alias "cyanfish-site-cert-$(date +%Y%m%d)" # 可选:给证书起个可识别的别名
{
"CertificateId": "RH5n72W9",
"RepeatCertId": "",
"RequestId": "59be0db8-395d-48ef-a5b4-a0473f1319c8"
}

🧐参数里的SVR是什么?

这是与另一种证书类型 CA(Certificate Authority,证书颁发机构证书)相对应的证书类型,也就是服务器证书,固定这么写就行了。

SVR与CA证书的对比如下:

证书类型 (CertificateType) 用途 示例
SVR 服务器证书。用于证明服务器身份,建立 HTTPS 加密连接。 你的网站 cyanfish.site使用的 SSL 证书。
CA CA 根证书或中间证书。用于签发和验证其他证书的身份。 Let‘s Encrypt、DigiCert 等机构自己的证书。

然后上腾讯云看一下,新证书果然已经上传,美滋滋😋:

image-20250909165221544

3.证书配置

证书上传完成后,还需要将新证书配置给CDN加速的域名,执行如下命令即可:

1
2
3
4
tccli cdn ModifyDomainConfig --cli-unfold-argument \
--Domain cyanfish.site \
--Route Https.CertInfo.CertId \
--Value '{"update":"这里换成你上传的新证书ID"}'

证书ID获取

细心的小伙伴可能注意到了,之前上传证书完成后,控制台打印了一个json,这其中就有新证书的ID

这个证书ID可以从之前的控制台复制,如果你找不到了,也可以到腾讯云SSL控制台里找到,将其复制下来,填入上面的ID位置,来验证证书配置命令

image-20250909230218223

成功的输出如下:

1
2
3
4
5
6
7
ubuntu@VM-16-3-ubuntu:~$ tccli cdn ModifyDomainConfig --cli-unfold-argument \
--Domain cyanfish.site \
--Route Https.CertInfo.CertId \
--Value '{"update":"RHPxxK6n"}'
{
"RequestId": "b2cf9133-e5dd-45a0-a8cd-ff23c644e872"
}

4.证书上传及配置自动化

上面的两个脚本如果添加到acme.sh的reloadcmd参数中也能实现自动化,但如果后续需要修改某些参数则不利于维护。

sh脚本封装

我这里将命令写到一个sh脚本中,并存放到/usr/local/bin/目录下,命名为uploadCert:

1
touch /usr/local/bin/uploadCert #新建一个空文件

写入如下代码:

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
#!/bin/bash
# 用途:将证书上传至腾讯云并绑定到多个 CDN 域名

#版本说明
echo "开始执行ssl证书上传脚本,版本:0.9"

# 配置部分
DOMAINS=("cyanfish.site" "picture.cyanfish.site" "demo.cyanfish.site") # 需要配置的域名列表
CERT_PUBLIC_KEY_FILE="/etc/nginx/ssl/cyanfish.site/fullchain.cer" # 证书公钥路径
CERT_PRIVATE_KEY_FILE="/etc/nginx/ssl/cyanfish.site/key.pem" # 私钥路径

# 检查证书文件是否存在
if [ ! -f "$CERT_PUBLIC_KEY_FILE" ] || [ ! -f "$CERT_PRIVATE_KEY_FILE" ]; then
echo "错误:证书文件不存在!"
echo "公钥路径: $CERT_PUBLIC_KEY_FILE"
echo "私钥路径: $CERT_PRIVATE_KEY_FILE"
exit 1
fi

# 上传证书到腾讯云
echo "正在上传证书到腾讯云..."
CERT_JSON=$(tccli ssl UploadCertificate \
--CertificatePublicKey "$(cat "$CERT_PUBLIC_KEY_FILE")" \
--CertificatePrivateKey "$(cat "$CERT_PRIVATE_KEY_FILE")" \
--CertificateType SVR \
--Alias "multi-domain-cert-$(date +%Y%m%d-%H%M%S)")

# 提取 CertificateId
CERT_ID=$(echo "$CERT_JSON" | jq -r '.CertificateId')
if [ -z "$CERT_ID" ]; then
echo "错误:获取 CertificateId 失败!原始响应:"
echo "$CERT_JSON"
exit 1
fi
echo "证书上传成功,CertificateId: $CERT_ID"

# 为每个域名配置证书
for DOMAIN in "${DOMAINS[@]}"; do
echo "正在配置 CDN 域名 $DOMAIN 使用新证书..."
tccli cdn ModifyDomainConfig --cli-unfold-argument --Domain "$DOMAIN" --Route "Https.CertInfo.CertId" --Value "{\"update\":\"$CERT_ID\"}" # 双引号确保变量展开

# 检查配置是否成功
if [ $? -eq 0 ]; then
echo "域名 $DOMAIN 证书配置成功!"
else
echo "警告:域名 $DOMAIN 证书配置可能失败!"
fi
done

echo "所有域名证书部署完成!"

🛠代码中的jq工具

为了解析JSON中的证书ID,使用了jq工具,安装jq:

1
sudo apt install jq

执行如下命令测试jq,可以看到jq正确解析出了JSON中我们想要的值:

1
2
ubuntu@VM-16-3-ubuntu:~$ echo '{"CertificateId":"cert-123"}' | jq -r '.CertificateId'
cert-123

uploadCert脚本添加可执行权限:

1
sudo chmod +x /usr/local/bin/uploadCert

执行脚本验证可用性:

1
uploadCert

成功的输出如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
ubuntu@VM-16-3-ubuntu:/usr/local/bin$ uploadCert
开始执行ssl证书上传脚本,版本:0.9
正在上传证书到腾讯云...
证书上传成功,CertificateId: RHPawZEw
正在配置 CDN 域名 cyanfish.site 使用新证书...
{
"RequestId": "df322dd6-e364-4109-a085-4517bcd3bab3"
}
域名 cyanfish.site 证书配置成功!
正在配置 CDN 域名 picture.cyanfish.site 使用新证书...
{
"RequestId": "825b8a2e-1b64-409d-9fbd-1aaf8b5f1a6e"
}
域名 picture.cyanfish.site 证书配置成功!
正在配置 CDN 域名 demo.cyanfish.site 使用新证书...
{
"RequestId": "ec66dcc2-ba9c-41e5-bb81-ddba5cb12e8c"
}
域名 demo.cyanfish.site 证书配置成功!
所有域名证书部署完成!
acme.sh配置

然后执行如下命令,更新acme.sh的reloadcmd:

1
2
3
4
acme.sh --install-cert -d cyanfish.site \
--key-file /etc/nginx/ssl/cyanfish.site/key.pem \
--fullchain-file /etc/nginx/ssl/cyanfish.site/fullchain.cer \
--reloadcmd "sudo service nginx reload && uploadCert"

💡reloadcmd参数是一个命令,每60天(默认)acme.sh更新ssl证书时,会自动触发该参数中配置的命令

❤执行以上命令后,每次证书更新后,nginx会重启应用证书的更新,并且会执行uploadCert脚本

最终成功的输出如下:

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
ubuntu@VM-16-3-ubuntu:/usr/local/bin$ acme.sh --install-cert -d cyanfish.site \
--key-file /etc/nginx/ssl/cyanfish.site/key.pem \
--fullchain-file /etc/nginx/ssl/cyanfish.site/fullchain.cer \
--reloadcmd "sudo service nginx reload && uploadCert"
[Tue Sep 9 09:39:07 PM CST 2025] The domain 'cyanfish.site' seems to already have an ECC cert, let's use it.
[Tue Sep 9 09:39:07 PM CST 2025] Installing key to: /etc/nginx/ssl/cyanfish.site/key.pem
[Tue Sep 9 09:39:07 PM CST 2025] Installing full chain to: /etc/nginx/ssl/cyanfish.site/fullchain.cer
[Tue Sep 9 09:39:07 PM CST 2025] Running reload cmd: sudo service nginx reload && uploadCert
开始执行ssl证书上传脚本,版本:0.9
正在上传证书到腾讯云...
证书上传成功,CertificateId: RHPxxK6n
正在配置 CDN 域名 cyanfish.site 使用新证书...
{
"RequestId": "f5d3cf3e-34db-4845-85a9-72472a843c6a"
}
域名 cyanfish.site 证书配置成功!
正在配置 CDN 域名 picture.cyanfish.site 使用新证书...
{
"RequestId": "ece0e617-d6c0-4552-beac-b5e7402fdd8e"
}
域名 picture.cyanfish.site 证书配置成功!
正在配置 CDN 域名 demo.cyanfish.site 使用新证书...
{
"RequestId": "4a9aaf4d-2b6a-40de-9202-57fd00b0b855"
}
域名 demo.cyanfish.site 证书配置成功!
所有域名证书部署完成!
[Tue Sep 9 09:39:16 PM CST 2025] Reload successful
ubuntu@VM-16-3-ubuntu:/usr/local/bin$

最后上腾讯云看一眼CDN里的证书,配置成功!😆

image-20250909214243308

大功告成,妈妈再也不用担心我每三个月都要手动申请并上传一次ssl证书了,好耶!ㄟ(≧◇≦)ㄏ

最终成果

  • ✅ 证书自动申请和续期(每60天)
  • ✅ 自动安装到本地 Nginx
  • ✅ 自动上传到腾讯云 SSL 证书服务
  • ✅ 自动配置到所有 CDN 域名
  • ✅ 全程无需人工干预

参考资料

1.acme.sh官网中文文档

2.tccli官方文档

3.nginx SSL配置指南