342 lines
16 KiB
Markdown
342 lines
16 KiB
Markdown
# Trojan、Nginx 与 Caddy 共用 80/443 的部署方案
|
||
|
||
> 适用对象:已按项目 README 的 Docker 方式运行 Trojan 管理程序,希望同一台服务器继续部署多个 HTTPS 网站或 Web 服务。
|
||
>
|
||
> 本文只给出架构和操作建议,不直接修改已经运行的服务器。
|
||
|
||
## 已确认的现状
|
||
|
||
- Trojan 对外域名:`www.hk.chermack.top`。
|
||
- 容器 `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. 结论
|
||
|
||
不能让 Trojan、Nginx 或 Caddy 同时直接监听宿主机的 `80`、`443`:同一 IP:端口只能被一个进程绑定。
|
||
|
||
由于 Trojan 使用的并非 HTTP/HTTPS Web 请求,而是基于 TLS 的代理协议,普通的 `reverse_proxy` 规则无法把 `/`、`/api` 一类路径转发给它。要同时使用 Trojan 和多个网站,应按 TLS 握手中的 **SNI(访问域名)** 在四层分流:
|
||
|
||
```mermaid
|
||
flowchart TB
|
||
Internet["Internet"] --> N80["Nginx :80\nHTTP 转发"]
|
||
Internet --> N443["Nginx stream :443\n按 TLS SNI 分流"]
|
||
N80 --> Caddy["Caddy :8080 / :9443\n自动申请、续期证书\n托管网站"]
|
||
N443 -->|"www.hk.chermack.top"| Trojan["Trojan 容器 :8443\nTLS 直通"]
|
||
N443 -->|"app.example.com\nwww.example.com"| Caddy
|
||
Caddy --> App1["网站 / 应用 1"]
|
||
Caddy --> App2["网站 / 应用 2"]
|
||
```
|
||
|
||
推荐职责划分:
|
||
|
||
- **Nginx**:唯一占用公网 `80` 和 `443`;`443` 使用 `stream + ssl_preread` 按域名转发 TLS 流量,自己不终止 TLS。
|
||
- **Caddy**:监听本机 `8080`/`9443`,为普通网站自动申请和续期 Let's Encrypt/ZeroSSL 证书,并反向代理到各个 Web 应用。
|
||
- **Trojan**:改为仅监听宿主机本地端口(示例使用 `8443`),由 Nginx 按 `www.hk.chermack.top` 的 SNI 转发。它仍使用自己的 Trojan TLS 证书。
|
||
|
||
## 2. 域名规划
|
||
|
||
应使用不同的子域名,不要让 Trojan 和网站共用同一个域名:
|
||
|
||
|用途|示例域名|TLS 终止位置|
|
||
|---|---|---|
|
||
|Trojan 服务|`www.hk.chermack.top`|Trojan|
|
||
|Trojan 管理后台|建议新增 `panel.hk.chermack.top`|Caddy|
|
||
|普通网站|例如 `site.hk.chermack.top`|Caddy|
|
||
|其他应用|例如 `app.hk.chermack.top`|Caddy|
|
||
|
||
所有域名的 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` 直接暴露到公网。
|
||
2. Nginx 需要带有 `stream` 和 `ssl_preread` 模块。Ubuntu/Debian 通常需要安装 `libnginx-mod-stream`;确认方式:`nginx -V 2>&1 | grep stream`。
|
||
3. 防火墙、安全组只开放公网 TCP `80`、`443`(以及 SSH 管理端口);关闭原先直接暴露的 Trojan 端口。
|
||
4. 当前 `trojan-mariadb` 的 `3306` 正暴露于 IPv4/IPv6 公网。除非确有远程数据库访问需求,切换时应改为仅容器网络或 `127.0.0.1:3306`,并从安全组和防火墙移除公网 3306。
|
||
5. Trojan 的 TLS 证书仍应有效且包含 `www.hk.chermack.top`。Caddy 的自动 HTTPS 只负责它所托管的网站,**不会自动替 Trojan 申请或安装证书**。
|
||
5. Caddy 使用 HTTP-01 验证时,Nginx 的 `80` 必须完整转发到 Caddy。Caddy 成功续期不需要人工定时执行证书申请命令。
|
||
|
||
## 4. 修改现有 Trojan Docker 端口
|
||
|
||
README 示例使用 `--net=host`,因此 Trojan 和管理 Web 服务会直接占用宿主机端口。建议改为显式端口映射,或在仍使用 host 网络时把 Trojan 和 Web 的监听端口改为非公网端口。
|
||
|
||
### 建议的目标端口
|
||
|
||
|服务|容器/程序内端口|宿主机绑定|公网暴露|
|
||
|---|---:|---|---|
|
||
|Trojan|改为 `8443`|`127.0.0.1:8443`|否,由 Nginx 443 转发|
|
||
|Trojan Web 管理后台|改为 `8081`|`127.0.0.1:8081`|否,由 Caddy 代理|
|
||
|Caddy HTTP|`8080`|`127.0.0.1:8080`|否,由 Nginx 80 转发|
|
||
|Caddy HTTPS|`8443`|`127.0.0.1:9443`(避免与 Trojan 冲突)|否,由 Nginx 443 转发|
|
||
|
||
上表特意将 Caddy 的宿主机 HTTPS 端口设为 `9443`,避免和 Trojan 的 `8443` 冲突。实际端口可以调整,但必须保证唯一。
|
||
|
||
### Trojan 配置原则
|
||
|
||
在 Trojan 的 `/usr/local/etc/trojan/config.json` 中,把 `local_port` 从 `443` 改为 `8443`;管理程序的 Web 服务以类似方式改为 `8081`(例如 systemd 服务执行 `trojan web -p 8081`)。
|
||
|
||
如果容器仍采用 `--net=host`,以上端口是宿主机端口。若改为 Docker bridge 网络,推荐使用仅回环地址的映射,例如:
|
||
|
||
```bash
|
||
-p 127.0.0.1:8443:8443
|
||
-p 127.0.0.1:8081:8081
|
||
```
|
||
|
||
重建容器前,应先备份:
|
||
|
||
```bash
|
||
docker inspect trojan > trojan.inspect.backup.json
|
||
docker cp trojan:/usr/local/etc/trojan/config.json ./trojan-config.backup.json
|
||
```
|
||
|
||
不要在未确认持久化目录和数据库位置前删除旧容器。README 所用方案还会单独运行 MariaDB,必须保留其数据卷。
|
||
|
||
## 5. Nginx 四层 SNI 分流示例
|
||
|
||
以下配置应放在 Nginx 主配置可加载的位置;`stream` 块通常不能放入普通的 `server {}` HTTP 配置文件。域名和端口必须替换为实际值。
|
||
|
||
```nginx
|
||
# /etc/nginx/nginx.conf(顶层 stream 块)
|
||
stream {
|
||
map $ssl_preread_server_name $tls_upstream {
|
||
www.hk.chermack.top trojan_backend;
|
||
default caddy_https_backend;
|
||
}
|
||
|
||
upstream trojan_backend {
|
||
server 127.0.0.1:8443;
|
||
}
|
||
|
||
upstream caddy_https_backend {
|
||
server 127.0.0.1:9443;
|
||
}
|
||
|
||
server {
|
||
listen 443 reuseport;
|
||
proxy_pass $tls_upstream;
|
||
ssl_preread on;
|
||
proxy_timeout 1h;
|
||
proxy_connect_timeout 5s;
|
||
}
|
||
}
|
||
|
||
http {
|
||
server {
|
||
listen 80;
|
||
server_name _;
|
||
|
||
location / {
|
||
proxy_pass http://127.0.0.1:8080;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
proxy_set_header X-Forwarded-Proto http;
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
此配置的关键是:Nginx 在 `443` 上只读取 ClientHello 的 SNI,不解密 TLS;因此 Trojan 和 Caddy 都能拿到自己完整的 TLS 握手和证书。
|
||
|
||
若客户端未发送 SNI,会被送到默认的 Caddy 后端;Trojan 客户端必须明确配置 SNI 为 `www.hk.chermack.top`。
|
||
|
||
## 6. Caddy 自动 HTTPS 示例
|
||
|
||
在 Caddyfile 设置内部监听端口。Caddy 仍会使用域名来签发和管理证书,只是外部 `80/443` 由 Nginx 转发到它。
|
||
|
||
```caddyfile
|
||
{
|
||
http_port 8080
|
||
https_port 9443
|
||
email ops@example.com
|
||
}
|
||
|
||
panel.hk.chermack.top {
|
||
reverse_proxy 127.0.0.1:8081
|
||
}
|
||
|
||
site.hk.chermack.top {
|
||
reverse_proxy 127.0.0.1:3000
|
||
}
|
||
|
||
app.hk.chermack.top {
|
||
reverse_proxy 127.0.0.1:9000
|
||
}
|
||
```
|
||
|
||
其中 `panel.hk.chermack.top` 反向代理 Trojan 管理后台;其他站点各自指向对应容器或本机应用。Caddy 会保存证书与续期状态,容器部署时必须挂载其数据目录,例如 `/data` 与 `/config`,否则重建容器会丢失 ACME 账户和证书缓存。
|
||
|
||
## 7. 建议实施顺序
|
||
|
||
1. 记录现有容器、端口、配置、数据库卷和证书路径,并完成备份。尤其保存 `docker inspect trojan`、`docker inspect trojan-mariadb` 和 MariaDB 数据卷信息;`trojan` 没有端口映射,变更前必须确认其是否为 `--network host`。
|
||
2. 为 `trojan`、`panel`、网站分别创建 DNS 记录。
|
||
3. 安装并验证 Nginx 的 `stream` 模块,但暂不切走 80/443。
|
||
4. 启动 Caddy 于 `127.0.0.1:8080` 和 `127.0.0.1:9443`,先验证内部反向代理可用。
|
||
5. 将 Trojan 改至 `127.0.0.1:8443`,将管理后台改至 `127.0.0.1:8081`,确认直连本机端口均正常。
|
||
6. 配置 Nginx `80` 转 Caddy、`443` SNI 分流,执行 `nginx -t`。
|
||
7. 在维护窗口切换 Nginx 到公网 `80/443`,验证网站 HTTPS、Caddy 证书签发、Trojan 客户端连接、管理后台登录和订阅链接。
|
||
8. 最后收紧 Docker 端口映射与防火墙,只留 `80/443` 公开;将当前 MariaDB 的公网 `3306` 映射关闭或至少限制为 `127.0.0.1`。
|
||
|
||
## 8. 不建议的方案
|
||
|
||
- 让 Caddy 或 Nginx 直接以 HTTP `reverse_proxy` 转发 Trojan:协议不匹配,不能工作。
|
||
- Trojan 与网站共用同一个域名且共用 443:SNI 无法区分目标,除非引入更复杂的协议伪装/回落设计;不适合当前项目的常规部署。
|
||
- 将 Trojan 改端口后直接对公网开放该端口:虽能运行,但无法达到只使用 80/443 的目标,也增加暴露面。
|
||
- 认为 Caddy 能替 Trojan 自动处理证书:Caddy 只能为它实际终止 TLS 的连接管理证书;本方案中 Trojan 的 TLS 是端到端直通。
|
||
|
||
## 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` 的首次连接会要求确认主机指纹;应通过服务器控制台或管理员提供的指纹核验,不应盲目接受来源不明的密钥变更。
|