Files
2026-07-18 20:09:26 +08:00

5.7 KiB
Raw Permalink Blame History

主服务(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" 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 ; 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。