文本首次发布在我的博客:
https://blog.mcloc.cn/2023/09/21/20230921-记群晖使用acme-sh自动续签SSL证书/
(博客中文章配图,建议去原文食用)
始
群晖默认带有自签署的SSL证书,足够日常使用了。但是浏览器会提示不安全,使用cloudflare进行代理时也无法开启完全(严格)模式。
如果想要完全体的SSL加密,还是需要自己部署可信CA的证书。但是免费通配符证书的有效期通常很短,有效期长的免费证书又不能通配。即使是付费的通配符证书,有效期一般也只有一年。
难道部署SSL证书就像西西弗斯推巨石,永远无法到达山顶吗?
或许不是,本文将做一些探索,尝试爬上这座山。
原理
在讲理论上的实现原理前,我们先明确一下需求。
无需频繁维护实现以下3点:
- 1.直接访问用域名部署到群晖的服务处于HTTPS加密状态
- 2.使用cloudflare代理群晖本地的服务时可以开启完全(严格)模式
- 3.群晖的VPN Server(OpenVPN)可以稳定使用
接下来构想实现路径。
路径1:群晖部署cloudflare提供的适用于源服务器的15年长期证书。但是cloudflare并不在主流OS默认的可信CA列表中,浏览器会提示不安全。为了消除此警告,还需要将cloudflare的根证书手动添加到操作系统的可信证书里。此路径可以完美实现需求2和需求3,需求1部分完成,在其他没有将cloudflare的根证书添加到操作系统可信证书的客户端访问群晖的服务还是会提示不安全。如果你的群晖服务不直接暴露到公网,而是通过cloudflare对公网提供服务,可以采用此方案。
路径2:使用acme.sh自动续签&部署SSL证书。此方案无需客户端做任何操作即可实现HTTPS访问,可完美实现需求1和需求2。但是需求3无法达成,因为每次证书更新都会让群晖的OpenVPN配置文件被动更改,造成一键回家失效。
路径1和路径2都有缺点。但是只要同时使用两个路径,就能达成我们一开始的目标,做出现阶段可以做到的最优解。
路径确定:在群晖上部署cloudflare提供的15年长期证书,用于OpenVPN;同时使用acme.sh自动续签&部署SSL证书,用于实现HTTPS访问群晖服务和cloudflare加密回源。
实现
理论可行,开始动手。
网上有关于acme.sh自动续签&部署SSL证书的教程,故不再赘述,本文只指出主要步骤及关键易错点。如果你想要尝试,请务必搜索相关教程学习后再动手,不可只看文本。
首先从cookie中获取did,保存备用。此处需要注意,不是所有的did都可以用,我就在这里卡了好久,最后读文档才发现,只有在登陆群晖时勾选了记住密码,cookie中的did才有效。
然后拉取并创建容器 neilpang/acme.sh
。
确认容器状态正常后,编写脚本,并将脚本添加到群晖的计划任务。下方附有脚本,可直接复制后修改并保存为sh文件上传。
此处要注意,如果你的DNS服务商是cloudflare的话,CF_Key
需要复制cloudflare个人资料里API 令牌
的Global API Key
。如果你不想用这么大权限的key,也可以选择使用token,感兴趣的话可以自己研究(只需要将脚本中的CF_Key
和CF_Email
换成CF_Token
和CF_Account_ID
并通过-e命令传给容器就行了)。
#你的域名
DOMAIN=''
#证书供应商
CERT_SERVER='letsencrypt'
#DNS供应商 可选 dns_dp(腾讯云) dns_ali(阿里云) dns_cf 其他可查https://github.com/acmesh-official/acme.sh/wiki/dnsapi
DNS="dns_dp"
#群晖账号密码
SYNO_Username=''
SYNO_Password=''
#如果开启了双重验证请在浏览器登录时选中保存此设备,然后从COOKIE中获取did cookie
SYNO_Device_ID=''
#以下群晖配置非必要不要更改
SYNO_Hostname="localhost" # Specify if not using on localhost
SYNO_Scheme="http"
SYNO_Port="5000"
#要添加的证书的名字,空字符串("")的话就是替代默认证书,一般建议使用空字符串,除非你有多个证书
SYNO_Certificate=''
#以下三选一
#DNSPOD.CN 腾讯云
DP_Id=''
DP_Key=''
#阿里云
Ali_Key=''
Ali_Secret=''
#CF
CF_Key=''
CF_Email=''
case $DNS in
"dns_dp")
a="DP_Id=${DP_Id}"&&b="DP_Key=${DP_Key}"
;;
"dns_ali")
a="Ali_Key=${Ali_Key}"&&b="Ali_Secret=${Ali_Secret}"
;;
"dns_cf")
a="CF_Key=${CF_Key}"&&b="CF_Email=${CF_Email}"
;;
esac
c="SYNO_Username=${SYNO_Username}"
d="SYNO_Password=${SYNO_Password}"
# e="SYNO_TOTP_SECRET=${SYNO_TOTP_SECRET}" (已废弃,新的参数为SYNO_Device_ID)
e="SYNO_Device_ID=${SYNO_Device_ID}"
f="SYNO_Hostname=${SYNO_Hostname}"
g="SYNO_Scheme=${SYNO_Scheme}"
h="SYNO_Port=${SYNO_Port}"
i="SYNO_Certificate=${SYNO_Certificate}"
j="SYNO_DID=${SYNO_Device_ID}"
docker exec -e ${a} -e ${b} acme acme.sh --log --server "${CERT_SERVER}" --issue -d "${DOMAIN}" -d "*.${DOMAIN}" --dns "${DNS}"
docker exec -e ${c} -e ${d} -e ${e} -e ${f} -e ${g} -e ${h} -e ${i} -e ${j} acme acme.sh --issue -d "${DOMAIN}" -d "*.${DOMAIN}" --dns "${DNS}" --deploy --deploy-hook synology_dsm
完成这些后,你就已经完成了80%,剩下的就是一键回家的问题。
为了实现一键回家不会随着SSL证书的更新而失效,可以去cloudflare申请一个适用于源服务器的15年证书,并将其保存到群晖里(不要作为默认证书,默认证书是上面那个)。然后将此证书仅分配给VPN Server。
结
至此,构想完全实现。
感谢你的阅读,本人(blog.mcloc.cn)享有此文的著作权,转载请附原文地址。