功能和特性
- 能够申请域名通配符证书
- 每两个月自动 renew 证书
- 自动将证书推送到阿里云 CDN
准备
- Linux 服务器(参考系统:Ubuntu 20.04)
- 在阿里云托管的域名
example.com
- 正确配置阿里云 CDN(HTTP / HTTPS 回源都可)
- 假设 CDN 访问域名为
example.com
和 www.example.com
步骤
证书申请部分参考了下文:
如何使用acme.sh与阿里云DNS自动签发Let’s Encrypt的免费数字证书 - 初心 (mayanpeng.cn)
阿里云建立 RAM 用户并授权
RAM 访问控制 (aliyun.com)
- 创建用户 > 随意填写登录名称 > 勾选“OpenAPI 调用访问” > 确定;
- 记录 AccessKey ID
<AccessKeyId>
和 AccessKey Secret <AccessKeySecret>
(之后看不到了,但可以创建新的 AccessKey);
- 添加权限:
- AliyunDNSFullAccess 管理云解析(DNS)的权限
- AliyunYundunCertFullAccess 管理云盾证书服务的权限
- AliyunCDNFullAccess 管理CDN的权限
服务器安装 acme.sh
以下假设初始目录为 /root
。
acmesh-official/acme.sh: A pure Unix shell script implementing ACME client protocol (github.com)
修改为自己的联系邮箱。
1
2
3
|
git clone https://github.com/acmesh-official/acme.sh.git
cd ./acme.sh
./acme.sh --install -m my@example.com
|
根据提示,安装完 acme.sh 需要重启或开启新终端,以生效 acme.sh 命令。acme.sh 会自动添加随机时间的定时执行任务(每天一次),可以通过 crontab -l
查看。
服务器安装 aliyun-openapi-bash-sdk
Hill-98/aliyun-openapi-bash-sdk: [Unofficial] Alibaba Cloud SDK for Bash (github.com)
1
|
git clone https://github.com/Hill-98/aliyun-openapi-bash-sdk.git
|
准备证书自动推送到 CDN 的脚本
安装 jq。
1
2
|
sudo apt update
sudo apt install jq
|
参考 aliyun-openapi-bash-sdk 中的 examples/UpdateSSLCert.sh
修改(修复一些 bug,更新使用较新的阿里云 API),填入 RAM 用户 步骤记录的 <AccessKeyId>
和 <AccessKeySecret>
,修改 aliyun-openapi-bash-sdk 的 AliyunOpenApiSDK.sh
的路径,并修改 CDN 域名列表。
/root/.acme.sh/UpdateSSLCert.sh
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
|
#!/usr/bin/env bash
# 使用的 OpenAPI
# CAS: https://help.aliyun.com/document_detail/126507.html
# CDN:https://help.aliyun.com/document_detail/106661.html
# 可配合 acme.sh 使用的 renewHook 脚本:自动将新证书上传至阿里云并更新对应 CDN 域名,然后删除对应域名的旧证书。
# 每次 API 执行都会检测是否失败,如果失败,会中断脚本执行并返回自定义错误代码。
# RIBO: 修改为自己的 AccessKey
AliAccessKeyId="<AccessKeyId>"
AliAccessKeySecret="<AccessKeySecret>"
# shellcheck source=AliyunOpenApiSDK.sh
# RIBO: 这里需要修改 aliyun-openapi-bash-sdk 的路径
source /root/aliyun-openapi-bash-sdk/AliyunOpenApiSDK.sh
# acme.sh 执行 renewHook 时导出的环境变量列表
ACME_ENV_LIST=(
"CERT_KEY_PATH"
"CERT_FULLCHAIN_PATH"
"Le_Domain"
)
# 检查环境变量是否存在
for value in "${ACME_ENV_LIST[@]}" ; do
[[ -v "$value" ]] || exit 1
done
unset value
# 获取证书自定义函数
get_cert() {
# 使用 sed 删除掉证书文件的空行
sed -e "/^$/d" "$CERT_FULLCHAIN_PATH"
}
# 获取密钥自定义函数
get_key() {
cat "$CERT_KEY_PATH"
}
# shellcheck disable=SC2154
DOMAIN=$Le_Domain
# 证书名称 (替换域名的 . 为 _,以符合阿里云证书名称规范)
CERT_NAME="${DOMAIN//./_}-$(date +%s)"
# 需要更新证书的 CDN 域名列表
# RIBO: 修改这里的 CDN 域名列表
DOMAIN_LIST=(
"example.com"
"www.example.com"
)
# 获取证书列表
result=$(aliapi_rpc GET cas.aliyuncs.com 2018-07-13 DescribeUserCertificateList --CurrentPage 1 --ShowSize 50) || exit 101
# 使用 jq 处理返回的 JSON 数据并提取出匹配当前证书域名的证书列表的 ID,用于稍后的删除旧证书操作。
cert_list=$(jq -cr ".CertificateList|map(select(.common == \"$DOMAIN\"))|map(.id)|.[]" <<< "$result")
# 上传新的证书
result=$(aliapi_rpc GET cas.aliyuncs.com 2020-04-07 UploadUserCertificate --Cert "get_cert()" --Key "get_key()" --Name "$CERT_NAME") || exit 102
cert_id=$(jq -cr ".CertId" <<< "$result")
# 设置 CDN 域名列表使用新的证书
for _domain in "${DOMAIN_LIST[@]}"; do
aliapi_rpc GET cdn.aliyuncs.com 2018-05-10 SetCdnDomainSSLCertificate --DomainName "$_domain" --CertName "$CERT_NAME" --CertId "$cert_id" --CertType cas --SSLProtocol on || exit 103
done
unset _domain
# 删除旧的证书
for _id in ${cert_list}; do
aliapi_rpc GET cas.aliyuncs.com 2018-07-13 DeleteUserCertificate --CertId "$_id" || exit 104
done
unset _id
|
acme.sh 申请证书
设置环境变量
使用 RAM 用户 步骤记录的 <AccessKeyId>
和 <AccessKeySecret>
。
1
2
|
export Ali_Key="<AccessKeyId>"
export Ali_Secret="<AccessKeySecret>"
|
只需要执行这一次,acme.sh 会将其保存在 /root/.acme.sh/account.conf
,后续自动 renew 时会用到。
签发通配符证书并设置 renew-hook
修改域名和 自动推送脚本 的路径,注意需要同时包含根域名和通配符域名。
1
|
acme.sh --issue --dns dns_ali -d example.com -d *.example.com --renew-hook /root/.acme.sh/UpdateSSLCert.sh
|
安装证书并配置 nginx
假设 nginx 已经配置好证书存放的路径。
修改域名和证书存放的路径,注意此时只需要根域名。
1
|
acme.sh --install-cert -d example.com --key-file /cert/example.com.key --fullchain-file /cert/example.com.pem --reloadcmd "systemctl restart nginx"
|
查看 acme.sh 配置信息
使用 acme.sh --info -d example.com
查看 acme.sh 自动配置的信息,在 renew 时会使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
DOMAIN_CONF=/root/.acme.sh/example.com_ecc/example.com.conf
Le_Domain=example.com
Le_Alt=*.example.com
Le_Webroot=dns_ali
Le_PreHook=
Le_PostHook=
Le_RenewHook=/root/.acme.sh/UpdateSSLCert.sh
Le_API=https://acme.zerossl.com/v2/DV90
Le_Keylength=ec-256
Le_OrderFinalize=https://acme.zerossl.com/v2/DV90/order/<...>/finalize
Le_LinkOrder=https://acme.zerossl.com/v2/DV90/order/<...>
Le_LinkCert=https://acme.zerossl.com/v2/DV90/cert/<...>
Le_CertCreateTime=1700301018
Le_CertCreateTimeStr=2023-11-18T09:50:18Z
Le_NextRenewTimeStr=2024-01-16T09:50:18Z
Le_NextRenewTime=1705398618
Le_RealCertPath=
Le_RealCACertPath=
Le_RealKeyPath=/cert/example.com.key
Le_ReloadCmd=systemctl restart nginx
Le_RealFullChainPath=/cert/example.com.pem
|
检查证书是否推送到 CDN
https://yundun.console.aliyun.com/?p=cas#/certExtend/upload/cn-hangzhou
“上传证书”中应当有刚才申请的证书。
CDN管理控制台 (aliyun.com)
“管理” > “HTTPS配置” > “HTTPS证书”应当显示“已开启”,并显示通配符证书。
如果没有,手动设置3个环境变量并运行推送脚本。
1
2
3
4
|
export CERT_KEY_PATH=/root/.acme.sh/example.com_ecc/example.com.key
export CERT_FULLCHAIN_PATH=/root/.acme.sh/example.com_ecc/fullchain.cer
export Le_Domain=example.com
bash /root/.acme.sh/UpdateSSLCert.sh
|