申请通配符域名证书并自动推送至阿里云 CDN

功能和特性

  • 能够申请域名通配符证书
  • 每两个月自动 renew 证书
  • 自动将证书推送到阿里云 CDN

准备

  • Linux 服务器(参考系统:Ubuntu 20.04)
  • 在阿里云托管的域名 example.com
  • 正确配置阿里云 CDN(HTTP / HTTPS 回源都可)
    • 假设 CDN 访问域名为 example.comwww.example.com

步骤

证书申请部分参考了下文:

如何使用acme.sh与阿里云DNS自动签发Let’s Encrypt的免费数字证书 - 初心 (mayanpeng.cn)

阿里云建立 RAM 用户并授权

RAM 访问控制 (aliyun.com)

  1. 创建用户 > 随意填写登录名称 > 勾选“OpenAPI 调用访问” > 确定;
  2. 记录 AccessKey ID <AccessKeyId> 和 AccessKey Secret <AccessKeySecret>(之后看不到了,但可以创建新的 AccessKey);
  3. 添加权限:
    • 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
京ICP备17016743号
Built with Hugo
主题 StackJimmy 设计