diff --git a/新镜像改造与无损替换方案.md b/新镜像改造与无损替换方案.md new file mode 100644 index 0000000..7bc0a55 --- /dev/null +++ b/新镜像改造与无损替换方案.md @@ -0,0 +1,472 @@ +# Trojan 管理项目新镜像改造与无损替换方案 + +## 1. 结论 + +基于当前 Go 后端和 Vue 前端源码,可以制作一个功能兼容的新镜像,并替换香港、美国两台服务器上现有的 `jrohy/trojan` 容器。用户数据、流量统计、管理员状态、订阅、分享链接、Trojan TLS 回落以及现有 Nginx/Caddy/Gitea 架构均可保留。 + +但不能只修改现有 Dockerfile 后重新构建。要达到“证书可靠自动续期、端口清晰可配置、容器重启自动恢复、可以无损替换”,必须同时完成以下五项: + +1. 持久化 Trojan 配置、管理后台 LevelDB、ACME 账户和证书; +2. 将 Trojan 内部监听端口和客户端公网端口分离; +3. 证书续期成功后校验证书并重启 Trojan,使新证书真正生效; +4. 修复容器内 Trojan 与 Web 服务的启动、状态、日志和健康检查; +5. 从本地前后端源码进行可复现、多阶段镜像构建,不再下载运行时 `latest` 文件。 + +推荐先发布一个“兼容替换版”镜像,保持当前 `host network + MariaDB + Nginx stream` 的部署模型,以最低风险替换现网;运行稳定后,再单独规划无特权、bridge 网络的容器原生版本。不要把两次架构迁移合并到同一次上线。 + +## 2. 当前源码确认出的约束 + +| 位置 | 当前行为 | 对新镜像的影响 | +| --- | --- | --- | +| `trojan-master/core/server.go` | Trojan 配置固定在 `/usr/local/etc/trojan/config.json`,直接覆盖写入 | 必须挂载持久化,并改为备份、临时文件、原子替换和失败回滚 | +| `trojan-master/core/leveldb.go` | 管理状态固定在 `/var/lib/trojan-manager` | 必须迁移,否则管理员密码、JWT 密钥、标题、重置日等会丢失 | +| `trojan-master/trojan/web.go` | `GetDomainAndPort()` 直接返回 `ssl.sni + local_port` | 当前内部端口为 8443,但客户端应连接 443,必须新增 `public_port` | +| `trojan-master/trojan/trojan.go` | `port` 命令修改 `local_port`、开防火墙并直接重启 | 无端口冲突检查、事务和回滚;在特权容器中还可能修改宿主机 iptables | +| `trojan-master/cmd/web.go` | 已支持 `--host`、`--port` | 能复用现有 CLI 参数,无需重写 Web 监听实现 | +| `trojan-master/asset/trojan-web.service` | 未传参数,默认监听 `0.0.0.0:80` | 新镜像必须显式使用 `127.0.0.1:8081` | +| `trojan-master/trojan/install.go` | ACME standalone 默认使用 80,证书直接引用 acme.sh 内部目录 | 与 Nginx 80 冲突;证书应安装到稳定路径 | +| `trojan-master/install.sh` | 定时任务停止 Web、执行 `acme.sh --cron`、再启动 Web | 没有重启 Trojan,新证书不一定生效;还会造成每天后台短暂中断 | +| `trojan-master/util/command.go` | systemctl 失败时会在线下载替代脚本,控制函数不向 API 返回错误 | 构建不可复现,Web 可能在服务启动失败时仍显示成功 | +| `trojan-master/asset/Dockerfile` | 构建时下载上游最新管理二进制、远程 unit 和 systemctl 脚本 | 当前 Dockerfile 不会包含本地修改过的 Go/前端源码,必须重写 | +| `trojan-master/web/web.go` | 前端通过 `//go:embed templates/*` 嵌入 Go 二进制 | 必须先构建前端,并将 `dist` 复制到 `web/templates` 后再编译 Go | +| `trojan-web-master/src/views/user/index.vue` | 后端返回的端口直接用于分享链接和订阅地址 | 后端旧字段 `port` 应继续返回公网端口 443,保证旧前端兼容 | +| `trojan-web-master/src/utils/request.js` | Axios 超时为 8 秒 | 证书申请和配置切换必须异步执行并返回任务 ID | + +## 3. 证书管理结论 + +### 3.1 不建议让 Caddy直接管理 Trojan 域名证书 + +当前链路是: + +```text +客户端 :443 + -> 宿主机 Nginx stream 按 SNI 分流 + -> www.* TLS 原样透传至 Trojan :8443 + -> 非 Trojan HTTPS 由 Trojan 回落到 Web :8081 +``` + +`www.*` 的 TLS 由 Trojan 终止,Caddy没有接触这条 TLS 会话,因此不能像管理 `git.*` 一样自然地为 Trojan 管理证书。强行读取 Caddy 内部证书存储并复制给 Trojan,会依赖 Caddy 的内部目录结构和续期事件,不适合作为稳定接口。 + +最终职责保持为: + +- `git.hk.chermack.top`、`git.us.chermack.top`:继续由 Caddy 自动申请和续期; +- `www.hk.chermack.top`、`www.us.chermack.top`:由新镜像内的 acme.sh 自动管理,并在续期后可靠重启 Trojan; +- Nginx继续保留 `/.well-known/acme-challenge/` 到 `127.0.0.1:8082` 的转发。 + +### 3.2 推荐的自动续期流程 + +新镜像常驻一个受限的 ACME challenge 服务,仅在 `127.0.0.1:8082` 提供 `/.well-known/acme-challenge/` 下的文件;Nginx 继续把相同路径转发到该端口。acme.sh 使用 webroot,不再临时监听端口,也不停止管理后台: + +```bash +acme.sh --issue \ + --server letsencrypt \ + --webroot /var/lib/trojan-acme-webroot \ + --keylength ec-256 \ + -d "${ACME_DOMAIN}" +``` + +证书不再让 Trojan 直接引用 acme.sh 内部工作目录,而是发布到稳定路径: + +```bash +acme.sh --install-cert --ecc -d "${ACME_DOMAIN}" \ + --fullchain-file /etc/trojan/certs/fullchain.pem \ + --key-file /etc/trojan/certs/private.key \ + --reloadcmd /usr/local/sbin/reload-trojan-after-cert +``` + +定时器每天检查一次并增加随机延迟,只有确实续期成功时才执行 reload hook。reload hook 必须: + +1. 检查证书和私钥匹配; +2. 检查 SAN 包含配置域名; +3. 比较新旧证书指纹,避免无变化重启; +4. 备份当前证书; +5. 重启 Trojan,并在限定时间内确认 8443 恢复监听; +6. 失败时恢复旧证书并记录错误。 + +不再每天停止 `trojan-web`,也不删除其他域名的 acme.sh 定时任务。 + +证书状态由 Go 读取 X.509 元数据,Web 页面只展示域名、签发者、起止时间、剩余天数、最近续期和错误;永不向浏览器返回私钥或任意文件内容。 + +## 4. 端口模型改造 + +### 4.1 必须拆分的端口 + +```text +public_port 客户端公开端口,当前固定为 443 +listen_port Trojan 内部监听端口,当前为 8443 +fallback_port Trojan 非协议流量回落端口,当前为 8081 +web_port 管理后台监听端口,当前为 8081 +acme_http_port HTTP-01 challenge 端口,当前为 8082 +``` + +Trojan 原生 `config.json` 继续只保存它支持的字段。新增独立管理配置,避免把未知字段写入 Trojan 配置: + +```yaml +schema_version: 1 +public: + domain: www.hk.chermack.top + port: 443 +runtime: + listen_address: 0.0.0.0 + listen_port: 8443 + fallback_address: 127.0.0.1 + fallback_port: 8081 +web: + listen_address: 127.0.0.1 + listen_port: 8081 +acme: + enabled: true + domain: www.hk.chermack.top + http_port: 8082 + renew_before_days: 30 +firewall: + manage: false +``` + +建议路径为 `/etc/trojan-manager/manager.yaml`,宿主机持久化到 `/opt/trojan/config/manager.yaml`。 + +### 4.2 兼容旧前端和旧客户端 + +保留所有现有接口与 JSON 字段。`/trojan/user` 的兼容响应为: + +```json +{ + "domain": "www.hk.chermack.top", + "port": 443, + "publicPort": 443, + "listenPort": 8443, + "userList": [] +} +``` + +- 旧前端继续读取 `port`,得到正确的公网端口 443; +- 新前端优先读取 `publicPort`; +- 未配置 `public_port` 的旧部署自动使用 `local_port`,保持原行为; +- `local_port` 只负责 Trojan 实际监听,不再用于生成分享链接。 + +### 4.3 端口变更的边界 + +Web 页面不能通过挂载 Docker Socket 去修改宿主机端口映射,这会给予容器近似 root 的宿主机控制权。建议: + +- Web 可以修改并验证应用内部端口和公网宣告端口; +- 宿主机 Nginx upstream、Docker/host-network 参数由配套的 `trojanctl apply` 脚本管理; +- 后端返回 `restartRequired`、`nginxReloadRequired` 或 `containerRecreateRequired`; +- `trojanctl apply` 先生成候选配置,再执行 `nginx -t`,成功后才 reload;失败自动恢复旧文件。 + +当前两台服务器无需频繁改变内部端口,建议长期保持 8443/8081/8082,只将客户端公网端口保持为 443。 + +## 5. Go 后端改造 + +### 5.1 配置与事务 + +新增包建议: + +```text +config/manager.go 管理配置结构、默认值、版本迁移 +config/transaction.go 文件锁、备份、原子写入和回滚 +certificate/manager.go 证书申请、状态、续期和校验 +service/manager.go 服务控制接口 +service/systemd.go 首版兼容镜像实现 +jobs/manager.go 异步任务和状态 +health/check.go Web、DB、Trojan、证书检查 +``` + +应用配置流程: + +1. 校验域名、端口范围和端口之间的冲突; +2. 生成 manager 配置和 Trojan 候选配置; +3. 检查证书路径、数据库连接和候选 JSON; +4. 备份当前配置; +5. 原子替换并重启相应服务; +6. 检查监听端口和服务状态; +7. 失败则恢复配置并重新启动旧版本。 + +`SystemctlStart/Stop/Restart` 必须返回 `error`,controller 只有在服务实际成功后才能返回 `success`。 + +### 5.2 新增 API + +现有 `/trojan/*`、`/common/*`、`/auth/*` 接口全部保留。新增: + +```text +GET /system/config +POST /system/config/validate +POST /system/config/apply +GET /system/certificate +POST /system/certificate/renew +GET /system/jobs/:id +GET /healthz +GET /readyz +``` + +申请证书和应用端口变更立即返回任务 ID,前端轮询任务;不能受现有 8 秒 Axios 超时限制。 + +网络、证书、服务控制接口必须仅允许 `admin`。当前认证中普通用户登录后也能通过统一 middleware 访问受保护路由;新接口必须增加后端管理员校验,不能只靠前端隐藏菜单。初始化接口也应只在尚无管理员时允许注册。 + +### 5.3 镜像模式下的现有命令 + +| 功能 | 处理方式 | +| --- | --- | +| 用户、流量、期限、订阅、导入导出 | 原样保留,继续使用现有 MariaDB 表 | +| Trojan 启停、重启、状态、实时日志 | 保留命令/API,服务层返回真实结果 | +| `trojan port` | 保留交互用法,新增非交互 `config set`;使用事务应用 | +| `trojan tls` | 保留自定义证书模式,新增受控 ACME 模式和状态查询 | +| Trojan / trojan-go 切换 | 镜像内预置已固定版本的核心,不再运行时下载 latest | +| `update` / `updateWeb` | 改为检查镜像版本并提示 Compose 升级;不允许容器自改二进制 | +| 自动开防火墙 | Docker 模式默认关闭,公网只开放 Nginx 80/443 与 Gitea 2222 | + +完全保留“容器内在线覆盖二进制”的更新方式与不可变镜像互相冲突,因此这一项应明确改为镜像升级;它不影响用户管理和 Trojan 业务功能。 + +## 6. 前端改造 + +新增 `src/views/settings/index.vue` 和 `src/api/system.js`,页面分为: + +1. 网络配置:域名、公网端口、Trojan 监听端口、Web 监听地址/端口; +2. 证书状态:模式、签发者、有效期、剩余天数、最近续期和错误; +3. 应用预览:展示会重启什么、是否需要 Nginx reload 或容器重建; +4. 任务状态:轮询申请/续期/应用任务,展示成功或回滚结果。 + +需要调整: + +- `src/router/index.js`:增加 `/settings`; +- `src/views/layout/components/Navbar.vue`:增加“网络与证书”入口; +- `src/views/user/index.vue`:优先使用 `publicPort`; +- `src/lang/zh.js`、`src/lang/en.js`:增加配置、证书和风险提示文案; +- `src/api/trojan.js`:原 API 保留,避免旧页面失效。 + +若新接口为 404,新设置页只提示“当前后端不支持”,不能影响 Dashboard、用户列表和日志页,从而允许新旧前后端短暂混用和快速回滚。 + +当前生产前端通过 CDN 加载 Vue、Axios、Element Plus 等依赖,同时在 Vite 中将它们 external。新镜像应将依赖打进静态资源,避免 CDN 不可达造成后台白屏;保留 `base: './'` 和 hash 路由。 + +## 7. 新镜像与服务器包 + +### 7.1 交付物 + +建议不是只交付一个镜像标签,而是交付以下完整包: + +```text +trojan-stack/ + compose.yaml + runtime.env.example + nginx/ + www-http.conf.tpl + tls-sni.conf.tpl + scripts/ + migrate-v1.sh + apply.sh + verify.sh + rollback.sh + docs/ + upgrade.md +``` + +镜像建议使用固定版本标签,例如: + +```text +REGISTRY/trojan-manager-compat:2.0.0 +``` + +部署时同时记录镜像 digest,禁止使用 `latest`。 + +### 7.2 可复现构建 + +新 Dockerfile 使用多阶段构建: + +```text +Node 固定版本 + npm ci -> npm run build + ↓ dist +Go 固定版本 + dist -> trojan-master/web/templates + go test -> go build + ↓ manager binary +Runtime 固定 digest + manager + 固定版本 Trojan 核心 + acme.sh + unit/timer + healthcheck +``` + +必须固定 Node、Go、Trojan、trojan-go、acme.sh 和基础镜像版本;下载的上游核心必须校验 SHA-256。建议生成 SBOM,并对发布镜像签名。 + +当前项目采用 GPLv3;发布修改后的二进制或镜像时,应同时提供对应修改源码和构建方式。 + +### 7.3 第一版兼容运行模式 + +为避免在一次变更中同时修改数据库网络、服务管理和容器权限,第一版保持现状: + +```yaml +services: + trojan: + image: REGISTRY/trojan-manager-compat:2.0.0 + container_name: trojan + network_mode: host + privileged: true + restart: always + command: init + env_file: + - /opt/trojan/runtime.env + volumes: + - /opt/trojan/config:/usr/local/etc/trojan + - /opt/trojan/manager:/var/lib/trojan-manager + - /opt/trojan/acme:/root/.acme.sh + - /opt/trojan/certs:/etc/trojan/certs +``` + +兼容镜像使用真实、预置的服务定义,不在运行时下载 systemctl 替代脚本。镜像内预置并启用: + +```text +trojan.service +trojan-web.service +trojan-acme-challenge.service +trojan-acme-renew.service +trojan-acme-renew.timer +``` + +这能最大程度保留现有 CLI、Web 启停和 `journalctl` 实时日志功能。长期版本再用 s6 等容器进程管理器替换 systemd,并同时重构服务控制与日志;该迁移不与首个兼容替换版本合并。 + +### 7.4 健康检查 + +镜像必须包含: + +- `GET /healthz`:管理进程存活; +- `GET /readyz`:数据库可访问、Trojan 服务运行、8443 监听、证书未过期; +- Docker `HEALTHCHECK`; +- 服务启动失败时非零退出,交给 Docker restart policy 恢复。 + +## 8. 持久化与数据兼容 + +替换前必须迁移: + +```text +/usr/local/etc/trojan/config.json Trojan 配置、数据库连接、TLS 路径 +/var/lib/trojan-manager 管理员、JWT 密钥、标题、重置日等 +/root/.acme.sh ACME 账户、域名和续期状态 +``` + +新证书固定发布到: + +```text +/etc/trojan/certs/fullchain.pem +/etc/trojan/certs/private.key +``` + +现有 `trojan-mariadb` 继续独立运行,不在本次更换版本或表结构。上线前备份数据库并记录用户数量;新容器启动后比对。不要在首次启动时自动执行会删表/重建的数据库升级。 + +## 9. 两台服务器的配置 + +香港 `/opt/trojan/runtime.env`: + +```env +ACME_DOMAIN=www.hk.chermack.top +PUBLIC_DOMAIN=www.hk.chermack.top +PUBLIC_PORT=443 +TROJAN_LISTEN_PORT=8443 +WEB_HOST=127.0.0.1 +WEB_PORT=8081 +ACME_HTTP_PORT=8082 +MANAGE_FIREWALL=false +``` + +美国: + +```env +ACME_DOMAIN=www.us.chermack.top +PUBLIC_DOMAIN=www.us.chermack.top +PUBLIC_PORT=443 +TROJAN_LISTEN_PORT=8443 +WEB_HOST=127.0.0.1 +WEB_PORT=8081 +ACME_HTTP_PORT=8082 +MANAGE_FIREWALL=false +``` + +Gitea、PostgreSQL、Caddy 和现有 Nginx 80/443 分流不需要改变。 + +## 10. 无损替换步骤 + +### 10.1 上线前 + +1. 固定新镜像版本和 digest; +2. 备份 MariaDB,并记录用户数量和关键表结构; +3. 从旧容器导出 Trojan 配置、LevelDB 和 `.acme.sh`; +4. 在宿主机 `/opt/trojan` 建立持久化目录,并限制证书/密钥权限; +5. 使用迁移工具生成 `manager.yaml`,其中 `public_port=443`、`listen_port=8443`; +6. 校验证书域名、证书与私钥匹配、数据库可连接; +7. 保存旧容器的 `docker inspect` 和当前 Nginx 配置。 + +### 10.2 预演 + +兼容镜像使用 host network,相同端口不能与旧容器并行。可先用临时端口启动候选容器: + +```text +Trojan 18443 +Web 18081 +ACME 18082 +public_port 仍为 443 +``` + +预演检查: + +- 管理员登录; +- 用户数量、流量和期限; +- 分享链接和 Clash 配置使用公网 443; +- Web 普通访问及 Trojan TLS 回落; +- Trojan 客户端真实连接; +- 证书状态可读取; +- 服务启停、重启和日志; +- 容器重启后两个服务自动恢复。 + +### 10.3 正式切换 + +1. 停止旧 `trojan` 容器,但不删除; +2. 将旧容器重命名为 `trojan-legacy`; +3. 将新镜像改回 8443/8081/8082,并以容器名 `trojan` 启动; +4. 保持 Nginx upstream 不变; +5. 验证 `www.*` 管理后台、Trojan 客户端、`git.*` Gitea; +6. 执行一次手动证书 dry-run/受控续期测试; +7. 执行 `docker restart trojan` 并验证自动恢复; +8. 在香港先进行宿主机重启验收,观察稳定后再部署美国。 + +现有宿主机 `trojan-container-services.service` 是旧容器的兜底。新镜像通过自身服务管理通过重启验收后,再停止并禁用该宿主机 unit,避免两套启动逻辑长期并存。 + +### 10.4 回滚 + +若任一核心验收失败: + +1. 停止新容器; +2. 保留新镜像的持久化目录和日志用于分析; +3. 将 `trojan-legacy` 恢复原名称并启动; +4. 确认 8443/8081 恢复监听; +5. 验证 `www.*` 和 Trojan 客户端; +6. MariaDB 未升级、旧容器未删除,因此无需数据库回滚。 + +旧容器至少保留一个完整证书续期周期或经两次重启验收后再决定清理。 + +## 11. 验收标准 + +只有以下项目全部通过,才视为可替换: + +- 两台服务器的现有用户、流量、期限和管理员设置完整; +- 旧前端与新前端均能使用原 API; +- 分享链接、二维码、JSON、Clash 订阅均使用公网端口 443; +- `www.hk`、`www.us` 的 HTTP/HTTPS、Trojan 客户端均正常; +- `git.hk`、`git.us` 不受影响; +- 证书状态能显示,续期成功后 Trojan 使用新证书; +- 续期检查不会每天停止管理后台; +- 错误证书、端口冲突或服务启动失败会自动回滚; +- `docker restart` 和宿主机重启后服务自动恢复; +- 新镜像断网启动不依赖 GitHub/CDN; +- 无 Docker Socket 挂载,内部端口不直接暴露公网; +- 回滚旧容器能够在短时间内恢复服务。 + +## 12. 推荐实施顺序 + +1. 增加后端配置模型、`public_port` 兼容逻辑和事务写入; +2. 让服务控制返回真实错误,并完成容器自启动/健康检查; +3. 实现固定证书路径、timer、续期 hook 和证书状态 API; +4. 增加前端“网络与证书”页面,保持旧接口不变; +5. 重写多阶段 Dockerfile,完成本地前后端嵌入和固定依赖; +6. 增加迁移、验证、应用和回滚脚本; +7. 自动测试旧 API、数据迁移、端口回滚、证书 reload 和容器重启; +8. 香港灰度,完成容器/宿主机重启验收; +9. 美国复制部署; +10. 稳定后再讨论 bridge 网络、非 privileged 和 MariaDB 升级。 + +综上,建议采用“兼容镜像 + 配套服务器包 + 分阶段灰度”的方式。它能解决当前重启后服务未拉起、证书续期不完整和端口语义混乱,同时将现网变化控制在 Trojan 容器本身,不牵动 Gitea、Caddy 和公网 Nginx 入口。 diff --git a/迁移实施记录-2026-07-26.md b/迁移实施记录-2026-07-26.md new file mode 100644 index 0000000..9c5dee4 --- /dev/null +++ b/迁移实施记录-2026-07-26.md @@ -0,0 +1,318 @@ +# Trojan、Nginx、Caddy 与 Gitea 迁移实施记录 + +实施日期:2026-07-26 + +## 1. 服务器与域名 + +| 服务器 | Trojan 与管理后台 | Gitea | 宿主机 SSH | Git SSH | +| --- | --- | --- | --- | --- | +| `43.154.35.111` | `www.hk.chermack.top` | `git.hk.chermack.top` | `22` | `2222` | +| `43.159.139.193` | `www.us.chermack.top` | `git.us.chermack.top` | `22` | `2222` | + +## 2. 最终架构 + +```mermaid +flowchart LR + U[客户端] -->|HTTP :80| N[Nginx] + U -->|HTTPS :443 / SNI| N + N -->|www.* HTTP| W[Trojan 管理后台 :8081] + N -->|www.* TLS 透传| T[Trojan :8443] + T -->|非 Trojan 流量回落| W + N -->|git.* HTTP| C1[Caddy :8080] + N -->|git.* TLS 透传| C2[Caddy :9443] + C1 --> G[Gitea :3000] + C2 --> G + U -->|Git SSH :2222| G +``` + +公网 `80/443` 只由宿主机 Nginx 监听。Trojan 客户端和 Trojan 管理后台继续共用 `www.*` 域名,不新增 `panel` 域名:Nginx 将 `www.*` 的 TLS 按 SNI 透传到 Trojan;Trojan 对非 Trojan TLS 流量回落到本机管理后台。 + +## 3. 迁移前备份与检查 + +在变更前检查原容器和配置: + +```bash +docker ps +docker inspect trojan +docker exec trojan cat /usr/local/etc/trojan/config.json +docker exec trojan systemctl cat trojan-web.service +``` + +已创建的备份目录: + +```text +/root/trojan-gitea-migration-backup-20260726-004630 # 香港 +/root/trojan-gitea-migration-backup-20260726-014435 # 美国 +/root/gitea-pre-reset-20260726-012207 # 香港 Gitea 重置前 +``` + +香港服务器另保留了迁移前 Trojan 镜像快照。 + +## 4. Trojan 端口调整 + +容器内 Trojan 配置调整为: + +```json +{ + "local_port": 8443, + "remote_port": 8081 +} +``` + +管理后台 systemd unit 调整为: + +```ini +ExecStart=/usr/local/bin/trojan web --host 127.0.0.1 --port 8081 +``` + +重载和启动命令: + +```bash +docker exec trojan systemctl daemon-reload +docker exec trojan systemctl enable trojan-web +docker exec trojan systemctl restart trojan +docker exec trojan systemctl restart trojan-web +``` + +预期监听:Trojan 为 `0.0.0.0:8443`,管理后台为 `127.0.0.1:8081`。这两个端口不应对公网开放。 + +## 5. Nginx 统一入口 + +安装并启用: + +```bash +apt-get update +apt-get install -y nginx libnginx-mod-stream +systemctl enable --now nginx +nginx -t +``` + +443 使用 `stream` 和 `ssl_preread` 按 SNI 分流。香港示例(美国替换为 `www.us.chermack.top` 与 `git.us.chermack.top`): + +```nginx +map $ssl_preread_server_name $tls_backend { + www.hk.chermack.top 127.0.0.1:8443; + git.hk.chermack.top 127.0.0.1:9443; + default 127.0.0.1:8443; +} +server { + listen 443; + proxy_pass $tls_backend; + ssl_preread on; +} +``` + +80 的 `www.*` 虚拟主机反向代理到 `127.0.0.1:8081`,并将 `/.well-known/acme-challenge/` 转发到 `127.0.0.1:8082`;`git.*` 反向代理到 Caddy `127.0.0.1:8080`。 + +每次配置改动后: + +```bash +nginx -t && systemctl reload nginx +``` + +## 6. Gitea 与 Caddy + +`/opt/gitea` 使用 Docker Compose 运行: + +- Gitea `1.27.0`:`127.0.0.1:3000->3000`,`2222->22`; +- PostgreSQL `16`:仅 Docker 内网; +- Caddy:`127.0.0.1:8080->80`、`127.0.0.1:9443->443`,反向代理到 Gitea。 + +```bash +cd /opt/gitea +docker compose up -d +docker compose ps +``` + +Caddy 管理 `git.*` 的 HTTPS 证书和自动续期;Nginx 对 Git 的 443 流量仅做 TLS 透传。Gitea 初始化时应填写: + +```text +ROOT_URL: https://git.<地区>.chermack.top/ +SSH 域名: git.<地区>.chermack.top +SSH 端口: 2222 +``` + +香港 Gitea 曾按授权清空数据并重新初始化;再次重置前必须先备份 `/opt/gitea/gitea` 和 `/opt/gitea/postgres`。 + +## 7. 美国服务器 SSH 端口处理 + +美国服务器原 OpenSSH 监听 `22`、`2222`、`2223`,与 Gitea Git SSH 的 `2222` 冲突。已保留宿主机 SSH `22`,移除宿主机的 `2222/2223` 监听,并将 `2222` 留给 Gitea。 + +变更前备份: + +```text +/etc/ssh/sshd_config.pre-gitea-20260726-014417 +``` + +安全操作: + +```bash +sshd -t +systemctl reload ssh +ss -ltnp | grep sshd +``` + +修改 SSH 配置时必须保持一条现有的 22 端口会话,验证完成后再断开。 + +## 8. Trojan 证书续期 + +Trojan 的 `www.*` 证书仍由容器内 `acme.sh` 管理;Caddy 只管理 `git.*`。由于宿主机 80 已交给 Nginx,Trojan standalone 验证改用本地 8082: + +```bash +docker exec trojan /root/.acme.sh/acme.sh --issue \ + --server letsencrypt --standalone --httpport 8082 \ + -d www.hk.chermack.top --ecc --force +docker exec trojan systemctl restart trojan +``` + +美国服务器替换为 `www.us.chermack.top`。本次两张 Trojan 证书均已成功签发,有效期为 2026-07-25 至 2026-10-23。后续应确认容器内自动续期任务也携带 `--httpport 8082`。 + +## 9. 重启后不可用:原因与永久修复 + +### 现象 + +香港服务器重启后,Nginx、Trojan 容器、Gitea、Caddy 都自动启动,但: + +```bash +docker exec trojan systemctl is-active trojan # inactive +docker exec trojan systemctl is-active trojan-web # inactive +``` + +此时没有 `8443` 和 `8081` 监听,因此 `www.hk.chermack.top` 不可用。 + +### 临时恢复 + +```bash +docker exec trojan systemctl start trojan +docker exec trojan systemctl start trojan-web +ss -ltnp | grep -E ':(8443|8081)' +``` + +### 永久兜底 + +容器内 unit 虽为 `enabled`,但容器启动时没有拉起服务。因此在香港宿主机新增并启用:`/etc/systemd/system/trojan-container-services.service`。 + +```ini +[Unit] +Description=Start Trojan services inside the trojan container +Requires=docker.service +After=docker.service network-online.target +Wants=network-online.target + +[Service] +Type=oneshot +ExecStart=/usr/bin/docker exec trojan /bin/systemctl start trojan.service +ExecStartPost=/usr/bin/docker exec trojan /bin/systemctl start trojan-web.service +RemainAfterExit=yes + +[Install] +WantedBy=multi-user.target +``` + +启用并验证: + +```bash +systemctl daemon-reload +systemctl enable trojan-container-services.service +systemctl start trojan-container-services.service +systemctl is-enabled trojan-container-services.service +systemctl is-active trojan-container-services.service +docker exec trojan systemctl is-active trojan trojan-web +``` + +本次已验证该兜底服务为 `enabled`、`active`;Trojan 和 `trojan-web` 均为 `active`,且外部 HTTPS 访问 `https://www.hk.chermack.top/` 成功。 + +美国服务器也已部署同名的宿主机兜底 unit,并完成验证:`trojan-container-services.service` 为 `enabled`、`active`;Trojan 监听 `0.0.0.0:8443`,管理后台监听 `127.0.0.1:8081`;`https://www.us.chermack.top/` 与 `https://git.us.chermack.top/` 外部访问均成功。 + +## 10. 日常检查与故障排查 + +```bash +# 统一入口、监听与容器 +nginx -t +ss -ltnp | grep -E ':(80|443|8080|8081|8443|9443|2222)' +docker ps + +# Trojan 及后台 +docker exec trojan systemctl is-active trojan +docker exec trojan systemctl is-active trojan-web + +# Web 链路 +curl -kI https://www.hk.chermack.top/ +curl -kI https://git.hk.chermack.top/ +curl -kI https://www.us.chermack.top/ +curl -kI https://git.us.chermack.top/ +``` + +`www.*` 不可访问时,先检查 `trojan-web` 和 `8081`;Trojan 客户端也异常时,再检查 `trojan` 和 `8443`: + +```bash +docker exec trojan systemctl restart trojan +docker exec trojan systemctl restart trojan-web +docker exec trojan journalctl -u trojan -u trojan-web --no-pager -n 200 +docker logs --tail 200 trojan +``` + +## 11. 美国服务器 Gitea 初始化记录 + +初始化页面曾提示数据库密码无效。服务器内核验结果如下: + +- `gitea` 容器的 `GITEA__database__*` 与 `gitea-db` 容器的 `POSTGRES_*` 用户、数据库和密码完全一致; +- 使用 Gitea 容器实际注入的密码连接 PostgreSQL 成功; +- `/data/gitea/conf/app.ini` 中落盘的密码也与 PostgreSQL 一致; +- 初始化页面已预填密码,错误由浏览器密码管理器覆盖表单密码或提交了其他密码造成,不是数据库密码失效。 + +为避免表单再次覆盖,已通过 Gitea CLI 使用明确配置文件完成初始化: + +```bash +gitea --work-path /data/gitea \ + --config /data/gitea/conf/app.ini migrate +``` + +随后创建临时管理员 `admin`,具备管理员权限并设置为首次登录必须修改密码;`INSTALL_LOCK` 已设为 `true`。随机初始密码只保存在美国服务器: + +```text +/root/gitea-initial-admin.txt +``` + +文件权限为 `0600 root:root`。登录并修改密码后应删除该凭据文件。 + +初始化前备份目录: + +```text +/root/gitea-us-pre-init-20260726-041434 +``` + +最终验证结果:数据库中存在一个管理员且 `must_change_password=true`,Gitea 日志无初始化错误,`https://git.us.chermack.top/user/login` 从服务器内外访问均成功。 + +## 12. 美国管理后台周期性退出的根因(2026-07-26) + +故障现象:`https://www.us.chermack.top/` 失效,Nginx 80/443 和 Trojan 8443 正常,但 `trojan-web` 为 `failed`,`127.0.0.1:8081` 无监听。 + +现场排查排除了磁盘耗尽、内存 OOM、Docker 容器重启、Nginx/Caddy 和本次 ACME 定时任务。关键证据: + +```text +宿主机 cgroup:cgroup2fs +容器基础系统:CentOS 7 / systemd 219 +容器 systemctl:systemctl.py 1.5.7113(Python 替代实现) +容器启动日志:Failed to allocate manager object, freezing +容器内状态:多个 trojan/web 僵尸进程,无 journal 日志 +``` + +当前镜像的 PID 1 systemd 无法在宿主机 cgroup v2 环境中正常建立 manager;`/usr/bin/systemctl` 实际是一次性 Python 替代脚本,不是持续运行的服务管理器。因此 unit 中的 `Restart=on-failure` 不能可靠监督进程。宿主机现有 `trojan-container-services.service` 也是 `Type=oneshot`,只在开机时执行一次启动,运行中 Web 退出后不会再次拉起。 + +以前台方式直接运行以下命令持续 15 秒无异常,证明 Go Web 程序、数据库和 8081 端口本身健康: + +```bash +docker exec trojan /usr/local/bin/trojan web \ + --host 127.0.0.1 --port 8081 +``` + +探针启动的进程随后继续运行,8081、本地 `/auth/check` 和公网 HTTPS 均恢复 200;但替代 `systemctl` 仍错误显示 `failed`,再次证明问题位于服务管理层。 + +临时永久化建议:由宿主机真实 systemd 使用前台 `docker exec` 直接监督 `trojan-web`(以及 Trojan core),设置 `Restart=always` 并将 stdout/stderr 写入宿主机 journal。最终解决方案是使用已规划的新镜像,移除 CentOS 7、冻结的容器 systemd 和运行时下载的 systemctl.py。 + +## 13. 后续维护事项 + +1. 两台服务器的 `trojan-mariadb` 仍将 `3306` 映射到公网;为避免额外中断,迁移中未改动。确认无外部依赖后,应限制为 `127.0.0.1:3306` 或移除映射。 +2. `www.*` 与 `git.*` 使用不同的证书管理者:前者为 Trojan 容器内 acme.sh,后者为 Caddy。 +3. 两台服务器均已部署 `trojan-container-services.service`。重启服务器后,优先检查该服务及容器内两个 Trojan 服务状态。 diff --git a/部署架构建议.md b/部署架构建议.md index 6d398b8..26ffb40 100644 --- a/部署架构建议.md +++ b/部署架构建议.md @@ -10,6 +10,13 @@ - 容器 `trojan`:镜像 `jrohy/trojan`,已运行约两年,当前已连续运行七周;`docker ps` 没有显示端口映射。这与项目 README 的 `--net=host` 启动方式一致,意味着它很可能直接使用宿主机网络,并占用 Trojan 的 `443` 与管理 Web 的 `80`。 - 容器 `trojan-mariadb`:镜像 `mariadb:10.2`,当前映射 `0.0.0.0:3306->3306` 和 `[::]:3306->3306`;即 MySQL/MariaDB 已对全网开放。 +### 2026-07-26 服务器只读核查结果 + +- `trojan` 容器实际使用 Docker `host` 网络,没有持久化挂载卷。配置和证书只在这个已运行两年的容器内;迁移前必须导出备份,不能直接删除或重建。 +- `443` 由 Trojan 监听,`80` 由容器内的 `/usr/local/bin/trojan web` 监听。 +- Trojan 配置已确认:`local_port=443`,`remote_addr=127.0.0.1`,`remote_port=80`,SNI 为 `www.hk.chermack.top`。这表示非 Trojan 的 HTTPS 请求会由 Trojan 回落到管理后台。 +- `trojan-mariadb` 的 3306 当前对 IPv4/IPv6 公网开放,后续应收紧到内部网络或回环地址。 + 本文后续均直接使用该真实 Trojan 域名:**`www.hk.chermack.top`**。 ## 1. 结论 @@ -48,6 +55,21 @@ flowchart TB 所有域名的 A/AAAA 记录都指向这台服务器。Trojan 客户端应继续使用 `www.hk.chermack.top` 作为 SNI 和服务器地址。 +### 是否必须新增 `panel.hk.chermack.top`? + +**不一定。**项目源码中附带的 systemd 服务执行的是 `trojan web`,而该命令默认监听 `0.0.0.0:80`、不启用 TLS;Trojan 服务则通常监听 `443`。这只是源码默认行为,典型原始部署会按端口区分: + +```text +http://www.hk.chermack.top:80 -> Trojan 管理后台(HTTP) +www.hk.chermack.top:443 -> Trojan TLS 服务 +``` + +服务器已核实 `http://www.hk.chermack.top` 与 `https://www.hk.chermack.top` 都能访问后台的原因:Trojan 已配置将非 Trojan 的 HTTPS 请求回落到 `127.0.0.1:80`。因此当前同域名 HTTPS 后台是已验证的 Trojan 回落链路,不需要新增 `panel.hk.chermack.top`。 + +迁移后应保持这一关系:Nginx 仅按 SNI 将 `www.hk.chermack.top` 的所有 443 TLS 流量转给 Trojan;Trojan 负责识别协议,并将普通 HTTPS 后台请求回落到迁移后的后台端口 `127.0.0.1:8081`。Nginx 不应尝试把相同的 `www.hk.chermack.top` SNI 拆分到 Caddy 与 Trojan。 + +`panel.hk.chermack.top` 仍可作为将来独立管理后台的可选项,但不是本服务器部署 Gitea 的必要条件。 + ## 3. 前置条件与注意事项 1. 所有服务应只向 Docker 内网或 `127.0.0.1` 发布端口;不要把 Trojan 的新端口、Caddy 的 `8080/9443` 直接暴露到公网。 @@ -188,3 +210,132 @@ app.hk.chermack.top { ## 9. 后续执行前需要确认的信息 实施时需要根据真实状态补全:当前 Trojan 容器的 `docker inspect` 输出、实际 `config.json`、MariaDB 容器/卷、当前占用 80/443 的进程、服务器系统发行版、可用域名及 DNS 提供商。 + +## 10. 与 Gitea 共存:`git.hk.chermack.top` + +### 能否完全不改变当前 Trojan Docker 服务? + +**不能同时满足“完全不变”与“通过 `https://git.hk.chermack.top` 对外访问”这两个条件。** + +目前 `trojan` 容器没有端口映射,结合原项目的启动方式可推断其使用宿主机网络,并直接占用宿主机 `80/443`。Gitea 可以新增容器并运行在 `127.0.0.1:3000`,SSH Git 可以运行在 `2222`,但在 Trojan 持续独占公网 `80/443` 时: + +- 不能启动 Nginx/Caddy 绑定公网 `80/443`; +- 不能让 Caddy 为 `git.hk.chermack.top` 完成 HTTP-01 校验并提供 HTTPS; +- Trojan 的 `plain_http_response` 不是 HTTP 反向代理,不能把 Gitea 的 Web 请求转发出去。 + +因此分为两个阶段: + +1. **不改变现状的准备阶段**:仅部署 Gitea 与 PostgreSQL,不发布 Web 域名;本机可通过 `curl http://127.0.0.1:3000` 验证,公网可暂时使用 SSH Git 端口 `2222`。 +2. **正式上线阶段(必要变更)**:按本文第 4~7 节让 Trojan 改至本机 `8443`、管理后台改至本机 `8081`,让 Nginx 接管公网 `80/443`,再由 Caddy 为 `git.hk.chermack.top` 托管 HTTPS。 + +现有容器中的 Trojan 和 MariaDB 数据不会被删除;但 Trojan 容器需要在完成备份后重建或调整启动配置,才能释放宿主机 `80/443`。这是端口绑定的硬性限制,不是 Gitea 配置能够规避的问题。 + +### 目标流量关系 + +```mermaid +flowchart LR + U["用户 / Git 客户端"] -->|"HTTPS :443\ngit.hk.chermack.top"| N["Nginx stream"] + U -->|"Trojan 或后台 HTTPS :443\nwww.hk.chermack.top"| N + N -->|"SNI = git.hk.chermack.top"| C["共享 Caddy"] + N -->|"SNI = www.hk.chermack.top"| T["Trojan :8443"] + T -->|"非 Trojan TLS 回落"| Pnl["管理后台 :8081"] + C -->|"HTTP"| G["Gitea :3000"] + U -->|"SSH Git :2222"| G + G --> P["PostgreSQL\n仅 Docker 网络"] +``` + +### DNS 与端口 + +新增 DNS 记录: + +```text +类型:A(若已启用 IPv6,则另加 AAAA) +主机记录:git +记录值:本服务器公网 IP +结果:git.hk.chermack.top +``` + +开放公网 TCP `2222` 供 SSH Git 克隆/推送;公网 `80/443` 仍只由统一 Nginx 入口占用。不要开放 PostgreSQL 的 `5432`,也不需要公开 Gitea 的 `3000`。 + +### Gitea Compose(不再单独启动 Caddy) + +原 Gitea 部署方案中的独立 `gitea-caddy` 不应再映射宿主机 `80:80` / `443:443`,因为这些端口已由统一入口占用。Gitea 目录建议为 `/opt/gitea`,其 `docker-compose.yml` 可只保留数据库与 Gitea: + +```yaml +services: + db: + image: postgres:16 + container_name: gitea-db + restart: unless-stopped + environment: + POSTGRES_USER: gitea + POSTGRES_PASSWORD: CHANGE_TO_A_LONG_RANDOM_PASSWORD + POSTGRES_DB: gitea + volumes: + - ./postgres:/var/lib/postgresql/data + networks: [gitea] + + gitea: + image: docker.gitea.com/gitea:CHANGE_TO_A_VALIDATED_VERSION + container_name: gitea + restart: unless-stopped + depends_on: [db] + environment: + USER_UID: "1000" + USER_GID: "1000" + GITEA__database__DB_TYPE: postgres + GITEA__database__HOST: db:5432 + GITEA__database__NAME: gitea + GITEA__database__USER: gitea + GITEA__database__PASSWD: CHANGE_TO_A_LONG_RANDOM_PASSWORD + GITEA__server__DOMAIN: git.hk.chermack.top + GITEA__server__ROOT_URL: https://git.hk.chermack.top/ + GITEA__server__PROTOCOL: http + GITEA__server__SSH_DOMAIN: git.hk.chermack.top + GITEA__server__SSH_PORT: "2222" + ports: + - "127.0.0.1:3000:3000" + - "2222:22" + volumes: + - ./gitea:/data + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + networks: [gitea] + +networks: + gitea: +``` + +示例固定 Gitea 镜像版本,避免 `latest` 在重建时产生不可预期的大版本升级。实际部署前应选定并验证当时支持的稳定版本;升级前先备份 `./gitea` 与 PostgreSQL。 + +### 共享 Caddy 新增站点 + +在第 6 节的**共享** Caddyfile 中新增: + +```caddyfile +git.hk.chermack.top { + reverse_proxy 127.0.0.1:3000 +} +``` + +当 Nginx 的 `443` stream 根据 SNI 将 `git.hk.chermack.top` 转到 Caddy 后,Caddy 会自动申请和续期该域名证书,再将 HTTP 请求反向代理给 Gitea。Gitea 生成的网页链接与 clone 地址则由 `ROOT_URL` 和 `SSH_*` 配置保持为正确的公网地址。 + +对本服务器,最终的 443 分流应是:`www.hk.chermack.top` 全部转给 Trojan,`git.hk.chermack.top` 转给 Caddy。迁移后的 Trojan 监听 `127.0.0.1:8443`,其 `remote_port` 改为 `8081`;Caddy 对外通过 Nginx 使用 `127.0.0.1:8080`(HTTP)和 `127.0.0.1:9443`(HTTPS)。 + +Nginx 的 80 端口按域名转发:`www.hk.chermack.top` 到 `127.0.0.1:8081`,`git.hk.chermack.top` 到 Caddy 的 `127.0.0.1:8080`。Trojan 证书续期不能再依赖独占 80 的 acme standalone 方式;正式迁移时应改用 Nginx 可服务的 ACME webroot 验证,或 DNS-01 验证。 + +### 验证清单 + +```bash +# Gitea 准备阶段:仅确认服务已运行(在服务器执行) +cd /opt/gitea +docker compose up -d +curl -I http://127.0.0.1:3000 + +# 正式切换统一入口之后 +curl -I https://git.hk.chermack.top +ssh -T -p 2222 git@git.hk.chermack.top +git clone ssh://git@git.hk.chermack.top:2222/<组织>/<仓库>.git +``` + +`ssh -T` 的首次连接会要求确认主机指纹;应通过服务器控制台或管理员提供的指纹核验,不应盲目接受来源不明的密钥变更。