主服务(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 ``` ### 版本文件 `/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。