群晖 NAS 公网访问配置(五):使用 certd 免费版配置泛域名证书
原本《群晖 NAS 公网访问配置》系列前四篇文章已经够用了,本系列中的 NAS 服务不会开放到公网上,https 本来是可有可无的。
但是,如果你和本站一样使用了 .dev 域,那么你肯定也会遇到和我一样的问题:只用 http 无法访问服务,浏览器会强制使用 HTTPS。
这个问题的背景是:
.dev是一个正式的通用顶级域(gTLD),由 Google(Charleston Road Registry / Google Registry)运营。
整个.dev被放到 HSTS preload 列表里,主流浏览器会把对.dev的请求当作必须走 HTTPS 的站点处理——如果没有有效 TLS,浏览器会拒绝/升级连接,导致无法通过http://yourname.dev正常打开。也就是说,使用.dev发布站点时必须配置有效的证书(Let’s Encrypt 等都可用)。
为了解决这个问题,本篇文章诞生了:本文将教会你如何通过 certd 这个开源项目申请泛域名证书,并通过流水线定时触发 + 脚本导入实现全自动更新,以便在 DSM 面板和 NAS 上的服务都能安全访问。
本文为《群晖 NAS 公网访问配置》系列文章的第五篇。全部文章请参考:
群晖 NAS 公网访问配置(一):配置 DDNS
群晖 NAS 公网访问配置(二):配置 WireGuard
群晖 NAS 公网访问配置(三):配置 Nginx 访问 Docker 服务
群晖 NAS 公网访问配置(四):配置云服务器
群晖 NAS 公网访问配置(五):使用 certd 免费版配置泛域名证书
方案概览
这套方案的核心思路很简单:
- 用
certd免费版通过 DNS-01 验证申请泛域名证书。 - 在流水线里把证书写入固定路径,再用脚本自动导入 DSM。
- 配置流水线定时触发,证书到期前自动续期并自动导入,实现全自动维护。
前置条件
- 你已按前四篇完成 DDNS、WireGuard、Nginx 等配置。
- 域名托管在腾讯云,并且能使用 API 密钥操作 DNS 解析。
- 群晖已安装 Container Manager(或 Docker 套件),可以运行容器。
- 先在套件中心安装 Python3 套件,脚本会用
python3(或python)解析 DSM 返回的数据。 - 准备一个主域名,支持
*.example.com泛域名证书。
在群晖部署 certd
certd 官方提供了 Docker 部署方式,群晖上直接用 Container Manager 即可。
- 打开 Container Manager,新增项目,选择
docker-compose.yaml。 - 建议使用下面的精简配置,并把数据目录改成你自己的卷路径。
version: "3.3"
services:
certd:
image: registry.cn-shenzhen.aliyuncs.com/handsfree/certd:latest
container_name: certd
restart: unless-stopped
volumes:
- /volume1/docker/certd:/app/data:delegated
ports:
- "7001:7001"
- "7002:7002"
environment:
- TZ=Asia/Shanghai
- certd_koa_hostname=0.0.0.0
- 部署完成后访问
http://群晖 IP:7001,使用默认账号admin/123456登录,并第一时间修改密码。
在 certd 中申请泛域名证书
1. 添加腾讯云授权
进入 certd 的授权管理,新增腾讯云授权,填写 SecretId 和 SecretKey。
这两个值可以复用你在 DDNS 配置时创建的密钥。
2. 创建证书申请流水线
进入证书流水线,新增 “证书申请(JS 版)”,按下面的方式填写:
- 域名验证方式:DNS 直接验证。
- 证书颁发机构:选择 “Let's Encrypt”。
- DNS 解析服务商:腾讯云。
- 证书域名:填写主域名和泛域名,例如
example.com与*.example.com。 - 更新天数:按需设置(默认值以界面为准),表示证书到期前多少天触发更新。
保存后不需要手动运行,后面设置定时触发后会自动执行。
如果你希望尽快拿到证书,可以把定时触发时间设置为当前时间后几分钟,让它自动跑首轮。
如果失败,多半是 DNS 生效时间不够。可以在流水线里把 “等待解析生效时长” 适当调大,再试一次。
将证书输出到固定路径
为了让脚本能拿到证书文件,需要把证书保存到 NAS 上一个固定目录。certd 的流水线里加一个“主机 - 复制到本机”任务即可。
- 打开对应流水线,新增任务 “主机 - 复制到本机”。
- 证书类型选择
pem。 - 路径建议这样填(相对路径会写到
/app/data下):- 全链证书保存路径:
tmp/fullchain.pem - 私钥保存路径:
tmp/private.key - 中间证书保存路径:
tmp/intermediate.pem
- 全链证书保存路径:
因为我在 docker-compose.yaml 里把 /volume1/docker/certd 映射到容器的 /app/data,所以上面三个文件最终会落在:
/volume1/docker/certd/tmp/fullchain.pem/volume1/docker/certd/tmp/private.key/volume1/docker/certd/tmp/intermediate.pem
你可以按自己的映射关系调整路径,但脚本里要用同一套路径。
用脚本导入证书到 DSM
(可选)为启用 2FA 的账户获取 device-id
如果你的 DSM 账户启用了两步验证(2FA),需要先获取一次 device-id,后续就可以用它来免 OTP 登录。
在你的本地电脑(或任何能访问 NAS 内网的设备)上执行以下命令,只需要执行一次(记得替换其中的参数):
curl -sS -k -G "https://192.168.1.10:5001/webapi/entry.cgi" \
--data-urlencode "api=SYNO.API.Auth" \
--data-urlencode "version=6" \
--data-urlencode "method=login" \
--data-urlencode "format=sid" \
--data-urlencode "account=admin" \
--data-urlencode "passwd=your-password" \
--data-urlencode "otp_code=123456" \
--data-urlencode "device_name=certd" \
--data-urlencode "enable_device_token=yes" \
--data-urlencode "enable_syno_token=yes" \
| grep -oP '"device_id"\s*:\s*"\K[^"]+'
参数说明:
https://192.168.1.10:5001:你的 NAS 内网地址和端口account:DSM 管理员账号passwd:DSM 管理员密码otp_code:从你的两步验证应用(如 Google Authenticator)获取的 6 位数字device_name:自定义设备名称,用于在 DSM 中标识该设备,建议填certd
命令会输出类似 aBcD1234eFgH5678 这样的字符串,这就是你的 device-id,记录下来供后续脚本使用。
如果命令执行失败或没有输出,可以检查:
- OTP 码是否正确且未过期
- 网络是否能访问到 NAS
- 账号密码是否正确
准备导入脚本
首次使用前不需要手动准备证书记录。执行脚本时传入 --cert-desc,并加上 --create,脚本会在 DSM 里找不到同名描述时自动创建证书记录并导入证书。
如果你需要手动排查或验证,也可以在 DSM 里走 “新增 → 新增新证书 → 导入证书”,导入一次后再交给脚本更新。
DSM 自带的
Let's Encrypt需要公网 80 端口可达,且通常不支持DNS-01与泛域名,不适合本方案。
把下面脚本保存到 NAS 上,例如 /volume1/docker/certd/scripts/synology-import-cert.sh,无需修改脚本内容,所有配置都通过参数传入。
该脚本同步保存在 Github 上:synology-import-cert.sh
脚本保存到本地后赋予其可执行权限:
chmod +x /volume1/docker/certd/scripts/synology-import-cert.sh
让流水线自动执行脚本
要做到全自动,需要让 certd 在证书更新后自动执行脚本。做法是给流水线增加一个 “主机 - 执行远程主机脚本命令” 任务。
- 在
certd的 “授权管理” 里新增 SSH 授权,主机填 NAS 地址,端口填你的 SSH 端口(可在 DSM 的 “控制面板 → 终端机和 SNMP → 终端机” 中查看,默认为22),账号使用管理员账户。
如果你之前没开 SSH,可以在 DSM 的 “控制面板 → 终端机和 SNMP → 终端机” 里开启。 - 回到证书流水线,在 “主机 - 复制到本机” 之后新增 “主机 - 执行远程主机脚本命令” 任务。
- 选择刚才的 SSH 授权,脚本命令填写下面的内容:
如果启用了 2FA,则在参数上带上之前获取到的
device-id
bash /volume1/docker/certd/scripts/synology-import-cert.sh \
--base-url "https://192.168.1.10:5001" \
--username "admin" \
--password "your-password" \
--cert-desc "nas-wildcard" \
--fullchain "/volume1/docker/certd/tmp/fullchain.pem" \
--key "/volume1/docker/certd/tmp/private.key" \
--device-id "your-device-id" \
--set-default \
--assign-services "all" \
--insecure \
--create
参数说明:
- 如果 DSM 证书尚未可信或域名不匹配,请保留
--insecure参数。携带该参数后将跳过 HTTPS 证书校验,避免首次接入时因自签证书或域名不匹配而登录失败。 - 如果你希望一次性覆盖全部服务,保留
--assign-services all;也可以指定具体服务名称,例如"DSM Desktop Service,Reverse Proxy"。
配置完成后,可以手动运行一次流水线测试。如果一切正常,证书会自动导入到 DSM。这样流水线每次拿到新证书后都会自动更新 DSM。
首次运行成功后,建议优化脚本配置:将 --base-url 改为你的域名,并去掉 --insecure 参数,这样可以正常校验 HTTPS 证书,更加安全:
bash /volume1/docker/certd/scripts/synology-import-cert.sh \
--base-url "https://nas.example.com:5001" \
--username "admin" \
--password "your-password" \
--cert-desc "nas-wildcard" \
--fullchain "/volume1/docker/certd/tmp/fullchain.pem" \
--key "/volume1/docker/certd/tmp/private.key" \
--device-id "your-device-id" \
--set-default \
--assign-services "all" \
--create
配置 Nginx 使用 SSL 证书
现在证书已经能通过 certd 自动管理了,接下来需要让 Nginx 使用这些证书来支持 HTTPS 访问。本文将基于 DSM 自带的 Nginx(参考第三篇文章)。
修改 Nginx 配置
DSM 自带的 Nginx 配置文件在 /etc/nginx/sites-enabled 目录下。
假设你在第三篇文章中为 calibre 配置了 calibre.example.com.conf,现在需要修改它以支持 HTTPS。
使用 SSH 连接到 NAS,编辑对应的配置文件:
sudo vim /etc/nginx/sites-enabled/calibre.example.com.conf
将原有的配置改为:
# HTTP 自动跳转到 HTTPS
server {
listen 80;
server_name calibre.example.com;
return 301 https://$server_name$request_uri;
}
# HTTPS 配置
server {
listen 443 ssl;
server_name calibre.example.com;
# SSL 证书配置
ssl_certificate /volume1/docker/certd/tmp/fullchain.pem;
ssl_certificate_key /volume1/docker/certd/tmp/private.key;
# SSL 协议与加密套件
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_redirect off;
proxy_pass http://172.17.0.1:8083;
client_max_body_size 2000m;
}
}
关键修改点:
- 新增 80 端口的 server 块,自动跳转到 HTTPS
- 原有的 server 块改为监听 443 端口,并启用 SSL
- 使用 certd 导出的证书路径:
/volume1/docker/certd/tmp/fullchain.pem和/volume1/docker/certd/tmp/private.key - 新增
X-Forwarded-Protoheader,让后端应用知道使用的是 HTTPS
重启 Nginx
保存配置文件后,测试配置并重启 Nginx 使配置生效:
sudo /usr/bin/nginx -c /etc/nginx/nginx.conf.run -t
sudo /usr/bin/nginx -c /etc/nginx/nginx.conf.run -s reload
验证 HTTPS 是否生效
用浏览器访问 https://calibre.example.com,检查:
- 浏览器地址栏显示小锁图标。
- 证书信息显示正确的域名和颁发机构。
- 页面能正常加载,没有证书警告。
如果有问题,可以查看 Nginx 日志:
sudo tail -f /var/log/nginx/error.log
常见问题:
- 证书路径不对:确认
/volume1/docker/certd/tmp/下有fullchain.pem和private.key文件。 - 权限问题:确保 Nginx 能读取证书文件,可以执行
sudo chmod 644 /volume1/docker/certd/tmp/*.pem /volume1/docker/certd/tmp/*.key。 - 证书未更新:certd 更新证书后,Nginx 需要重启才能重新加载证书。你可以在流水线的 “主机 - 执行远程主机脚本命令” 任务中,在导入脚本后面加一个重启命令:
bash /volume1/docker/certd/scripts/synology-import-cert.sh \
--base-url "https://nas.example.com:5001" \
--username "admin" \
--password "your-password" \
--cert-desc "nas-wildcard" \
--fullchain "/volume1/docker/certd/tmp/fullchain.pem" \
--key "/volume1/docker/certd/tmp/private.key" \
--device-id "your-device-id" \
--set-default \
--assign-services "all" \
--create
# 重启 Nginx 以重新加载证书
sudo /usr/bin/nginx -c /etc/nginx/nginx.conf.run -s reload
续期与维护
在创建 certd 流水线时,默认会有包含一个定时触发器,Let's Encrypt 证书有效期是 90 天。要实现全自动,只需要在流水线里配置定时触发即可。
certd 会在证书到期前按照 “更新天数” 设定的天数自动申请新证书并继续执行后续部署任务,没到期前不会重复申请。