群晖SynologyNAS DSM通过 acme.sh 实现域名证书自动更新
注意事项:
①尽量使使用root帐号去操作,管理员帐号可能会出现各种报错。
②群晖默认证书保存目录:
/usr/syno/etc/certificate/_archive/$(cat /usr/syno/etc/certificate/_archive/DEFAULT)
默认情况下群晖的默认证书是官方的证书,acme导入证书前一定先手动导入一次证书。
③阿里与腾讯及其他域名供应商的 dns api 参数不一样,具体可参考 Acme.sh 官方文档 中的「DNS API」列表。
https://github.com/acmesh-official/acme.sh/wiki/dnsapi
④准备申请证书的域名一定先去 阿里云、腾讯云(DNSPod) 看下有没有解析记录,新申请的域名或者从没有使用的二级域名会申请失败。
一、准备工作
登录 阿里云、腾讯云(DNSPod) 创建并获取API密钥保存下来。
二、使用ssh登录群晖
1.下载acme.sh
acme.sh 是一个用于自动化获取和管理 Let’s Encrypt 证书的脚本。
curl https://get.acme.sh | sh -s email=1396053199@qq.com --force 或 wget -O - https://get.acme.sh | sh -s email=1396053199@qq.com --force
将 1396053199@qq.com 替换为自己的有效电子邮件地址,用于接收 Let’s Encrypt 的重要通知,如证书即将过期等信息。
安装过程会有报错,群晖系统默认没有安装或没有权限启用 cron 服务(定时任务工具),而 Acme.sh 依赖 cron 实现证书自动续签。
可以忽略这个报错,最后出现 Install success 就可以。acme.sh 会自动添加到系统环境变量中。

如果成功安装,脚本会将 acme.sh 下载到当前用户目录下,也就是 ~/.acme.sh 目录。
通过 cd ~/.acme.sh 或 cd /var/services/homes/当前登录的用户名/.acme.sh 进入目录
使用命令验证 acme.sh 是否安装成功:
cd ~/.acme.sh #或 cd /var/services/homes/sunfeng/.acme.sh “sunfeng”为我当前登录的用户名 ./acme.sh --version

2. 添加API密钥至/etc/profile
#阿里云 API 密钥添加到环境变量 echo "export Ali_Key="AccessKey ID"" >> /etc/profile echo "export Ali_Secret="AccessKey Secret"" >> /etc/profile && source /etc/profile # 腾讯云 DNSPod 的 API 密钥添加到环境变量 echo "export DP_Id="AccessKey ID"" >> /etc/profile echo "export DP_Key="AccessKey Token"" >> /etc/profile && source /etc/profile
# 删除包含"export DP_Id="的行 sudo sed -i '/export DP_Id=/d' /etc/profile # 删除包含"export DP_Key="的行 sudo sed -i '/export DP_Key=/d' /etc/profile
3.申请证书,填写的域名一定是阿里、腾讯或其他域名供应商的管理页面上有过解析记录
./acme.sh --issue --dns dns_ali -d example.com -d *.example.com ./acme.sh # 使用 acme.sh 脚本执行证书申请操作 --issue # 核心参数:表示证书申请流程 --dns dns_ali # 指定 DNS 验证方式,这里使用阿里云(Aliyun)的 DNS 解析接口 -d example.com # 为主域名 example.com 申请证书 -d *.example.com # 同时为该域名的所有子域名(通配符)申请证书

证书申请成功后会保存在 ~/.acme.sh (/root/.acme.sh/) 目录下
4、查看已安装证书信息:
acme.sh --info -d example.com # example.com 改为自己的域名
三、替换群晖NAS的默认证书
新申请的证书保存名录:
# ls /root/.acme.sh/ 可以看到多了一个example.com 或者 example.com_ecc的目录 此目录下就是新申请的证书 /root/.acme.sh/example.com_ecc/
群晖默认证书保存目录:
/usr/syno/etc/certificate/_archive/$(cat /usr/syno/etc/certificate/_archive/DEFAULT)
sudo cp -f "/root/.acme.sh/example.com_ecc/fullchain.cer" "/usr/syno/etc/certificate/_archive/$(cat /usr/syno/etc/certificate/_archive/DEFAULT)/cert.pem" sudo cp -f "/root/.acme.sh/example.com_ecc/hkcnas.com.key" "/usr/syno/etc/certificate/_archive/$(cat /usr/syno/etc/certificate/_archive/DEFAULT)/privkey.pem" sudo cp -f "/root/.acme.sh/example.com_ecc/ca.cer" "/usr/syno/etc/certificate/_archive/$(cat /usr/syno/etc/certificate/_archive/DEFAULT)/chain.pem"
然后重启 Nginx 即可看到证书已更新
nginx -s reload
四、设置自动更新
acme 申请证书每 60 天自动更新,默认情况下你无需任何操作,但是最开始下载安装 acme 时 cron 服务报错,不确定是否可以自动更新。
acme可以强制续签证书:
acme.sh --renew -d example.com --force # example.com 改为自己的域名
!!!修改下面脚本中的 DOMAIN="example.com" 与 DNS_MODE="dns_ali"
未检测到证书:强制执行续签
证书已过期(剩余 0 天):强制执行续签
证书剩余有效期≤10 天:强制执行续签
证书剩余有效期 > 10 天:不执行任何操作(无论是否超过 20 天)
#!/bin/bash
set -e # 遇到错误立即退出
# ==================== 配置参数 ====================
DOMAIN="example.com" # 你的域名
ACME_CERT_PATH="/root/.acme.sh" # acme.sh证书存放路径
SYNO_CERT_ARCHIVE="/usr/syno/etc/certificate/_archive" # 群晖证书存档目录
DNS_MODE="dns_ali" # DNS验证方式(根据你的服务商修改 https://github.com/acmesh-official/acme.sh/wiki/dnsapi)
CERT_TYPE="ecc" # 证书类型(ecc/rsa,留空自动适配)
RENEW_THRESHOLD=10 # 少于此天数时强制续签(天)
# =================================================
# 函数:显示信息
info() {
echo -e "\033[1;34m=== $1 ===\033[0m"
}
# 函数:显示成功信息
success() {
echo -e "\033[1;32m=== $1 ===\033[0m"
}
# 函数:显示错误信息
error() {
echo -e "\033[1;31m=== 错误:$1 ===\033[0m"
exit 1
}
# 函数:检查证书剩余有效期(天)
check_cert_validity() {
local cert_file=$1
if [ ! -f "$cert_file" ]; then
echo 0
return
fi
# 获取证书过期时间(Unix时间戳)
local end_date=$(openssl x509 -in "$cert_file" -noout -enddate | cut -d= -f2)
local end_timestamp=$(date -d "$end_date" +%s 2>/dev/null || true)
local current_timestamp=$(date +%s)
if [ -z "$end_timestamp" ] || [ "$end_timestamp" -le "$current_timestamp" ]; then
echo 0 # 证书已过期
return
fi
# 计算剩余天数
local diff_seconds=$((end_timestamp - current_timestamp))
local diff_days=$((diff_seconds / 86400)) # 86400秒=1天
echo $diff_days
}
# 1. 检查证书是否已存在(支持RSA和ECC)
info "检查证书是否已存在"
CERT_DIR_RSA="$ACME_CERT_PATH/$DOMAIN"
CERT_DIR_ECC="$ACME_CERT_PATH/${DOMAIN}_ecc"
CERT_EXISTS=0
CURRENT_CERT_DIR=""
CERT_FILE=""
# 优先检查配置的证书类型
if [ "$CERT_TYPE" = "ecc" ] && [ -d "$CERT_DIR_ECC" ]; then
CURRENT_CERT_DIR="$CERT_DIR_ECC"
CERT_FILE="$CURRENT_CERT_DIR/fullchain.cer"
CERT_EXISTS=1
elif [ "$CERT_TYPE" = "rsa" ] && [ -d "$CERT_DIR_RSA" ]; then
CURRENT_CERT_DIR="$CERT_DIR_RSA"
CERT_FILE="$CURRENT_CERT_DIR/fullchain.cer"
CERT_EXISTS=1
# 未指定类型时自动检测
elif [ -z "$CERT_TYPE" ] && [ -d "$CERT_DIR_ECC" ]; then
CURRENT_CERT_DIR="$CERT_DIR_ECC"
CERT_FILE="$CURRENT_CERT_DIR/fullchain.cer"
CERT_TYPE="ecc"
CERT_EXISTS=1
elif [ -z "$CERT_TYPE" ] && [ -d "$CERT_DIR_RSA" ]; then
CURRENT_CERT_DIR="$CERT_DIR_RSA"
CERT_FILE="$CURRENT_CERT_DIR/fullchain.cer"
CERT_TYPE="rsa"
CERT_EXISTS=1
fi
if [ $CERT_EXISTS -eq 1 ]; then
echo "发现已存在的$CERT_TYPE证书:$CURRENT_CERT_DIR"
# 检查证书剩余有效期
info "检查证书剩余有效期"
REMAIN_DAYS=$(check_cert_validity "$CERT_FILE")
echo "证书剩余有效期:$REMAIN_DAYS 天"
# 根据剩余天数执行不同操作
if [ $REMAIN_DAYS -eq 0 ]; then
echo "证书已过期,将强制执行续签"
elif [ $REMAIN_DAYS -le $RENEW_THRESHOLD ]; then
echo "证书剩余有效期不足$RENEW_THRESHOLD天,将执行续签"
else
success "证书有效期充足(剩余$REMAIN_DAYS天),无需更新"
exit 0
fi
else
echo "未发现已存在的证书,将执行新申请"
# 未指定类型时默认使用ECC
if [ -z "$CERT_TYPE" ]; then
CERT_TYPE="ecc"
echo "默认使用ECC证书类型"
fi
fi
# 2. 生成证书命令参数
info "准备证书更新/申请命令"
ACME_CMD_BASE="$ACME_CERT_PATH/acme.sh"
DOMAIN_PARAMS="-d $DOMAIN -d *.${DOMAIN}"
FORCE_PARAM="--force"
DNS_PARAM="--dns $DNS_MODE"
CERT_TYPE_PARAM=""
if [ "$CERT_TYPE" = "ecc" ]; then
CERT_TYPE_PARAM="--ecc"
fi
# 3. 执行更新或申请
info "执行证书操作($([ $CERT_EXISTS -eq 1 ] && echo "续签" || echo "新申请"))"
if [ $CERT_EXISTS -eq 1 ]; then
# 证书已存在,执行续签
if ! $ACME_CMD_BASE --renew $DOMAIN_PARAMS $FORCE_PARAM $DNS_PARAM $CERT_TYPE_PARAM; then
error "证书续签失败,请检查acme.sh配置和网络"
fi
else
# 证书不存在,执行新申请
if ! $ACME_CMD_BASE --issue $DOMAIN_PARAMS $FORCE_PARAM $DNS_PARAM $CERT_TYPE_PARAM; then
error "证书申请失败,请检查acme.sh配置和网络"
fi
# 申请后更新证书目录变量
if [ "$CERT_TYPE" = "ecc" ]; then
CURRENT_CERT_DIR="$CERT_DIR_ECC"
else
CURRENT_CERT_DIR="$CERT_DIR_RSA"
fi
CERT_FILE="$CURRENT_CERT_DIR/fullchain.cer"
fi
# 4. 验证证书文件是否存在
info "验证证书文件完整性"
REQUIRED_FILES=(
"$CURRENT_CERT_DIR/fullchain.cer"
"$CURRENT_CERT_DIR/$DOMAIN.key"
"$CURRENT_CERT_DIR/ca.cer"
)
for file in "${REQUIRED_FILES[@]}"; do
if [ ! -f "$file" ]; then
error "缺少必要的证书文件:$file"
fi
done
# 5. 复制证书到群晖默认目录并替换
info "复制证书到群晖系统目录"
# 获取群晖当前默认证书ID
CERT_ID=$(cat "$SYNO_CERT_ARCHIVE/DEFAULT" 2>/dev/null || true)
if [ -z "$CERT_ID" ]; then
error "无法获取群晖默认证书ID,请检查证书目录"
fi
DEST_DIR="$SYNO_CERT_ARCHIVE/$CERT_ID"
if [ ! -d "$DEST_DIR" ]; then
error "群晖证书目标目录不存在:$DEST_DIR"
fi
# 强制复制并覆盖
echo "复制证书到目标目录:$DEST_DIR"
cp -f "$CURRENT_CERT_DIR/fullchain.cer" "$DEST_DIR/cert.pem" || error "复制证书文件失败"
cp -f "$CURRENT_CERT_DIR/$DOMAIN.key" "$DEST_DIR/privkey.pem" || error "复制私钥文件失败"
cp -f "$CURRENT_CERT_DIR/ca.cer" "$DEST_DIR/chain.pem" || error "复制CA证书失败"
# 6. 重启相关服务确保生效
info "重启服务使证书生效"
if ! nginx -s reload; then
error "Nginx重启失败,请手动检查"
fi
success "所有操作完成!证书已$( [ $CERT_EXISTS -eq 1 ] && echo "更新" || echo "安装" )并生效"复制上面的代码并修改 “example.com”“dns_ali”另存为 “update_synology_cert.sh”(文件名可以随便定义)
或者直接 下载 我的脚本修改 “example.com”“dns_ali”update_synology_cert.sh
然后上传至群晖个人home目录下(目录自己随意选择),记住并复制“位置”信息。

打开计划任务:控制面板→计划任务→新增→用户自定义的脚本
用户帐号一定选择 “root”,
然后任务设置→用户自定义的脚本输入:
cd /volume1/homes/sunfeng/ chmod +x update_synology_cert.sh ./update_synology_cert.sh

保存计划任务即可,以后就不用操心证书更新问题了
feng的博客
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。