在 Docker 中运行 acme.sh 为 Nginx 容器部署免费 SSL 证书
前言
acme.sh
是一个 Unix Shell 脚本,实现了 ACME 客户端协议,支持自动化签发和更新免费证书。它支持 ECDSA 证书、SAN 和通配符证书,易于使用,无需 root 权限。项目仓库中的各类说明已经较为全面,这里仅仅是针对我个人在 Docker 环境下使用时一些配置记录。
配置记录
安装 acme.sh
作为 Docker 爱好者,总是有点强迫症地想将一切操作都放在容器里隔离开来,不想在宿主机上处理,尽管只是一个单纯的 shell 脚本。如下为 docker compose 文件配置:
compose.yaml
services: acme-sh: image: neilpang/acme.sh container_name: acme.sh volumes: - ./acme-data:/acme.sh - /var/run/docker.sock:/var/run/docker.sock network_mode: host command: daemon stdin_open: true tty: true restart: no

签发证书
- Nginx 容器配置
首先要为 Nginx 容器增加一个 label:
sh.acme.autoload.domain=okhk.net
,其中 okhk.net 就是将要为之签发的域名,如下为 Nginx 容器 compose 文件修改后的内容:services: nginx: image: nginx:alpine container_name: nginx restart: always network_mode: host volumes: - ./nginx/nginx.conf:/etc/nginx/nginx.conf - ./nginx/ssl:/etc/nginx/ssl - ./nginx/conf:/etc/nginx/conf.d environment: TZ: Asia/Shanghai labels: - sh.acme.autoload.domain=okhk.net
- Cloudflare API Token
因为我的域名托管在 Cloudflare 上,要想通过 DNS 签发证书,需要先获取 Cloudflare 平台的 API Token。
不建议使用 Global API Key !!!
API Token是一个40个字符的字符串,可能包含大写字母、小写字母、数字和下划线。
在 Cloudflare Dashboard 个人资料页面的 API Tokens 部分,点击 Create Token

可以直接选用具有 DNS 编辑权限的模板


选中你想要签发证书的域名,还可以配置 IP 地址过滤增强安全性,各项配置无误后点击继续。

将这里生成的 API Token 保存下来(如未保存遗失后只能重新创建),后面会用到。
打开对应域名的概览页面,在右侧栏靠下方获取对应 Zone ID 以及 Account ID ,同样记录下来。

- 签发证书
执行如下命令
docker exec \ -e CF_Token=[替换为前面获取的 API Token ] \ -e CF_Account_ID=[ 替换为前面获取的 Account ID ] \ -e CF_Zone_ID=[ 替换为前面获取的 Zone ID ] \ acme.sh --issue -d okhk.net --dns dns_cf
报错如下:

注册账户执行命令:
docker exec acme.sh --register-account -m ssl@okhk.net --server zerossl
之后再重新执行👆上面的签发命令。
- 部署证书
执行命令:
docker exec \ -e DEPLOY_DOCKER_CONTAINER_LABEL=sh.acme.autoload.domain=okhk.net \ -e DEPLOY_DOCKER_CONTAINER_KEY_FILE=/etc/nginx/ssl/okhk.net/key.pem \ -e DEPLOY_DOCKER_CONTAINER_CERT_FILE="/etc/nginx/ssl/okhk.net/cert.pem" \ -e DEPLOY_DOCKER_CONTAINER_CA_FILE="/etc/nginx/ssl/okhk.net/ca.pem" \ -e DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE="/etc/nginx/ssl/okhk.net/full.pem" \ -e DEPLOY_DOCKER_CONTAINER_RELOAD_CMD="service nginx force-reload" \ acme.sh --deploy -d okhk.net --deploy-hook docker
最终提示 Success 即已经成功将证书安装到指定路径了,只需要 Nginx 的 conf 配置中路径对应上即可,可按需修改。
Nginx 的配置 ssl_certificate 使用 /etc/nginx/ssl/fullchain.cer ,而非 /etc/nginx/ssl/<domain>.cer ,否则 SSL Labs 的测试会报 Chain issues Incomplete 错误。
最终 acme.sh 的容器 compose 配置如下:
compose.yaml
services: acme-sh: image: neilpang/acme.sh container_name: acme.sh volumes: - ./acme-data:/acme.sh - /var/run/docker.sock:/var/run/docker.sock network_mode: host command: daemon stdin_open: true tty: true restart: no environment: - DEPLOY_DOCKER_CONTAINER_LABEL=sh.acme.autoload.domain=okhk.net - DEPLOY_DOCKER_CONTAINER_KEY_FILE=/etc/nginx/ssl/okhk.net/key.pem - DEPLOY_DOCKER_CONTAINER_CERT_FILE="/etc/nginx/ssl/okhk.net/cert.pem" - DEPLOY_DOCKER_CONTAINER_CA_FILE="/etc/nginx/ssl/okhk.net/ca.pem" - DEPLOY_DOCKER_CONTAINER_FULLCHAIN_FILE="/etc/nginx/ssl/okhk.net/full.pem" - DEPLOY_DOCKER_CONTAINER_RELOAD_CMD="service nginx force-reload"
配置完成后检测一下
