acme.sh 用于生成免费的 ssl 证书,其实现了 acme 协议,由纯 Shell 脚本语言编写。 本文使用 docker 部署 acme.sh 。

1. 前言

群晖自带 Let’s Encrypt 证书申请功能,但是需要开放 80 或 443 端口来验证域名所属权,悲剧的是运营商禁掉了家庭网络相关的端口,这个方案并不可行。

后来通过其他云服务商的面版申请证书,通过群晖面板导入证书。这个方案的不便之处在于:

  • 证书有效期太短,有的服务商把证书的有效期从 1 年降为 3 个月
  • 申请的域名多了,操作起来麻烦,尤其是各个证书的续期时间还不一样

经过几番搜索之后找到了一劳永逸的方式:acme.sh

2. 获取 DNS API 调用密钥

根据域名 DNS 服务商的不同,获取密钥的方式也不同。

获取方式参考:https://github.com/acmesh-official/acme.sh/wiki/dnsapi

本文使用的是 Cloudflare,需要获取的参数:CF_TokenCF_Zone_ID

3. 确认 SSL 服务商

acme.sh 默认使用 ZeroSSL

切换其他服务商参考:https://github.com/acmesh-official/acme.sh/wiki/Server

4. 注册 ACME 账号

使用 sudo -i 切换到 root 用户,执行:

docker run --rm \
  -v /volume1/docker/acme_sh:/acme.sh \
  --net=host \
  neilpang/acme.sh:latest \
  --register-account \
  -m <YOUR_EMAIL> \
  --eab-kid <eab-kid> \
  --eab-hmac-key <eab-hmac-key>

其中: /volume1/docker/acme_sh 可以替换为你想要加载 acme 输出的本地目录,并确保文件夹已存在。

<YOUR_EMAIL> 应替换为你的邮箱。

可选参数:<eab-kid><eab-hmac-key> 要是你在 ZeroSSL 注册过账号的话,可以使用外部帐户绑定 (EAB) 凭据引导 acme.sh 。拥有 ZeroSSL 帐户的用户可以从开发者控制台管理颁发的证书。

参考:https://github.com/acmesh-official/acme.sh/wiki/ZeroSSL.com-CA

5. 签发证书

export CF_Token=<CF_Token>
export CF_Zone_ID=<CF_Zone_ID>
export DOMAIN=<DOMAIN>

docker run --rm \
  -v /volume1/docker/acme_sh:/acme.sh \
  -e CF_Token="${CF_Token}" \
  -e CF_Zone_ID="${CF_Zone_ID}" \
  --net=host \
  neilpang/acme.sh:latest \
  --issue --dns dns_cf --ocsp \
  --keylength ec-256 \
  -d "${DOMAIN}" -d "*.${DOMAIN}"

其中: <CF_Token><CF_Zone_ID> 是在 Cloudflare 申请的 DNS API 调用密钥。

<DOMAIN> 填需要申请证书的域名。

--keylength 指定证书的长度,可选值:2048, 3072, 4096, 8192 或者 ec-256, ec-384, ec-521。本文选择的是 ec-256

命令执行完后,证书会输出在 /volume1/docker/acme_sh/<DOMAIN> 目录下。

6. 部署证书

证书生成后,不建议手动复制和导入生成的证书。acme.sh 的 deploy 命令提供了群晖 NAS 的部署方式。

参考:https://github.com/acmesh-official/acme.sh/wiki/deployhooks

具体命令如下:

export SYNO_Username=<SYNO_Username>
export SYNO_Password=<SYNO_Password>
export DOMAIN=<DOMAIN>

docker run --rm \
  -v /volume1/docker/acme_sh:/acme.sh \
  -e SYNO_Username="${SYNO_Username}" \
  -e SYNO_Password="${SYNO_Password}" \
  -e SYNO_Certificate="<SYNO_Certificate>" \
  -e SYNO_Scheme="<SYNO_Scheme>" \
  -e SYNO_Port="<SYNO_Port>" \
  -e SYNO_Create="1" \
  --net=host \
  neilpang/acme.sh:latest \
  --deploy --ecc --insecure --deploy-hook synology_dsm \Í
  -d "${DOMAIN}" -d "*.${DOMAIN}"

其中: <SYNO_Username><SYNO_Password> 能登录群晖的用户名,密码(具有管理员权限)

<DOMAIN> 需要部署的域名证书

<SYNO_Certificate> 证书的描述,以后通过他替换已有证书

<SYNO_Scheme><SYNO_Port> 访问群晖的地址,比如你在局域网内是通过 https://192.168.1.5000 访问的,那么 SYNO_Scheme 填 http,SYNO_Port 填 5000

SYNO_Create 第一次导入证书时需要填此参数

--ecc 签发证书时 keylength 选的 ec-256,因此需要这个参数

--insecure 不需要校验服务端证书,部署证书是通过群晖的 API 实现的,比如:https://localhost:5000/webapi/entry.cgi?api=SYNO.Core.Certificate&method=import&version=1&SynoToken=

7. 自动续签

/volume1/docker/acme_sh/ 下创建自动续签的脚本auto_renew.sh

#!/bin/bash

export SYNO_Username=<SYNO_Username>
export SYNO_Password=<SYNO_Password>

export DOMAIN=<DOMAIN>

docker run --rm \
  -v /volume1/docker/acme_sh:/acme.sh \
  --net=host \
  neilpang/acme.sh:latest \
  --renew --force --dns dns_dp --ocsp \
  --keylength ec-256 --ecc \
  -d "${DOMAIN}" -d "*.${DOMAIN}"

再创建一个日志文件auto_renew.log

群晖打开控制面板-任务计划-新增。 常规理设置脚本名称,用户账号选项 root ,脚本需要该权限执行。

image.png

计划每月执行一次

image.png

任务设置,用户定义脚本输入:

bash /volume1/docker/acme_sh/auto_renew.sh >> /volume1/docker/acme_sh/auto_renew.log

image.png