核心思路就是利用 DNS 手动模式申请泛域名证书,分发到局域网的服务器上,再在 DNS 中添加相应的解析记录指向这些局域网 IP。
因为手动模式通过在 DNS 中添加特定的 TXT 类型记录来验证域名归属,因此域名不需要有指向公网 IP 的解析。如果域名提供商能够提供相关的操作 API,则可由一些自动化工具完成验证。
Let’s Encrypt 推荐使用 certbot 做证书的自动续签工作,不过 acme.sh
对系统的侵入更少,完全够用了。
首先是安装:
curl https://get.acme.sh | sh -s email=my@example.com
服务器访问 raw.githubusercontent.com 可能会出现一些问题,gitee 镜像 不一定跟得上 GitHub 的原始版本,输出可能会有一些变化。因此推荐 clone code 到对应的服务器上手动安装:
git clone https://github.com/acmesh-official/acme.sh.git
cd ./acme.sh
./acme.sh --install -m <your@email.com>
安装脚本会在当前用户的 Home
目录下创建 .acme.sh/
目录,所有后续相关配置以及签发的证书都会存放在这个目录中。脚本还会创建更新证书的 crontab 任务。
$ crontab -l
58 0 * * * "/home/lighthouse/.acme.sh"/acme.sh --cron --home "/home/lighthouse/.acme.sh" > /dev/null
如果 DNS 提供了操作 API,可在服务商的控制台处获取 API 令牌,并使用 --dns
参数让 acme.sh 自动完成验证操作。本次使用的域名来自阿里云,需要通过 RAM 账号控制台 获取 AccessKey ID 和 AccessKey Secret。
export Ali_Key="LT**********R3"
export Ali_Secret="HT**********aL"
$HOME/.acme.sh/acme.sh --issue --dns dns_ali -d ddrpa.com -d '*.ddrpa.com'
如果是在腾讯云处购买的域名,则需要去 DNSPod 的管理控制台获取 DNSPod Token。
export DP_Id="4******5"
export DP_Key="HT**********aL"
~/.acme.sh/acme.sh --issue --dns dns_dp -d ddrpa.com -d '*.ddrpa.com'
如果需要在公共环境执行操作,担心 history 记录中留下密码等信息,可以通过脚本执行这些命令,之后直接删除脚本。密钥信息会在第一次执行时保存在 account.conf
文件中,也需要注意删除。
虽然本文题目叫做《使用 Let’s Encrypt ……》,acme.sh 默认使用的是 ZeroSSL 的服务,如果需要换回 Let’s Encrypt,使用 --server letsencrypt
参数。
申请成功后会在 $HOME/.acme.sh/ddrpa.com_ecc/
目录下生成证书相关文件:
- cert:
ddrpa.com.cer
- cert key:
ddrpa.com.key
。 - intermediate CA cert:
ca.cer
- full chain certs:
fullchain.cer
因为 .acme.sh/
目录的内部结构将来可能会有调整,不建议在 HTTP 服务器中直接引用这个位置的证书。推荐使用 --install-cert
命令拷贝/安装证书:
$HOME/.acme.sh/acme.sh --install-cert -d example.com \
--key-file /path/to/keyfile/in/nginx/key.pem \
--fullchain-file /path/to/fullchain/nginx/cert.pem \
--reloadcmd "service nginx force-reload"
不过我这里申请证书的机器和部署服务的机器不是同一台,就做手动分发了。
最后创建一个 NGINX 配置指向本地服务,由于是配给多个服务的泛域名证书,配置在 http
块中,否则也可以配置在 server
块中。
ssl_certificate "/home/yufan/Documents/ddrpa.com_ecc/fullchain.cer";
ssl_certificate_key "/home/yufan/Documents/ddrpa.com_ecc/ddrpa.com.key";
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH+kEECDH+AESGCM:HIGH+kEECDH:HIGH+kEDH:HIGH:!aNULL;
ssl_prefer_server_ciphers on;
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name redmine.ddrpa.com;
location / {
proxy_set_header Host $host;
proxy_set_header X-Scheme $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://localhost:3000;
}
}
替换证书后使用 systemctl reload nginx
重启 NGINX 使证书生效。
acme.sh 默认会申请 ZeroSSL 的证书,有效期 90 天,当前设置的 cronjob 会在 60 天左右更新证书。后续考虑使用 Fabric 自动完成分发工作。