配置 DNSPod 自动获取泛域名证书

最近升级了下家里的 LB,通过 gitlab 将 LB 的配置做了版本管理,并且通过 docker-compose 实现 LB 的快速部署。但是家里的网络做了内外网的区分,为了实现内网 https 访问,我需要在内网的 LB 上配置一套 SSL 证书(公网的部分直接在 CDN 上配置了 let's encrypt 的免费证书,一年一换)

为了避免麻烦,对于内网的 https 证书希望做到以下两点:

  • 到期自动续
  • 泛域名

查找了一番,有两个工具比较符合:Certbotacme.sh,都是通过 ACME protocol 去自动获取免费证书,但是需要自动获取泛域名证书的话,还需要能够自动在 DNS Provider 处更新 DNS TXT Record。由于我的域名是在 DNSPod 购买的,因此需要能够支持在 DNSPod 上自动更新 TXT 记录。Certbot 没有对应的官方插件,但是有第三方好心人写的插件能够实现该功能,如 certbot-dns-dnspod;而 acme.sh 是国人写的工具,官方支持 Aliyun 和 DNSPod,思来想去,还是打算使用 acme.sh。

Docker Compose Configuration

根据 官方文档 先将 Docker Compose 配置好,从文档里可以看出,acme.sh 容器会以 daemon 的形式一直运行,后续的申请证书、部署、续签等操作都需要 docker compose exec 单独去操作。Dockers Compose 配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
version: "3.9"
services:
nginx:
restart: always
container_name: nginx
image: nginx:latest
ports:
- 80:80
- 443:443
labels:
- sh.acme.autoload.domain=${DOMAIN_NAME}
volumes:
- $PWD/conf.d:/etc/nginx/conf.d:ro
- $PWD/nginx.conf:/etc/nginx/nginx.conf:ro
- $PWD/log/:/var/log/nginx/:rw
environment:
- TZ=Asia/Shanghai

acme:
image: neilpang/acme.sh
container_name: acme.sh
command: daemon
volumes:
- $PWD/acmeout:/acme.sh
- /var/run/docker.sock:/var/run/docker.sock
dns:
- 8.8.8.8
environment:
- DEPLOY_DOCKER_CONTAINER_LABEL=sh.acme.autoload.domain=${DOMAIN_NAME}
- DEPLOY_DOCKER_CONTAINER_KEY_FILE=/etc/nginx/ssl/key.pem
- DEPLOY_DOCKER_CONTAINER_CERT_FILE=/etc/nginx/ssl/cert.pem
- DEPLOY_DOCKER_CONTAINER_CA_FILE=/etc/nginx/ssl/ca.pem
- DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE=/etc/nginx/ssl/full.pem
- DEPLOY_DOCKER_CONTAINER_RELOAD_CMD="service nginx force-reload"
- DP_Id=${DP_Id}
- DP_Key=${DP_Key}

另外还需要添加一个 .env 文件用来存放如下变量:

  • ${DOMAIN_NAME}:用于申请证书的泛域名
  • ${DP_Id}:DNSPod API Key ID
  • ${DP_Key}:DNSPod API Key

申请证书

acme.sh 默认的 ssl 服务器是 ZeroSSL.com,根据 文档 里提到的,在申请证书之前,需要先注册账户

1
docker compose exec acme --register-account -m <your email address>

注册完成后就可以正式申请证书了

1
docker compose exec acme --issue --dns dns_dp -d ${DOMAIN_NAME}

等待证书申请完成后,可以看到 acmeout 目录下会出现一个 ${DOMAIN_NAME} 命名的目录,该目录下就是我们申请到的证书。接着运行命令将证书部署到 nginx 的目录下(该目录则是在 docker compose 中通过 environment 传参给 acme 容器的)

1
docker compose exec acme --deploy -d ${DOMAIN_NAME}  --deploy-hook docker

理论上到这里我们的证书申请和部署就已经结束了。

修锅

当然我在申请证书的时候,遇到了些问题,比如在等待证书签售的过程中,一直在报错

1
Order status is processing, lets sleep and retry

等待的超时时间是 15s,连续 retry 了多次之后肯定是有问题的,参考了 网络上其他的人建议,将默认的 CA Server 从 ZeroSSL.com 更换成了 Let's Encrypt

1
docker compose exec acme --set-default-ca --server letsencrypt

续签证书

acme.sh 不用我们手动去执行 renew 的命令来续签到期的证书,正如之前所说,acme 的容器是以 daemon 状态运行的,因此他会定时的去检测我们的证书到期时间,在到期之前会自动进行 renew 操作。可以在 acmeout/${DOMAIN_NAME}/${DOAMIN_NAME}.conf 中看到,有一项 Le_NextRenewTimeStr 配置,该配置了记录了 acme.sh 下一次续签证书的时间。