Files
acme-auto/方案.md
T
2026-07-18 20:09:26 +08:00

273 lines
5.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
主服务(CertCenter
负责 acme.sh 申请与续签、维护 version 文件、提供 HTTPS 下载接口,以及生成“拉取脚本”。
拉取脚本(deploy-cert.sh
部署到每台服务器,通过 cron/systemd timer 定时执行:检查版本 → 下载 → 比对 → 原子替换 → nginx -t → reload。
这样每台业务服务器 不需要安装 Python、FastAPI、acme.sh 或任何 Agent,只需要一个 Shell 脚本和 cron。
### 最终架构
CertCenter
acme.sh · AliDNS · version · HTTPS 下载
HTTPS 拉取
VPS-A
blog.example.com
cron → deploy-cert.sh → reload nginx
VPS-B
api.example.com
cron → deploy-cert.sh → reload nginx
VPS-C
git.example.com
cron → deploy-cert.sh → reload nginx
### CertCenter 目录
```
certcenter/
├── app.py # FastAPI
├── config.yaml
├── certs/
│ └── example.com/
│ ├── fullchain.pem
│ ├── private.key
│ ├── version
│ └── metadata.json
└── templates/
└── deploy-cert.sh.j2 # 脚本模板
```
### 主服务提供的接口
| 接口 | 用途 |
| -------------------------------- | ---------------- |
| GET /api/version/{domain} | 返回当前版本号 |
| GET /api/cert/{domain}/fullchain | 下载 fullchain.pem |
| GET /api/cert/{domain}/private | 下载 private.key |
| GET /api/script/{domain} | 动态生成部署脚本 |
所有接口都使用:
http
```
Authorization: Bearer <TOKEN>
```
### 版本文件
`/srv/certs/example.com/version`
```
20260718143000
```
续签成功后,CertCenter 只需要:
Bash
```
date -u +%Y%m%d%H%M%S > /srv/certs/example.com/version
```
客户端先请求这个小文件,只有版本变化时才下载证书。
### 生成的拉取脚本(核心)
下面就是 CertCenter 返回给服务器的脚本内容。你只需要把它保存为 `/usr/local/bin/deploy-cert.sh`
Edit
#!/usr/bin/env bash
set -euo pipefail
DOMAIN="example.com"
BASE_URL="[https://cert.example.com](https://cert.example.com)"
TOKEN="REPLACE_WITH_TOKEN"
CERT_DIR="/etc/nginx/ssl/example.com"
TMP_DIR="/tmp/cert-sync-${DOMAIN}"
VERSION_FILE="${CERT_DIR}/.version"
RELOAD_CMD="systemctl reload nginx"
mkdir -p "${CERT_DIR}" "${TMP_DIR}"
auth=(-H "Authorization: Bearer ${TOKEN}")
# 1. 获取远端版本
REMOTE_VERSION=$(curl -fsSL "${auth[@]}"
"${BASE_URL}/api/version/${DOMAIN}")
# 2. 获取本地版本
LOCAL_VERSION=""
if [[ -f "${VERSION_FILE}" ]]; then
LOCAL_VERSION=$(cat "${VERSION_FILE}")
fi
# 3. 版本一致则退出
if [[ "${REMOTE_VERSION}" == "${LOCAL_VERSION}" ]]; then
exit 0
fi
# 4. 下载新证书
curl -fsSL "${auth[@]}"
"${BASE_URL}/api/cert/${DOMAIN}/fullchain"
-o "${TMP_DIR}/fullchain.pem"
curl -fsSL "${auth[@]}"
"${BASE_URL}/api/cert/${DOMAIN}/private"
-o "${TMP_DIR}/private.key"
chmod 600 "${TMP_DIR}/private.key"
# 5. 备份旧证书
cp -f "${CERT_DIR}/fullchain.pem" "${CERT_DIR}/fullchain.pem.bak" 2>/dev/null || true
cp -f "${CERT_DIR}/private.key" "${CERT_DIR}/private.key.bak" 2>/dev/null || true
# 6. 原子替换
mv "${TMP_DIR}/fullchain.pem" "${CERT_DIR}/fullchain.pem"
mv "${TMP_DIR}/private.key" "${CERT_DIR}/private.key"
# 7. 校验 Nginx 配置
if nginx -t; then
echo "${REMOTE_VERSION}" > "${VERSION_FILE}"
${RELOAD_CMD}
echo "[$(date -Is)] certificate updated: ${REMOTE_VERSION}"
else
echo "Nginx config test failed, rollback" >&2
mv -f "${CERT_DIR}/fullchain.pem.bak" "${CERT_DIR}/fullchain.pem" 2>/dev/null || true
mv -f "${CERT_DIR}/private.key.bak" "${CERT_DIR}/private.key" 2>/dev/null || true
exit 1
fi
脚本已经包含:
* 版本检查(避免重复下载)
* 下载证书
* SHA/内容变化隐式通过版本号控制
* 原子替换(`mv`
* `nginx -t` 校验
* 失败自动回滚
* 成功后 `systemctl reload nginx`
### CertCenter 的“生成脚本”接口
FastAPI 示例(核心逻辑):
Python
Run
```
from fastapi import FastAPI, HTTPException
from fastapi.responses import PlainTextResponse
from jinja2 import Environment, FileSystemLoader
import secrets
app = FastAPI()
env = Environment(loader=FileSystemLoader("templates"))
TOKENS = {
"server-a": "tok_xxx",
"server-b": "tok_yyy",
}
@app.get("/api/script/{domain}", response_class=PlainTextResponse)
def generate_script(domain: str, server: str):
token = TOKENS.get(server)
if not token:
raise HTTPException(404, "unknown server")
tpl = env.get_template("deploy-cert.sh.j2")
return tpl.render(
domain=domain,
base_url="https://cert.example.com",
token=token,
cert_dir=f"/etc/nginx/ssl/{domain}",
)
```
### 业务服务器首次部署
只需要执行一次:
Bash
```
curl -fsSL \
"https://cert.example.com/api/script/example.com?server=server-a" \
-o /usr/local/bin/deploy-cert.sh
chmod +x /usr/local/bin/deploy-cert.sh
```
然后添加 cron
cron
```
*/30 * * * * /usr/local/bin/deploy-cert.sh >> /var/log/deploy-cert.log 2>&1
```
### 主服务续签流程
Bash
```
# 每天凌晨执行
/root/.acme.sh/acme.sh --cron
# 如果证书更新,安装到统一目录
/root/.acme.sh/acme.sh --install-cert \
-d example.com \
--key-file /srv/certs/example.com/private.key \
--fullchain-file /srv/certs/example.com/fullchain.pem
# 更新版本号
date -u +%Y%m%d%H%M%S > /srv/certs/example.com/version
```
### 为什么这个方案最适合你
极轻量
业务服务器只需要 Bash + curl + cron,没有常驻进程。
安全
AliDNS AccessKey 只保存在 CertCenter,业务服务器只拿下载 Token。
自动化
续签后客户端自动感知版本变化并更新证书。
易扩展
新增服务器只需执行一次 curl 下载脚本并添加 cron。