在 docker 环境下使用 acme.sh 自动签发 ssl 范域名证书
acme.sh 是一个使用 shell 编写的ACME协议客户端,可以用于自动签发 ssl 证书并且自动更新证书。本文主要介绍在 docker 环境下使用 nginx 反向代理时,如何通过 acme.sh 自动签发和更新 ssl 证书供 nginx 使用。
首先,让我们整理一下在使用云服务器厂商免费的 ssl 证书时,是一个怎么样的步骤:
- 在云服务器厂商网页上申请免费的 ssl 证书,在签发 ssl 证书时还需要在云服务器的 dns 上添加记录,签发完成后再删除这条记录。
- 将签发完成的ssl证书文件下载下来并且上传到云服务上。
- 更新 ssl 证书文件重新加载 nginx。
上述流程上如果要自动化我们需要:
- 云服务器厂商提供的修改dns 的api
- 可以签发免费证书的 ca 机构
- 自动执行上述操作的工具
让我们开始行动:
获取云服务器厂商自动化修改 dns 的api 密钥
可以参考 acme.sh 文档介绍,选择 acme.sh 支持的云服务器厂商 Github
我这里以腾讯云的 dnspod 为例,其他云服务器厂商同理
登录 dnspod 控制台 https://dnspod.cn 生成一个 DNSPod Token,记录 ID 和 Token 后续会使用
选择一个 acme.sh 支持的 CA
还是参考 acme.sh 文档选择一个 ca Github
我这里以 ZeroSSL 为例
- 前往 ZeroSSL 官网注册一个账号 https://zerossl.com
- 创建完成后再账号页面的 Developer 页面创建一个 ACME客户端的EAB证书,记录 eab-kid 和 eab-hmac-key
在 docker 上使用 acme.sh 自动生成 ssl 证书并拷贝到 nginx 容器中
- 通过 docker compose 启动 nginx 和 acme.sh 容器
假设你需要签发证书的域名为 example.com
volumes:
# 创建一个命名卷用于持久化 nginx 数据
nginx-data:
driver: local
# 创建一个命名卷用于持久化 acme.sh 数据
acme-sh-data:
driver: local
services:
# nginx 容器
nginx:
image: nginx
# 为 nginx 容器打上一个标签,让 acme.sh 容器识别出
labels:
- sh.acme.autoload.domain=example.com
# acme.sh 容器
acme.sh:
image: neilpang/acme.sh
command: daemon
volumes:
# 将需要持久化的数据挂载到 acme-sh-data 上
- acme-sh-data:/acme.sh
# 让 acme.sh 容器可以访问外部 docker api
- /var/run/docker.sock:/var/run/docker.sock
environment:
# 识别 ngxin 容器
- DEPLOY_DOCKER_CONTAINER_LABEL=sh.acme.autoload.domain=example.com
# 配置 acme.sh 生成的 ssl 证书文件拷贝到 nginx 容器里的位置
- DEPLOY_DOCKER_CONTAINER_KEY_FILE=/etc/nginx/ssl/example.com/key.pem
- DEPLOY_DOCKER_CONTAINER_CERT_FILE="/etc/nginx/ssl/example.com/cert.pem"
- DEPLOY_DOCKER_CONTAINER_CA_FILE="/etc/nginx/ssl/example.com/ca.pem"
- DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE="/etc/nginx/ssl/example.com/full.pem"
# 当重新签发证书时重新刷新 nginx 配置的命令
- DEPLOY_DOCKER_CONTAINER_RELOAD_CMD="service nginx force-reload"
启动容器
docker compose up -d
- 配置ZeroSSL.com CA api
使用外部账号绑定引导 acme.sh,将 ${ZeroSSL-EAB-KID}
和${ZeroSSL-EAB-HMAC-KEY}
替换为你自己的值
docker exec acme.sh --register-account --server zerossl \
--eab-kid ${ZeroSSL-EAB-KID} \
--eab-hmac-key ${ZeroSSL-EAB-HMAC-KEY}
- 签发一个证书
docker exec \
-e DP_Id=${DNSPod-Id} \
-e DP_Key=${DNSPod-Token} \
acme.sh --issue -d example.com -d *.example.com --dns dns_dp
${DNSPod-Id}
和${DNSPod-Token}
填写在 DNSPod 获取的 id 和 token-d example.com -d *.example.com
生成的是范域名证书,即这个证书可以用于 example.com 的任何子命名(比如 test.example.com),如果只是生成单个域名的证书可以写-d example.com
,如果需要支持二级域名的范域名可以增加一个-d *.xxx.example.com
DP_Id, DP_Key 和 dns_dp 三个变量在不同的云服务器厂商下名字不同,请参考 acme.sh 文档填写
- 部署证书文件到 nginx 容器里
docker exec acme.sh --deploy -d example.com -d *.example.com --deploy-hook docker
执行完成相关的证书文件已经拷贝到了 nginx 容器里,并且重新加载了 nginx 配置。这个操作完成后 acme.sh 容器每隔 60 天就会重新签发并刷新 nginx 容器,不需要再手动执行。
- 如果需要手动强制签发,只需要执行:
docker exec acme.sh --renew -d example.com -d *.example.com --force
- 如果想停止后续的自动签发,只需要执行:
docker exec acme.sh --remove -d example.com -d *.example.com