Files
opsx-vpn/docs/specs/SDD-003-domain-tls.md
T

77 lines
7.5 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.
# SDD-003:域名与 HTTPS 证书管理
| 字段 | 内容 |
|---|---|
| 版本 / 变更级别 | 0.1 / S3(证书私钥、网络入口及生产代理配置) |
| 设计状态 | Draft |
| 实现状态 | 应用内 ACME、域名设置与管理员状态页已实现,本地构建/单测通过;生产端口/证书待部署验证 |
| 负责人 / 日期 | 待指定 / 2026-10-21 |
| 关联需求 | `test-link.appx365.com` 使用 HTTP-01 自动签发和续期;管理后台提供域名及证书页面;桌面客户端可使用 HTTP 或 HTTPS,并在设置中填写地址 |
| 批准状态 | 用户要求开发与评估;生产入口切换以部署核验为准 |
## 1. 背景、目标与范围
- 当前行为:[后端路由](../../backend/internal/api/api.go)在 `:18080` 提供管理站点和 API;[部署文档](../deploy.md)仅描述外置 Nginx/Let's Encrypt 方案,没有集成证书状态页面。桌面 [URL 校验](../../client/src-tauri/src/server.rs)拒绝远端 HTTP,地址位于概览页。
- 2026-10-21 只读核对:`test-link.appx365.com` 解析到 `121.4.111.208`;服务器没有发现 Nginx、Certbot,80/443 未被监听。
- 本次目标:使用项目已有的 Go `autocert` 依赖,在现有 Gin 进程中提供公网 80/443、HTTP-01 自动签发/续签;管理 API 和页面展示域名及缓存证书状态;桌面将地址放在设置中,接受 HTTP(提示明文风险)。单域名场景无需 Nginx、Certbot 或额外的 `gin-gonic/autotls` 封装。
- 非目标:将证书私钥存到 SQLite/浏览器;允许任意命令、代理任意上游、公开认证服务内部端口;向桌面发放管理员 token;把 WireGuard 51820/UDP 交给 Nginx 代理。
- 保留行为:旧 `:18080` 管理界面与现有 VPN 专线不受证书签发流程影响;证书失败保持现有 HTTP 服务可用。
## 2. 需求与约束
- FR-01:管理员能查看当前域名、DNS、监听地址、证书颁发者/到期日;接口不返回私钥、登录凭据或证书正文。
- FR-02:服务端仅为管理员保存的域名或 `VPN_TLS_DOMAIN` 指定的单一有效域名签发(环境变量优先),ACME 使用该域名 HostWhitelist;首次 HTTPS 握手签发,证书与账号密钥持久缓存于 `data/acme`,禁止任意域名签发。
- FR-03:续签由 `autocert.Manager` 自动管理;重启后缓存证书由首次握手加载并继续续签,到期日由管理页显示;签发或续签失败保留已有 18080 HTTP 入口。
- FR-04:HTTP 80 通过 `autocert.Manager.HTTPHandler` 放行 HTTP-01,其他当前域名请求重定向 HTTPS;443 用同一 Gin handler 提供管理站点与 API。OIDC 认证入口若另设域名需另行设计其路由,不从后台域名假设回调。
- FR-05:桌面客户端服务端地址移到“设置”,支持完整 HTTP/HTTPS 根地址并允许保存;HTTP 展示风险提示,但健康检查可以执行。不保存账号和令牌。
- NFR-01:管理 API 仅 `admin`,域名符合严格 FQDN 白名单;私钥缓存目录权限 0700,不暴露证书缓存内容或触发任意命令。
- NFR-02:部署前核对 DNS、云安全组 TCP 80/443、端口占用、现有入口与回退;续签有可观测到期信息。浏览器页面与 Tauri 环境的 URL 校验一致。
## 3. 架构与方案
```text
Internet TCP 80 → autocert HTTPHandler(HTTP-01;其余跳转 HTTPS)
Internet TCP 443 → autocert TLSConfig → 同一 Gin 管理站点 / API
ACME 密钥与证书 → data/acme(重启保留)
管理页面 → admin API → 仅读 DNS、域名及证书缓存元数据
旧 TCP 18080 → 同一 Gin handler(保留兼容;后续限制公网访问)
```
使用项目已存在的 `golang.org/x/crypto/acme/autocert`,无需额外 Gin 插件或依赖:管理员将域名保存到 `data/tls-domain`,重启后生效;`VPN_TLS_DOMAIN` 优先于文件配置,`VPN_TLS_HTTP_ADDR` / `VPN_TLS_HTTPS_ADDR` 可覆盖监听地址;未配置域名时完全保留旧启动行为。证书管理不写入业务数据库。首次公网 HTTPS 请求后才有真实缓存证书,管理页未签发前必须显示“等待首次访问”,不能声称证书已签发。HTTP 客户端输入只是用户选择;登录与设备令牌将来仍应推荐 HTTPS。
## 4. 接口、数据与配置契约
- `GET /api/system/tls` 与 `PUT /api/system/tls/domain` 仅管理员可用,分别读取状态与保存单域名;状态返回当前/待启用域名、是否需重启、DNS、监听地址及 autocert 缓存证书的颁发者/到期日。没有手动签发按钮,首次 TLS 握手会触发签发。证书缓存位于 `data/acme`,不接收请求任意域名作为签发目标。
- 云安全组 TCP 80/443 与 DNS A 解析须对应当前服务器;`VPN_TLS_DOMAIN=test-link.appx365.com` 生效时若端口占用,应用拒绝启动并保持旧进程可回滚。
- 桌面仅在本机保存不含凭据的服务端地址;只允许 `http` / `https` 和根地址,拒绝 URL 账号、查询、片段、非根路径;实际健康检查继续调用 `/api/health`。
## 5. 风险与验收
| 风险或失败场景 | 防护 / 恢复 | 可观察信号 |
|---|---|---|
| 域名指向错误或 80 不可达 | 不启用证书前先核对 DNS 和公网入站;失败保留旧 18080 | ACME 错误日志、证书未缓存 |
| 80/443 端口占用 | 启动时先绑定两个端口,失败则终止新版进程并回退旧版 | 启动日志与端口监听 |
| 续签失败与证书过期 | autocert 自动续签、到期日提示、缓存保留 | 证书到期信息与 ACME 日志 |
| 公网 HTTP 管理凭据泄漏 | HTTPS 默认推荐;明示 HTTP 风险;服务端切换后限制 18080 公网 | HTTPS 可达、18080 仅可信入口 |
| AC | 对应需求 | 前置与操作 | 预期结果 | 验证方式 | 状态 |
|---|---|---|---|---|---|
| AC-01 | FR-01, NFR-01 | 管理员/非管理员请求状态 | 仅管理员可查看,证书私钥不回传 | 路由权限检查;线上请求待验证 | 部分验证 |
| AC-02 | FR-02, NFR-01 | 设置非法域名或端口占用 | 启动前拒绝,不向任意域名申请 | 域名/端口失败分支单测 | 已通过(本地) |
| AC-03 | FR-03, FR-04, NFR-02 | 首次 HTTPS 请求与后续续签 | HTTP-01 可达、443 站点正确、缓存保留与自动续签 | DNS、ACME、TLS 实机验证 | Not Run |
| AC-04 | FR-05 | 桌面保存远端 HTTP/HTTPS 并检测 | 地址保存在设置;HTTP 提示且能检测,其他协议拒绝 | Rust 单测、UI 构建;桌面实机待验证 | 部分验证 |
## 6. 发布与恢复
- 先完成本地构建和测试,备份现有二进制、确认公网 80/443 无占用及云安全组策略,再配置 `VPN_TLS_DOMAIN` 并切换新二进制。以 `https://test-link.appx365.com/api/health` 触发首次签发;确认 TLS 域名、证书到期和管理站点之后收敛 18080 公网权限。
- 若端口监听、HTTP-01 或首次握手失败,恢复原二进制/启动参数并重启旧 `:18080` 入口;`data/acme` 保留避免重复申请。不修改现有 VPN 网关或删除证书缓存。
- 私钥文件和用户数据不删除;证书吊销、DNS 变更及防火墙收敛分别决策。
## 7. 待决问题
| 问题 | 是否阻塞 | 推荐方案 |
|---|---|---|
| 云安全组 TCP 80/443 是否放行 | 阻塞线上 HTTP-01 | 先核实公网访问再签发 |
| `test-link.appx365.com` 是否同时承载 OIDC 认证服务 | 不阻塞管理站点 | 初版仅反代管理站点;OIDC 独立域名或另行设计路径路由 |
| 证书到期告警 | 不阻塞本地实现 | 当前页面展示到期时间;后续可补主动告警 |