群晖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
保存计划任务即可,以后就不用操心证书更新问题了
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。