UniFi Cloud Gateway — Let’s Encrypt 证书自动化手动安装教程
适用设备:UniFi Cloud Gateway Fiber / Ultra / UDM 系列
适用场景:国内网络无法访问 GitHub / Gitee,需要手动传输 acme.sh 安装包
验证环境:Debian 11 (bullseye) · aarch64 · UniFi OS 4.x
端口要求:无需开放 80 / 443,全程使用 DNS-01 验证
目录
- 前置准备
- 手动安装 acme.sh
- 切换 CA 并注册账号
- 配置 DNSPod API 凭据
- 签发 RSA-2048 证书
- 创建 UniFi 证书部署脚本
- 绑定 acme.sh 自动部署 Hook
- 配置 Cron 自动续签
- 配置开机自愈(on_boot.d)
- 验证证书是否生效
- 日常维护与故障排查
- 常见问题汇总
1. 前置准备
1.1 确认基础环境
SSH 登录设备后,先确认系统工具是否齐全:
1 | which curl openssl |
预期输出:
1 | /usr/bin/curl |
注意:UniFi Cloud Gateway 没有
java/keytool,不要尝试 keystore 方式,新版固件全部使用 PEM 格式证书,路径在/data/unifi-core/config/。
1.2 确认 UniFi 证书目录
1 | ls /data/unifi-core/config/*.crt |
预期看到以下文件:
1 | unifi-core.crt |
UUID 文件名无需手动记录,部署脚本会自动扫描。
1.3 确认持久化存储目录
1 | ls /data/ |
/data/ 目录在固件升级和重启后保持不变,所有脚本和配置均放在此处。
1.4 准备域名
需要一个由 DNSPod 管理的域名,例如 route.plnl.vip。
域名 A 记录是否指向当前 IP 不影响证书签发(DNS-01 验证只写 TXT 记录)。
2. 手动安装 acme.sh
2.1 在能访问 GitHub 的电脑上下载
在本地电脑操作(能访问 GitHub 的网络环境):
1 | # 方法一:浏览器直接下载 zip 包 |
2.2 传输到 UniFi 设备
在本地电脑执行(替换为设备实际 IP):
1 | # 上传 zip 包 |
2.3 在设备上解压并安装
回到 UniFi 设备 SSH 终端:
1 | cd /root |
2.4 验证安装成功
1 | ~/.acme.sh/acme.sh --version |
看到版本号(如 v3.x.x)即安装成功。
3. 切换 CA 并注册账号
关键步骤,不可跳过。
acme.sh 默认 CA 是 ZeroSSL,在国内网络下无法自动获取 EAB 凭据,会报Cannot resolve _eab_kid错误。必须手动切换到 Let’s Encrypt。
3.1 切换默认 CA
1 | ~/.acme.sh/acme.sh --set-default-ca --server letsencrypt |
成功输出:
1 | [...] Changed default CA to: https://acme-v02.api.letsencrypt.org/directory |
3.2 注册 Let’s Encrypt 账号
1 | ~/.acme.sh/acme.sh --register-account \ |
成功输出:
1 | [...] Registering account: https://acme-v02.api.letsencrypt.org/directory |
3.3 确认 CA 配置已写入
1 | grep "DEFAULT_ACME_SERVER" ~/.acme.sh/account.conf |
预期输出:
1 | DEFAULT_ACME_SERVER='https://acme-v02.api.letsencrypt.org/directory' |
如果之前曾安装过 acme.sh 且注册失败,执行以下命令彻底清理后重试:
1
2
3 rm -f ~/.acme.sh/account.conf
rm -rf ~/.acme.sh/ca/acme-v02.api.letsencrypt.org
# 然后重新执行 3.1 和 3.2
4. 配置 DNSPod API 凭据
4.1 获取 API 凭据
登录 DNSPod 控制台 → 用户中心 → API 密钥,获取 ID(纯数字)和 Token(字符串)。
安全建议:优先使用 DNSPod 域名级 API Token(仅授权该域名的 DNS 写入权限),而非账号级全局密钥。一旦路由器上的配置文件泄露,仅影响该域名,不会导致整个账号下所有域名失控。申请路径:DNSPod 控制台 → API 密钥 → 创建域名密钥。
4.2 写入配置文件
1 | mkdir -p /data/unifi-cert-manager |
4.3 加载凭据到当前 Shell
1 | . /data/unifi-cert-manager/config.conf |
5. 签发 RSA-2048 证书
必须使用 RSA-2048,严禁使用 ECC(
--keylength ec-256)。
UniFi Core 启动时会校验证书类型,遇到 ECC 证书会误判为损坏,在服务启动后约 2 秒内用自签名 RSA 证书将其静默覆盖,且不报任何错误。
1 | . /data/unifi-cert-manager/config.conf |
--server letsencrypt 显式指定 CA,防止 acme.sh 内部配置漂移回 ZeroSSL。
5.1 正常签发过程输出说明
1 | [...] Using CA: https://acme-v02.api.letsencrypt.org/directory ← CA 正确 |
5.2 签发失败常见原因
| 错误信息 | 原因 | 解决 |
|---|---|---|
Using CA: https://acme.zerossl.com |
CA 未切换 | 重新执行第 3 节 |
Cannot resolve _eab_kid |
CA 是 ZeroSSL | 重新执行第 3 节 |
invalidContact |
邮箱格式无效 | 清理 account.conf 重新注册(见 3.3) |
DNS record not found |
API 凭据错误或域名不在该账号 | 检查 DP_Id / DP_Key |
error code: 35 / error code: 28 |
DNS 查询出口不通,acme.sh 自动重试 | 通常等待即可自动通过 |
Rate limit exceeded |
同域名短期签发超过限制(每周 5 次) | 等待一周,或用 --staging 测试 |
6. 创建 UniFi 证书部署脚本
6.1 创建 SSL 目录
1 | mkdir -p /data/ssl |
6.2 写入部署脚本
1 | cat > /data/deploy-unifi-cert.sh << 'EOF' |
6.3 脚本关键设计说明
| 设计点 | 原因 |
|---|---|
| 顶部声明 PATH | cron/hook 环境 PATH 不完整,openssl、systemctl 可能找不到 |
| 证书完整性校验 | 损坏的 fullchain.pem 会导致 unifi-core 启动失败,黑屏 |
| RSA 格式校验 | ECC 证书被 UniFi 静默覆盖,且不报错,极难排查 |
| 动态扫描 UUID | 固件升级后 Console UUID 可能变化,硬编码会失效 |
| 只重启 unifi-core | 同时重启 nginx 抢占 443 端口,网页彻底崩溃 |
| 自动清理备份 | 每次部署都备份,不清理会长期积累占满 /data |
| 日志截断 | 长期追加日志,文件无限增长 |
| 不操作 keystore | 新版固件无 Java,keystore 由 Network Application 独立管理 |
7. 绑定 acme.sh 自动部署 Hook
1 | ~/.acme.sh/acme.sh --install-cert \ |
绑定后立即手动触发一次完整部署验证:
1 | /data/deploy-unifi-cert.sh |
查看部署日志:
1 | tail -20 /data/cert_deploy.log |
预期输出:
1 | 2026-08-06 18:06:26 ===== 开始更新证书 ===== |
8. 配置 Cron 自动续签
UniFi OS 重启后 crontab 可能被重置,需要手动确认并写入。
8.1 检查当前 cron 任务
1 | crontab -l |
8.2 写入 cron 任务
1 | crontab -e |
加入以下内容(保留已有的 DDNS 等任务):
1 | # acme.sh 自动续签 |
8.3 创建每小时健康检查脚本
1 | cat > /data/check-cert.sh << 'EOF' |
9. 配置开机自愈(on_boot.d)
UniFi OS 重启后 unifi-core 先于 cron 启动,会写入自签名证书,需要等 cron 下次触发才能修复(最长 1 小时)。通过 on_boot.d 可在开机后立即检查并修复(30 秒内完成)。
1 | cat > /data/on_boot.d/10-restore-cert.sh << 'EOF' |
10. 验证证书是否生效
10.1 检查本地证书文件
1 | openssl x509 -in /data/unifi-core/config/unifi-core.crt \ |
预期输出:
1 | issuer=C = US, O = Let's Encrypt, CN = YR2 |
10.2 检查 HTTPS 实际返回的证书
1 | echo | openssl s_client \ |
10.3 查看 acme.sh 管理的证书列表及到期时间
1 | ~/.acme.sh/acme.sh --list |
10.4 浏览器验证
用无痕模式访问 https://route.plnl.vip,地址栏显示绿色锁图标,点击后证书信息中可看到 Let's Encrypt 签发机构。
11. 日常维护与故障排查
手动续签(测试用)
1 | . /data/unifi-cert-manager/config.conf |
固件大版本升级后的恢复步骤
固件大版本升级可能同时重置证书和 cron,升级完成后执行以下两步即可完全恢复:
1 | # 步骤 1:重新安装 cron 续签任务 |
查看所有日志
1 | # 证书部署日志(含健康检查和开机自愈记录) |
更新 acme.sh(推荐每月执行)
DNSPod / 阿里云等 DNS API 接口会随厂商变动而调整,建议定期更新:
1 | # 有网络时直接升级 |
12. 常见问题汇总
Q1:签发成功但证书约 2 秒后又变回 unifi.local
原因:使用了 ECC 证书(--keylength ec-256)。UniFi Core 启动时误判 ECC 证书为损坏,静默覆盖为自签名 RSA 证书,全程不报错。
解决:
1 | # 删除 ECC 证书目录 |
Q2:重启 nginx 后网页完全无法访问
原因:443 端口由 unifi-core 独占,nginx 与 unifi-core 同时重启会产生端口抢占冲突,两个服务均无法正常绑定端口。
恢复:
1 | # 删除自定义证书,让 UniFi 用自签名证书自我修复 |
预防:部署脚本中永远不要加 systemctl restart nginx。
Q3:固件升级后证书又失效
原因:大版本固件升级可能重置 /data/unifi-core/config/ 下的证书文件。
解决:执行第 11 节「固件大版本升级后的恢复步骤」,两条命令搞定。
Q4:UUID 证书文件名变了
原因:固件重置或大版本升级后 Console UUID 可能改变,旧 UUID 文件被新 UUID 文件替换。
解决:部署脚本使用 find 动态扫描,直接重新执行即可:
1 | /data/deploy-unifi-cert.sh |
Q5:cron 续签任务重启后丢失
原因:UniFi OS 某些固件版本重启会重置 crontab。
解决:开机自愈脚本(第 9 节)会在开机后自动检查并重装;也可手动恢复:
1 | ~/.acme.sh/acme.sh --install-cronjob |
Q6:Cannot resolve _eab_kid
原因:acme.sh 默认 CA 是 ZeroSSL,国内网络无法获取其 EAB 凭据。
解决:重新执行第 3 节,切换到 Let’s Encrypt。
Q7:Account registration error: invalidContact
原因:安装 acme.sh 时邮箱为空或格式无效,account.conf 中存储了损坏的联系人信息。
解决:
1 | rm -f ~/.acme.sh/account.conf |
Q8:续签成功但 deploy 脚本没有被触发
原因:acme.sh hook 执行时 PATH 不完整,openssl 或 systemctl 找不到,脚本静默失败。
验证:
1 | # 手动模拟 hook 环境测试 |
预防:部署脚本顶部已声明 PATH(见第 6.2 节),此问题已规避。
附录:文件清单
/data/acme_cron.log
| 文件路径 | 用途 |
|---|---|
~/.acme.sh/ |
acme.sh 程序本体及证书数据 |
~/.acme.sh/account.conf |
CA 账号配置(含默认 CA、邮箱、密钥缓存) |
/data/unifi-cert-manager/config.conf |
DNS API 凭据及配置(权限 600,仅 root 可读) |
/data/ssl/fullchain.pem |
acme.sh 导出的完整证书链(部署来源) |
/data/ssl/key.pem |
acme.sh 导出的私钥(部署来源) |
/data/deploy-unifi-cert.sh |
证书部署脚本(替换证书、重启 unifi-core) |
/data/check-cert.sh |
每小时健康检查(被覆盖则自动修复) |
/data/on_boot.d/10-restore-cert.sh |
开机自愈脚本(30 秒后检查并修复) |
/data/cert_deploy.log |
证书部署、健康检查、开机自愈统一日志 |
| acme.sh cron 续签日志 | |
/data/unifi-core/config/unifi-core.crt |
UniFi Core 主证书(替换目标) |
/data/unifi-core/config/unifi-core-direct.crt |
UniFi 直连证书(替换目标) |
/data/unifi-core/config/<UUID>.crt |
Console UUID 证书(动态扫描替换) |