使用 Let’s Encrypt 签发泛域名证书并用于开发环境和内部系统

2023-02-16, 星期四, 09:48

System Administration

核心思路就是利用 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 自动完成分发工作。