叁·分

不求甚解

用户工具

站点工具


tech-notes:running-acme.sh-on-synology

在 Synology 上运行 acme.sh

Synology DSM1) 本身提供了自动 Let's Encrypt 证书的功能。但其实现不完整,仅支持 http-01 验证方式,该方式需要 Synology 的 80/443 端口可以通过公网访问。本文中将利用 acme.sh docker 镜像(image)使用 DNS-01 协议 验证并签发证书,并利用 DSM 中的“任务计划”自动更新证书并配置到 DSM 的相关应用中,以解决家用 Synology 无公网 80/443 端口,无法签发 Let's Encrypt的问题。

安装 Docker 并下载 acme.sh 镜像

首先,需要确认你的 Synology 可以运行 Docker,你可以根据官方信息判断。

之后在 DMS 中运行 Docker,在“注册表”中搜索“acme.sh”,双击并下载 neilpang/acme.sh 的 latest 镜像。

<imgcaption image1|下载neilpang/acme.sh镜像>pull-acmesh-docker-image.jpg</imgcaption>

创建 acme.sh 容器

neilpang/acme.sh 镜像下载好后,切换到“容器”,并双击 neilpang/acme.sh 镜像,准备创建容器(container)。

<imgcaption image2|创建 acme.sh 容器>create-acmesh-docker-container.jpg</imgcaption>

点击“高级设置”,并完成对容器的配置:

  • 勾选“启动自动重新启动”,这样当 DSM 重启后或容器出错退出后,容器可以重新自动启动并保持运行;<imgcaption image3|容器配置:勾选“启动自动重新启动”>acmesh-docker-container-always-restart.jpg</imgcaption>
  • 挂载 DSM 文件夹至容器中,以便 acme.sh 签发的证书后续使用;<imgcaption image4|容器配置:挂载 DSM 文件夹>acmesh-docker-container-vol.jpg</imgcaption>
  • 在最下方命令处填入 daemon,这样,acme.sh 便会在容器中定时运行,自动签发将到期证书;<imgcaption image5|容器配置:定时运行>acmesh-docker-container-entry.jpg</imgcaption>
  • 其他使用方法可以参考 neilpang/acme.sh 镜像页面。

至此,acme.sh 容器已经搭建完毕,我们将继续进行证书签发。

证书签发

Synology Docker 套件支持在 DSM 中对容器执行操作,因此本节中我们通过 DSM Docker 套件的“终端机”功能签发证书。

容器中执行 sh

为了签发证书,我们需要可以在容器中执行命令,为此我们在容器中启动 shell。

双击运行中的 acme.sh 容器,切换到“终端机”选项卡,点击“新增”旁的🔻,并点击“通过命令启动”。

在弹出的框中,根据提示,输入

/bin/sh

<imgcaption image6|执行 sh>acmesh-docker-container-sh.jpg</imgcaption>

至此,我们在容器中运行了 shell。

DNS 验证与签发

由于本教程中的 Synology 的 80 端口无法通过公网访问,因此无法通过 http 验证。签发过程中利用了 DNS 验证。

根据不同的域名托管商,执行的命令略有不同,但大体上先要从域名托管商那里获得 API 密钥,然后通过 export 命令将 API 密钥导至 shell 运行环境,最后运行 acme.sh 的签发命令。

<imgcaption image7|签发证书>acmesh-docker-container-issue-cert.jpg</imgcaption>

实践中,建议为 Synology 签发野证书,这样可以在“应用程序门户”中使用多种子域。

acme.sh --issue --dns dns_cf -d *.example.com

在终端机中,可以通过 Ctrl A + Ctrl C 粘贴剪切板中的命令至终端机,其他热键可以在终端机右上角的“热键”查阅。 syno-docker-hotkey.jpg

将签发的证书应用于 Synology

至此,我们应该可以在 /volume1/docker/acme.sh/*.example.com 中得到相应的证书和密钥,可以进一步导入到 DSM 或用于其他用途。

导入到 DSM

DSM 的证书一般存放在 /usr/syno/etc/certificate/_archive/AsDFgH (AsDFgH 这几个字母可能有差别,需要留意)。因此,我们可以选择性的替换证书为 acme.sh 容器签发的证书。

打开“控制面板”中的“任务计划”,新建“用户定义的脚本”

<imgcaption image8|“任务计划”配置:>syno-create-cronjob.jpg</imgcaption>

在“常规”选项卡中,确认用户账号为 root

<imgcaption image9|“任务计划”配置:确认用户账号为 root>syno-cron-general.jpg</imgcaption>

在“计划”选项卡中,确认将 acme.sh 签发的证书导入到 DSM 的频率,建议可以设置为每日运行。由于下步中的脚本将自动检查证书是否有更新,因此不会频繁重启 DSM 网页服务器。

<imgcaption image10|“任务计划”配置:脚本运行频率>syno-cron-schedule.jpg</imgcaption>

在“任务设置”选项卡中,贴入下述脚本2)

# Note: The $CERT_FOLDER must be hardcoded here since the running environment is unknown. Don't blindly copy&paste!
# if you used the normal method the certificate will be installed in the system/default directory
#CERTDIR="system/default"
# if you used the alternative method it is copied to an unknown path, change the following example to the output of the creation process and uncomment. 
CERTDIR="_archive/UpWnzk"
 
# do not change anything beyond this line!
CERTROOTDIR="/usr/syno/etc/certificate"
PACKAGECERTROOTDIR="/usr/local/etc/certificate"
FULLCERTDIR="$CERTROOTDIR/$CERTDIR"
 
# Check if a new cert has been issued. If "yes", old certs will be updated and corresponding Packages will be restarted.
is_cert_new=`cmp --silent "/volume1/docker/acme.sh/*.example.com/*.example.com.cer" "$FULLCERTDIR/cert.pem" && echo "no" || echo "yes"`
if [ $is_cert_new = "yes" ]
then
 
    # sync the new cert to syno cert loc
    rsync -avh "/volume1/docker/acme.sh/*.example.com/*.example.com.cer" "$FULLCERTDIR/cert.pem"
    rsync -avh "/volume1/docker/acme.sh/*.example.com/*.example.com.key" "$FULLCERTDIR/privkey.pem"
    rsync -avh "/volume1/docker/acme.sh/*.example.com/fullchain.cer" "$FULLCERTDIR/fullchain.pem"
    rsync -avh "/volume1/docker/acme.sh/*.example.com/ca.cer" "$FULLCERTDIR/chain.pem"
 
    # Issue cert for Plex, the cert has a password of "plex" and will be put in Plex Media Library
    openssl pkcs12 -export -out "/volume1/docker/acme.sh/*.example.com/*.example.com.pfx" -inkey "/volume1/docker/acme.sh/*.example.com/*.example.com.key" -in "/volume1/docker/acme.sh/*.example.com/*.example.com.cer" -certfile "/volume1/docker/acme.sh/*.example.com/fullchain.cer" -passout pass:plex
    cp "/volume1/docker/acme.sh/*.example.com/*.example.com.pfx" /volume1/Plex/Library/Application\ Support/Plex\ Media\ Server/plex.example.com.pfx
    chown plex:users /volume1/Plex/Library/Application\ Support/Plex\ Media\ Server/plex.example.com.pfx
 
    # # find all subdirectories containing cert.pem files
    PEMFILES=$(find $CERTROOTDIR -name cert.pem)
    if [ ! -z "$PEMFILES" ]; then
            for DIR in $PEMFILES; do
                    # replace the certificates, but never the ones in the _archive folders as those are all the unique
                    # certificates on the system.
                    if [[ $DIR != *"/_archive/"* ]]; then
                            rsync -avh "$FULLCERTDIR/" "$(dirname $DIR)/"
                    fi
            done
    fi
 
    # reload the DSM web server
    /usr/syno/sbin/synoservicectl --reload nginx
 
    echo 'SUCCESS: Certs are updated'
    exit 1
 
else
    echo "No new cert issued."
    exit 1
fi

应用程序门户

笔者在 Synology 中搭建了其他服务,如 Radarr,Sonarr,Ombi等,因此用 Synology 的“应用程序门户”中的“反向代理服务器”功能为上述服务开通了子域3),通过在“控制面板-安全性-证书”中配置子域名使用签发的证书,浏览器地址栏上再也不是红色的小锁了。

<imgcaption image11|导入 Let's Encrypt 后的结果>syno-certs.jpg</imgcaption>

<imgcaption image12|让 DSM 使用 Let's Encrypt 证书>syno-certs-conf.jpg</imgcaption>

<imgcaption image13|DSM 中配置的应用门户>dsm-appgw.jpg</imgcaption>

多数情况下,仅建议为 DSM 网页服务器使用 acme.sh 签发的证书,Drive、Hyperbackup 等套件继续使用原自签证书。因为 Let's Encrypt 的证书至迟 3 个月过期,换发证书后,Drive、Hyperbackup 等套件会停止同步并提示证书变动,需要重新认证,比较麻烦。因此建议 Drive、Hyperbackup 等套件使用自签证书即可。

将 acme.sh 安装在 Synology 本地

你也可以选择不使用 Docker 而将 acme.sh 安装在 Synology 本地,你只需将 git 目录完整拷贝到目标目录,通过 /volume1/homes/admin/.acme.sh/acme.sh –issue –dns dns_cf -d *.example.com –home /volume1/homes/admin/.acme.sh4) 签发证书,然后在“任务计划”中使用下述脚本自动更新证书。

# Note: The $CERT_FOLDER must be hardcoded here since the running environment is unknown. Don't blindly copy&paste!
# if you used the normal method the certificate will be installed in the system/default directory
#CERTDIR="system/default"
# if you used the alternative method it is copied to an unknown path, change the following example to the output of the creation process and uncomment. 
CERTDIR="_archive/UpWnzk"
 
# do not change anything beyond this line!
CERTROOTDIR="/usr/syno/etc/certificate"
PACKAGECERTROOTDIR="/usr/local/etc/certificate"
FULLCERTDIR="$CERTROOTDIR/$CERTDIR"
# renew certificates if acme.sh is installed locally, this used to be explained as a custom cronjob but works just as well within this script according to the output of the task. 
/volume1/homes/admin/.acme.sh/acme.sh --cron --force --home /volume1/homes/admin/.acme.sh
 
# sync the new cert to syno cert loc
rsync -avh "/volume1/docker/acme.sh/*.example.com/*.example.com.cer" "$FULLCERTDIR/cert.pem"
rsync -avh "/volume1/docker/acme.sh/*.example.com/*.example.com.key" "$FULLCERTDIR/privkey.pem"
rsync -avh "/volume1/docker/acme.sh/*.example.com/fullchain.cer" "$FULLCERTDIR/fullchain.pem"
rsync -avh "/volume1/docker/acme.sh/*.example.com/ca.cer" "$FULLCERTDIR/chain.pem"
 
# # find all subdirectories containing cert.pem files
PEMFILES=$(find $CERTROOTDIR -name cert.pem)
if [ ! -z "$PEMFILES" ]; then
        for DIR in $PEMFILES; do
                # replace the certificates, but never the ones in the _archive folders as those are all the unique
                # certificates on the system.
                if [[ $DIR != *"/_archive/"* ]]; then
                        rsync -avh "$FULLCERTDIR/" "$(dirname $DIR)/"
                fi
        done
fi
 
# # update and restart all installed packages
PEMFILES=$(find $PACKAGECERTROOTDIR -name cert.pem)
if [ ! -z "$PEMFILES" ]; then
	for DIR in $PEMFILES; do
              #active directory has it's own certificate so we do not update that package
              if [[ $DIR != *"/ActiveDirectoryServer/"* ]]; then
		rsync -avh "$FULLCERTDIR/" "$(dirname $DIR)/"
		/usr/syno/bin/synopkg restart $(echo $DIR | awk -F/ '{print $6}')
              fi
	done
fi

结论

至此,我们为 Synology 补完了 Let's Encrypt 证书签发功能,并可以让 DSM 及部分应用拥有小绿锁。对于没有自由域名的用户,仍可以利用 Synology 提供的 DDNS 服务附带的 Let's Encrypt 证书,该证书是不依赖于开放 80 端口的。

3)
因 Synology 提供的“反向代理服务器”功能有限,不支持 Basic Auth 等功能,上述子域仅限于局域网访问
4)
/volume1/homes/admin/.acme.sh为安装目录
tech-notes/running-acme.sh-on-synology.txt · 最后更改: 2019/09/12 15:19 由 Librarian