Files
acme-auto/方案.md
T

273 lines
5.7 KiB
Markdown
Raw Normal View History

2026-07-18 20:09:26 +08:00
主服务(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。