884 lines
22 KiB
Markdown
884 lines
22 KiB
Markdown
# CertCenter - 轻量化证书管理中心
|
||
|
||
一站式 SSL 证书管理平台,集成 ACME 证书申请、自动续签、多服务器分发,通过 Web UI 完成所有操作。
|
||
|
||
---
|
||
|
||
## 目录
|
||
|
||
- [架构概览](#架构概览)
|
||
- [环境要求](#环境要求)
|
||
- [安装部署](#安装部署)
|
||
- [WebUI 操作指南](#webui-操作指南)
|
||
- [仪表盘](#1-仪表盘)
|
||
- [服务器管理](#2-服务器管理)
|
||
- [域名管理](#3-域名管理)
|
||
- [ACME 配置](#4-acme-配置)
|
||
- [部署日志](#5-部署日志)
|
||
- [ACME 日志](#6-acme-日志)
|
||
- [测试验证](#测试验证)
|
||
- [环境检查](#1-环境检查)
|
||
- [端到端测试](#2-端到端测试)
|
||
- [客户端部署指南](#客户端部署指南)
|
||
- [Linux 服务器](#linux-服务器)
|
||
- [Windows 服务器](#windows-服务器)
|
||
- [API 参考](#api-参考)
|
||
- [客户端 API](#客户端-api)
|
||
- [管理 API](#管理-api)
|
||
- [配置说明](#配置说明)
|
||
- [常见问题](#常见问题)
|
||
|
||
---
|
||
|
||
## 架构概览
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────┐
|
||
│ CertCenter 服务 │
|
||
│ │
|
||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||
│ │ WebUI │ │ ACME 引擎 │ │ 数据库 │ │
|
||
│ │ (Vue) │ │ (certbot) │ │ (SQLite) │ │
|
||
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
|
||
│ │ │ │ │
|
||
│ └──────────────┴──────────────┘ │
|
||
│ │ │
|
||
│ FastAPI 后端服务 │
|
||
└──────────────────────┬──────────────────────────────────┘
|
||
│
|
||
┌────────────┼────────────┐
|
||
│ │ │
|
||
┌────▼────┐ ┌────▼────┐ ┌────▼────┐
|
||
│ VPS-A │ │ VPS-B │ │ VPS-C │
|
||
│ Linux │ │ Linux │ │ Windows │
|
||
│ cron │ │ cron │ │ schtasks│
|
||
└─────────┘ └─────────┘ └─────────┘
|
||
```
|
||
|
||
**核心流程:**
|
||
|
||
1. 在 WebUI 配置 ACME 凭据(阿里云 DNS / Cloudflare)
|
||
2. 添加服务器和域名信息
|
||
3. 点击"申请证书",CertCenter 自动完成 DNS 验证并签发证书
|
||
4. 业务服务器通过 cron 定时拉取最新证书
|
||
5. 证书到期前自动续签
|
||
|
||
---
|
||
|
||
## 环境要求
|
||
|
||
### 服务端(CertCenter)
|
||
|
||
| 依赖 | 版本 | 说明 |
|
||
|------|------|------|
|
||
| Python | 3.11 ~ 3.12 | ⚠️ 不支持 3.14(pydantic-core 无预编译 wheel) |
|
||
| certbot | 2.10+ | ACME 证书申请 |
|
||
| certbot-dns-alicloud | 最新 | 阿里云 DNS 插件(如使用阿里云) |
|
||
|
||
### 客户端(业务服务器)
|
||
|
||
| 依赖 | 说明 |
|
||
|------|------|
|
||
| bash / PowerShell | 执行部署脚本 |
|
||
| curl / Invoke-WebRequest | 下载证书 |
|
||
| cron / schtasks | 定时任务 |
|
||
|
||
---
|
||
|
||
## 安装部署
|
||
|
||
### 0. 确认 Python 版本
|
||
|
||
```bash
|
||
python --version
|
||
# 需要 3.11.x 或 3.12.x,不支持 3.14
|
||
```
|
||
|
||
如果版本不对,先装 Python 3.12:
|
||
- 下载地址:https://www.python.org/downloads/release/python-3128/
|
||
- 安装时勾选 "Add python.exe to PATH"
|
||
|
||
### 1. 克隆项目
|
||
|
||
```bash
|
||
git clone <repo-url> certcenter
|
||
cd certcenter
|
||
```
|
||
|
||
### 2. 创建虚拟环境并安装依赖
|
||
|
||
```bash
|
||
# 创建虚拟环境(指定 Python 3.12)
|
||
py -3.12 -m venv .venv
|
||
|
||
# 激活虚拟环境
|
||
.venv\Scripts\activate # Windows
|
||
# source .venv/bin/activate # Linux/Mac
|
||
|
||
# 安装依赖
|
||
pip install -r requirements.txt
|
||
```
|
||
|
||
如果使用阿里云 DNS,还需要安装 certbot 插件:
|
||
|
||
```bash
|
||
pip install certbot-dns-alicloud
|
||
```
|
||
|
||
### 3. 安装前端依赖并构建
|
||
|
||
```bash
|
||
cd frontend
|
||
npm install
|
||
npm run build
|
||
cd ..
|
||
```
|
||
|
||
构建完成后,`frontend/dist/` 目录包含前端静态文件,FastAPI 会自动加载。
|
||
|
||
### 4. 配置环境变量
|
||
|
||
```bash
|
||
cp .env .env
|
||
```
|
||
|
||
编辑 `.env` 文件:
|
||
|
||
```ini
|
||
# 数据库路径(SQLite)
|
||
DATABASE_URL=sqlite+aiosqlite:///./certcenter.db
|
||
|
||
# 证书存储目录
|
||
CERT_DIR=/srv/certs
|
||
|
||
# CertCenter 的访问地址(客户端通过此地址拉取证书)
|
||
BASE_URL=https://cert.example.com
|
||
```
|
||
|
||
### 5. 启动服务
|
||
|
||
```bash
|
||
python -m backend.main
|
||
```
|
||
|
||
服务默认监听 `http://0.0.0.0:8000`。
|
||
|
||
**首次启动时,系统会自动为 CertCenter 的域名生成一份自签证书**(存放在 `CERT_DIR` 下),确保 HTTPS 可用。后续正式签发证书后会自动替换。
|
||
|
||
### 6. 引导流程(首次使用)
|
||
|
||
首次使用时的推荐操作顺序:
|
||
|
||
```
|
||
1. 启动 CertCenter(自签证书自动生成)
|
||
python -m backend.main
|
||
|
||
2. 打开 WebUI,配置 ACME 凭据(阿里云 AK/SK)
|
||
|
||
3. 添加域名:cert.zhzp.top(CertCenter 自身的域名)
|
||
|
||
4. 点击"申请证书",为 CertCenter 签发正式证书
|
||
|
||
5. 重启 CertCenter,正式证书自动加载
|
||
# 此时所有客户端连接都是受信任的 HTTPS
|
||
```
|
||
|
||
**为什么需要这个流程?**
|
||
|
||
```
|
||
CertCenter 使用 HTTPS → 需要证书 → 证书由 CertCenter 自己签发
|
||
↑
|
||
这就是"引导"问题
|
||
```
|
||
|
||
解决方式:先用自签证书启动 → 签发正式证书 → 替换。客户端脚本已内置 `curl -k` 跳过 TLS 验证,不受影响。
|
||
|
||
### 7. 生产环境部署(可选)
|
||
|
||
使用 systemd 托管服务:
|
||
|
||
```bash
|
||
cat > /etc/systemd/system/certcenter.service << 'EOF'
|
||
[Unit]
|
||
Description=CertCenter Service
|
||
After=network.target
|
||
|
||
[Service]
|
||
Type=simple
|
||
User=root
|
||
WorkingDirectory=/opt/certcenter
|
||
ExecStart=/opt/certcenter/venv/bin/python -m backend.main
|
||
Restart=always
|
||
RestartSec=5
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
EOF
|
||
|
||
systemctl daemon-reload
|
||
systemctl enable --now certcenter
|
||
```
|
||
|
||
如果需要 HTTPS 访问 CertCenter 本身,可以使用 nginx 反向代理:
|
||
|
||
```nginx
|
||
server {
|
||
listen 443 ssl;
|
||
server_name cert.example.com;
|
||
|
||
ssl_certificate /etc/nginx/ssl/cert.example.com/fullchain.pem;
|
||
ssl_certificate_key /etc/nginx/ssl/cert.example.com/private.key;
|
||
|
||
location / {
|
||
proxy_pass http://127.0.0.1:8000;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 测试验证
|
||
|
||
部署完成后,建议按以下步骤验证系统是否正常工作。
|
||
|
||
### 1. 环境检查
|
||
|
||
运行部署验证脚本,检查所有依赖和配置:
|
||
|
||
```bash
|
||
python -m tests.test_setup
|
||
```
|
||
|
||
该脚本会检查:
|
||
|
||
| 检查项 | 说明 |
|
||
|--------|------|
|
||
| Python 依赖 | fastapi、sqlalchemy、acme、cryptography 等 |
|
||
| Certbot | certbot 命令是否可用 |
|
||
| DNS 插件 | certbot-dns-alicloud 或 certbot-dns-cloudflare |
|
||
| .env 配置 | 环境变量文件是否存在 |
|
||
| 数据库 | 数据库连接是否正常 |
|
||
| 前端构建 | frontend/dist/ 是否存在 |
|
||
| 证书目录 | 存储目录是否可写 |
|
||
| BASE_URL | 是否已修改默认值 |
|
||
| Let's Encrypt 连通性 | Staging 和 Production 环境是否可达 |
|
||
| FastAPI 服务 | 服务能否正常启动 |
|
||
|
||
所有检查通过后,进入下一步。
|
||
|
||
### 2. 端到端测试
|
||
|
||
使用 Let's Encrypt **Staging 环境**(测试环境)验证完整的证书签发流程。
|
||
|
||
**前提条件:**
|
||
|
||
- 一个使用阿里云 DNS 解析的域名
|
||
- 阿里云 RAM 子账号的 AccessKey(需有 `AliyunDNSFullAccess` 权限)
|
||
- CertCenter 服务已启动
|
||
|
||
**执行测试:**
|
||
|
||
```bash
|
||
python -m tests.test_e2e \
|
||
--domain test.example.com \
|
||
--email admin@example.com \
|
||
--ak YOUR_ACCESS_KEY \
|
||
--sk YOUR_ACCESS_SECRET
|
||
```
|
||
|
||
**测试流程:**
|
||
|
||
```
|
||
[0] 检查 API 服务是否正常
|
||
[1] 配置 ACME(Staging 环境 + 阿里云 DNS 凭据)
|
||
[2] 创建测试服务器
|
||
[3] 创建测试域名配置
|
||
[4] 申请证书(自动完成 DNS-01 验证)
|
||
[5] 检查证书信息(签发者、有效期、SAN)
|
||
[6] 测试版本接口
|
||
[7] 测试脚本生成
|
||
[8] 测试证书下载
|
||
```
|
||
|
||
**预期输出:**
|
||
|
||
```
|
||
==================================================
|
||
端到端测试: test.example.com
|
||
==================================================
|
||
|
||
✅ [0] API 服务正常
|
||
✅ [1] ACME 配置完成
|
||
✅ [2] 服务器 ID: 1
|
||
✅ [3] 域名 ID: 1
|
||
✅ [4] 证书申请成功: Certificate issued, expires: 2026-10-16 00:00:00
|
||
✅ [5] 签发者: CN=(STAGING) Artificial Apricot R3, O=Let's Encrypt
|
||
✅ [5] 有效期: 2026-07-18 ~ 2026-10-16
|
||
✅ [5] 域名: ['test.example.com']
|
||
✅ [5] 剩余天数: 90
|
||
✅ [6] 版本号: 1
|
||
✅ [7] 脚本生成成功,长度: 1234 字符
|
||
✅ [8] 证书下载成功
|
||
|
||
==================================================
|
||
✅ 端到端测试完成!
|
||
==================================================
|
||
|
||
证书位置: /tmp/cert-e2e-test/test.example.com/
|
||
Staging 证书不受浏览器信任,仅用于验证流程。
|
||
确认流程正常后,在 WebUI 中切换到生产环境重新签发。
|
||
```
|
||
|
||
**Staging vs Production:**
|
||
|
||
| 环境 | 地址 | 说明 |
|
||
|------|------|------|
|
||
| Staging | `https://acme-staging-v02.api.letsencrypt.org/directory` | 测试环境,证书不受信任,无速率限制 |
|
||
| Production | `https://acme-v02.api.letsencrypt.org/directory` | 生产环境,正式证书,有速率限制 |
|
||
|
||
**建议:** 先用 Staging 环境完成全流程测试,确认无误后在 WebUI 中切换到 Production 环境,删除测试域名,重新添加正式域名并签发。
|
||
|
||
---
|
||
|
||
## WebUI 操作指南
|
||
|
||
启动服务后,浏览器访问 `http://服务器IP:8000`(或配置的域名)。
|
||
|
||
### 1. 仪表盘
|
||
|
||
首页展示:
|
||
|
||
- **统计卡片**:服务器总数、域名总数、即将过期证书数
|
||
- **最近部署日志**:最近 10 条客户端部署记录
|
||
- **快捷操作**:新增服务器、新增域名
|
||
|
||
### 2. 服务器管理
|
||
|
||
**新增服务器:**
|
||
|
||
1. 点击侧边栏"服务器管理"
|
||
2. 点击"+ 新增服务器"
|
||
3. 填写表单:
|
||
- **名称**:服务器标识,如 `vps-blog`、`api-server`
|
||
- **平台**:选择 `Linux` 或 `Windows`
|
||
- **Token**:点击"生成"按钮自动生成,或手动填写
|
||
- **IP**:可选,仅用于备注
|
||
4. 点击"创建"
|
||
|
||
**编辑/删除服务器:**
|
||
|
||
- 在列表中点击"编辑"修改信息
|
||
- 点击"删除"会同时删除该服务器下的所有域名配置
|
||
|
||
### 3. 域名管理
|
||
|
||
**新增域名:**
|
||
|
||
1. 点击侧边栏"域名管理"
|
||
2. 点击"+ 新增域名"
|
||
3. 填写表单:
|
||
- **所属服务器**:选择域名所在的业务服务器
|
||
- **域名**:如 `example.com`、`api.example.com`
|
||
- **证书存放路径**:证书在业务服务器上的存放目录
|
||
- Linux 示例:`/etc/nginx/ssl/example.com`
|
||
- Windows 示例:`C:\certs\example.com`
|
||
- **校验命令**:替换证书后验证服务是否正常的命令
|
||
- nginx 示例:`nginx -t`
|
||
- 其他服务根据实际情况填写
|
||
- **重载命令**:证书更新后重载服务的命令
|
||
- Linux 示例:`systemctl reload nginx`
|
||
- Windows 示例:`nginx -s reload`
|
||
4. 点击"创建"
|
||
|
||
**申请证书:**
|
||
|
||
1. 在域名列表中找到目标域名
|
||
2. 点击"申请证书"按钮
|
||
3. 系统自动完成:
|
||
- 通过 DNS-01 验证(自动添加 `_acme-challenge` TXT 记录)
|
||
- 向 Let's Encrypt 申请证书
|
||
- 保存证书到配置的目录
|
||
- 更新证书到期时间
|
||
4. 申请成功后,"证书到期"列会显示到期日期
|
||
|
||
**复制部署命令:**
|
||
|
||
点击"复制部署命令",获取在业务服务器上执行的一键部署命令。
|
||
|
||
**查看脚本:**
|
||
|
||
点击"查看脚本",预览将下发到业务服务器的部署脚本内容。
|
||
|
||
### 4. ACME 配置
|
||
|
||
**基本配置:**
|
||
|
||
1. 点击侧边栏"ACME 配置"
|
||
2. 配置项:
|
||
- **ACME 服务器**:
|
||
- Let's Encrypt (生产):正式环境使用
|
||
- Let's Encrypt (测试):测试环境,不受速率限制
|
||
- **邮箱**:Let's Encrypt 注册邮箱,用于接收证书到期提醒
|
||
- **DNS 提商**:选择使用的 DNS 服务商
|
||
- **续签提前天数**:证书到期前几天自动续签(默认 30 天)
|
||
|
||
**DNS 凭据配置:**
|
||
|
||
根据选择的 DNS 提商填写对应的凭据:
|
||
|
||
| DNS 提商 | 需要的凭据 |
|
||
|----------|-----------|
|
||
| 阿里云 DNS | Access Key + Access Secret |
|
||
| Cloudflare | API Token |
|
||
|
||
**获取阿里云 DNS 凭据:**
|
||
|
||
1. 登录 [阿里云控制台](https://ram.console.aliyun.com/)
|
||
2. 创建子账号,授予 `AliyunDNSFullAccess` 权限
|
||
3. 创建 AccessKey,记录 Access Key ID 和 Access Key Secret
|
||
|
||
**自动续签:**
|
||
|
||
点击"🔄 自动续签所有"按钮,系统会:
|
||
|
||
1. 检查所有域名的证书到期时间
|
||
2. 对即将过期的证书自动执行续签
|
||
3. 显示每个域名的续签结果
|
||
|
||
### 5. 部署日志
|
||
|
||
查看所有客户端(业务服务器)的证书拉取部署记录:
|
||
|
||
- **时间**:部署执行时间
|
||
- **服务器**:执行部署的服务器
|
||
- **域名**:部署的域名
|
||
- **状态**:成功 / 失败 / 跳过
|
||
- **消息**:详细信息
|
||
|
||
支持按状态筛选。
|
||
|
||
### 6. ACME 日志
|
||
|
||
查看所有证书申请/续签操作记录:
|
||
|
||
- **时间**:操作时间
|
||
- **域名**:操作的域名
|
||
- **操作**:申请 / 续签 / 吊销 / 注册
|
||
- **状态**:成功 / 失败 / 进行中
|
||
- **消息**:结果信息
|
||
- **详情**:点击"查看"显示完整日志
|
||
|
||
---
|
||
|
||
## 客户端部署指南
|
||
|
||
### Linux 服务器
|
||
|
||
**首次部署(只需执行一次):**
|
||
|
||
```bash
|
||
# 1. 下载部署脚本(从 WebUI "复制部署命令" 获取)
|
||
curl -fsSL "https://cert.example.com/api/script/example.com?server_name=vps-blog" \
|
||
-o /usr/local/bin/deploy-cert.sh
|
||
|
||
# 2. 添加执行权限
|
||
chmod +x /usr/local/bin/deploy-cert.sh
|
||
|
||
# 3. 添加 cron 定时任务(每 30 分钟检查一次)
|
||
echo '*/30 * * * * /usr/local/bin/deploy-cert.sh >> /var/log/deploy-cert.log 2>&1' | crontab -
|
||
```
|
||
|
||
**手动执行测试:**
|
||
|
||
```bash
|
||
/usr/local/bin/deploy-cert.sh
|
||
```
|
||
|
||
**查看部署日志:**
|
||
|
||
```bash
|
||
tail -f /var/log/deploy-cert.log
|
||
```
|
||
|
||
**脚本执行流程:**
|
||
|
||
```
|
||
1. 请求 CertCenter 获取远端版本号
|
||
2. 对比本地版本号
|
||
├── 一致 → 退出(无需更新)
|
||
└── 不一致 ↓
|
||
3. 下载新证书(fullchain.pem + private.key)
|
||
4. 备份旧证书
|
||
5. 替换新证书
|
||
6. 执行校验命令(nginx -t)
|
||
├── 失败 → 回滚旧证书 → 退出
|
||
└── 成功 ↓
|
||
7. 更新本地版本号
|
||
8. 重载服务(systemctl reload nginx)
|
||
```
|
||
|
||
### Windows 服务器
|
||
|
||
**首次部署:**
|
||
|
||
```powershell
|
||
# 1. 创建脚本目录
|
||
New-Item -ItemType Directory -Force -Path C:\scripts
|
||
|
||
# 2. 下载部署脚本
|
||
Invoke-WebRequest `
|
||
-Uri "https://cert.example.com/api/script/www.example.com?server_name=win-web" `
|
||
-OutFile C:\scripts\deploy-cert.ps1
|
||
|
||
# 3. 添加计划任务(每 30 分钟)
|
||
schtasks /create /sc minute /mo 30 /tn "CertDeploy-www.example.com" /tr "powershell -ExecutionPolicy Bypass -File C:\scripts\deploy-cert.ps1"
|
||
```
|
||
|
||
**手动执行测试:**
|
||
|
||
```powershell
|
||
powershell -ExecutionPolicy Bypass -File C:\scripts\deploy-cert.ps1
|
||
```
|
||
|
||
---
|
||
|
||
## API 参考
|
||
|
||
### 客户端 API
|
||
|
||
供业务服务器上的部署脚本调用,使用 Bearer Token 认证。
|
||
|
||
#### 获取证书版本号
|
||
|
||
```
|
||
GET /api/version/{domain}
|
||
```
|
||
|
||
- 认证:无需认证
|
||
- 返回:版本号字符串(纯文本)
|
||
|
||
**示例:**
|
||
|
||
```bash
|
||
curl https://cert.example.com/api/version/example.com
|
||
# 返回: 3
|
||
```
|
||
|
||
#### 下载证书
|
||
|
||
```
|
||
GET /api/cert/{domain}/fullchain
|
||
GET /api/cert/{domain}/private
|
||
```
|
||
|
||
- 认证:`Authorization: Bearer <token>`
|
||
- 返回:PEM 格式证书文件
|
||
|
||
**示例:**
|
||
|
||
```bash
|
||
curl -H "Authorization: Bearer tok_xxx" \
|
||
https://cert.example.com/api/cert/example.com/fullchain \
|
||
-o fullchain.pem
|
||
```
|
||
|
||
#### 获取部署脚本
|
||
|
||
```
|
||
GET /api/script/{domain}?server_name={server_name}
|
||
```
|
||
|
||
- 认证:无需认证
|
||
- 返回:部署脚本内容(bash 或 PowerShell)
|
||
|
||
**示例:**
|
||
|
||
```bash
|
||
curl https://cert.example.com/api/script/example.com?server_name=vps-blog \
|
||
-o /usr/local/bin/deploy-cert.sh
|
||
```
|
||
|
||
#### 上报部署结果
|
||
|
||
```
|
||
POST /api/report/{domain}?status={status}&message={message}
|
||
```
|
||
|
||
- 认证:`Authorization: Bearer <token>`
|
||
- 参数:
|
||
- `status`:`success` / `failed` / `skipped`
|
||
- `message`:可选,详细信息
|
||
|
||
**示例:**
|
||
|
||
```bash
|
||
curl -X POST \
|
||
-H "Authorization: Bearer tok_xxx" \
|
||
"https://cert.example.com/api/report/example.com?status=success&message=updated+to+v3"
|
||
```
|
||
|
||
### 管理 API
|
||
|
||
供 WebUI 前端调用,所有接口以 `/admin/api` 为前缀。
|
||
|
||
#### 统计数据
|
||
|
||
```
|
||
GET /admin/api/stats
|
||
```
|
||
|
||
返回:
|
||
|
||
```json
|
||
{
|
||
"server_count": 3,
|
||
"domain_count": 5,
|
||
"expiring_count": 1,
|
||
"recent_logs": [...]
|
||
}
|
||
```
|
||
|
||
#### 服务器管理
|
||
|
||
| 方法 | 路径 | 说明 |
|
||
|------|------|------|
|
||
| GET | `/admin/api/servers` | 服务器列表 |
|
||
| GET | `/admin/api/servers/{id}` | 服务器详情 |
|
||
| POST | `/admin/api/servers` | 新增服务器 |
|
||
| PUT | `/admin/api/servers/{id}` | 编辑服务器 |
|
||
| DELETE | `/admin/api/servers/{id}` | 删除服务器 |
|
||
|
||
**新增/编辑请求体:**
|
||
|
||
```json
|
||
{
|
||
"name": "vps-blog",
|
||
"platform": "linux",
|
||
"token": "tok_xxxxxxxxxxxx",
|
||
"ip": "1.2.3.4"
|
||
}
|
||
```
|
||
|
||
#### 域名管理
|
||
|
||
| 方法 | 路径 | 说明 |
|
||
|------|------|------|
|
||
| GET | `/admin/api/domains` | 域名列表 |
|
||
| GET | `/admin/api/domains/{id}` | 域名详情 |
|
||
| POST | `/admin/api/domains` | 新增域名 |
|
||
| PUT | `/admin/api/domains/{id}` | 编辑域名 |
|
||
| DELETE | `/admin/api/domains/{id}` | 删除域名 |
|
||
|
||
**新增/编辑请求体:**
|
||
|
||
```json
|
||
{
|
||
"server_id": 1,
|
||
"domain": "example.com",
|
||
"cert_dir": "/etc/nginx/ssl/example.com",
|
||
"check_cmd": "nginx -t",
|
||
"reload_cmd": "systemctl reload nginx"
|
||
}
|
||
```
|
||
|
||
#### ACME 配置
|
||
|
||
| 方法 | 路径 | 说明 |
|
||
|------|------|------|
|
||
| GET | `/admin/api/acme/config` | 获取 ACME 配置 |
|
||
| PUT | `/admin/api/acme/config` | 更新 ACME 配置 |
|
||
|
||
**更新请求体:**
|
||
|
||
```json
|
||
{
|
||
"acme_server": "https://acme-v02.api.letsencrypt.org/directory",
|
||
"email": "admin@example.com",
|
||
"dns_provider": "aliyun",
|
||
"dns_credentials": "{\"access_key\":\"xxx\",\"access_secret\":\"xxx\"}",
|
||
"renew_days": 30
|
||
}
|
||
```
|
||
|
||
#### ACME 操作
|
||
|
||
| 方法 | 路径 | 说明 |
|
||
|------|------|------|
|
||
| POST | `/admin/api/acme/issue/{domain_id}` | 申请证书 |
|
||
| POST | `/admin/api/acme/renew/{domain_id}` | 续签证书 |
|
||
| POST | `/admin/api/acme/auto-renew` | 自动续签所有 |
|
||
|
||
#### 日志查询
|
||
|
||
| 方法 | 路径 | 说明 |
|
||
|------|------|------|
|
||
| GET | `/admin/api/logs?status={status}` | 部署日志 |
|
||
| GET | `/admin/api/acme/logs?status={status}` | ACME 日志 |
|
||
|
||
#### 证书信息
|
||
|
||
```
|
||
GET /admin/api/cert-info/{domain_id}
|
||
```
|
||
|
||
返回:
|
||
|
||
```json
|
||
{
|
||
"domain": "example.com",
|
||
"cert_info": {
|
||
"subject": "CN=example.com",
|
||
"issuer": "CN=R3, O=Let's Encrypt",
|
||
"not_before": "2026-01-01T00:00:00",
|
||
"not_after": "2026-04-01T00:00:00",
|
||
"serial_number": "1234567890",
|
||
"san": ["example.com"]
|
||
},
|
||
"need_renew": false,
|
||
"days_left": 75
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 配说说明
|
||
|
||
### 环境变量(.env)
|
||
|
||
| 变量 | 默认值 | 说明 |
|
||
|------|--------|------|
|
||
| `DATABASE_URL` | `sqlite+aiosqlite:///./certcenter.db` | 数据库连接字符串 |
|
||
| `CERT_DIR` | `/srv/certs` | 证书存储根目录 |
|
||
| `BASE_URL` | `https://cert.example.com` | CertCenter 访问地址 |
|
||
|
||
### 证书存储目录结构
|
||
|
||
```
|
||
/srv/certs/
|
||
├── example.com/
|
||
│ ├── fullchain.pem # 完整证书链
|
||
│ ├── private.key # 私钥
|
||
│ └── .version # 当前版本号
|
||
├── api.example.com/
|
||
│ ├── fullchain.pem
|
||
│ ├── private.key
|
||
│ └── .version
|
||
└── .certbot-work/ # certbot 工作目录
|
||
├── .certbot-config/
|
||
└── .certbot-logs/
|
||
```
|
||
|
||
### 版本号说明
|
||
|
||
版本号为递增整数,初始值为 `0`。每次证书申请/续签成功后自动 +1。
|
||
|
||
客户端通过对比版本号判断是否需要下载新证书,避免重复下载。
|
||
|
||
---
|
||
|
||
## 常见问题
|
||
|
||
### Q: 申请证书时报错 "DNS problem: NXDOMAIN looking up TXT for _acme-challenge.xxx"
|
||
|
||
**原因:** DNS TXT 记录未正确添加或未及时传播。
|
||
|
||
**解决方案:**
|
||
|
||
1. 确认阿里云 DNS 凭据正确(Access Key / Secret)
|
||
2. 确认域名使用的是阿里云 DNS 服务
|
||
3. 等待 DNS 记录传播(通常 1-2 分钟)
|
||
4. 使用测试环境(Let's Encrypt Staging)调试
|
||
|
||
### Q: 客户端脚本执行后没有更新证书
|
||
|
||
**可能原因:**
|
||
|
||
1. 版本号一致,无需更新(正常行为)
|
||
2. Token 不正确,API 返回 401
|
||
3. 网络不通,无法访问 CertCenter
|
||
|
||
**排查步骤:**
|
||
|
||
```bash
|
||
# 1. 手动测试版本接口
|
||
curl https://cert.example.com/api/version/example.com
|
||
|
||
# 2. 手动测试证书下载
|
||
curl -H "Authorization: Bearer tok_xxx" \
|
||
https://cert.example.com/api/cert/example.com/fullchain
|
||
|
||
# 3. 查看脚本详细执行过程
|
||
bash -x /usr/local/bin/deploy-cert.sh
|
||
```
|
||
|
||
### Q: nginx -t 校验失败,证书已回滚
|
||
|
||
**原因:** 新证书与 nginx 配置不匹配。
|
||
|
||
**排查步骤:**
|
||
|
||
```bash
|
||
# 1. 检查 nginx 配置
|
||
nginx -t
|
||
|
||
# 2. 检查证书内容
|
||
openssl x509 -in /etc/nginx/ssl/example.com/fullchain.pem -text -noout
|
||
|
||
# 3. 检查私钥是否匹配
|
||
openssl x509 -noout -modulus -in /etc/nginx/ssl/example.com/fullchain.pem | md5sum
|
||
openssl rsa -noout -modulus -in /etc/nginx/ssl/example.com/private.key | md5sum
|
||
```
|
||
|
||
### Q: 如何更换 DNS 提商
|
||
|
||
1. 在 WebUI 的"ACME 配置"页面修改"DNS 提商"下拉框
|
||
2. 填写新提商的凭据
|
||
3. 点击"保存凭据"
|
||
4. 重新申请证书
|
||
|
||
### Q: 如何备份和迁移
|
||
|
||
**备份:**
|
||
|
||
```bash
|
||
# 备份数据库
|
||
cp certcenter.db certcenter.db.bak
|
||
|
||
# 备份证书目录
|
||
tar czf certs-backup.tar.gz /srv/certs/
|
||
|
||
# 备份配置
|
||
cp .env .env.bak
|
||
```
|
||
|
||
**迁移:**
|
||
|
||
1. 在新服务器部署项目
|
||
2. 恢复数据库和证书目录
|
||
3. 修改 `.env` 中的 `BASE_URL` 为新地址
|
||
4. 更新业务服务器脚本中的地址(或重新执行部署命令)
|
||
|
||
### Q: 如何添加通配符证书
|
||
|
||
通配符证书需要 DNS 验证,CertCenter 已支持。在域名管理中添加:
|
||
|
||
```
|
||
域名: *.example.com
|
||
```
|
||
|
||
申请时系统会自动通过 DNS-01 验证完成签发。
|
||
|
||
### Q: Let's Encrypt 速率限制
|
||
|
||
Let's Encrypt 有以下限制:
|
||
|
||
| 限制类型 | 限制值 |
|
||
|----------|--------|
|
||
| 每个域名每周证书数 | 50 张 |
|
||
| 每个域名每小时重复证书数 | 5 张 |
|
||
| 注册账户数 | 每个 IP 每小时 10 个 |
|
||
|
||
**建议:** 先使用测试环境(Staging)调试,确认无误后再切换到生产环境。
|