# proxy-ng origin 加固方案 — 审阅版
日期:2026-05-26
状态:已执行,2026-05-26 有边界调整
适用范围:`proxy.api.lilifamily.com` 公开入口、`catproxy.lilifamily.com` 公开直连主站入口、`origin.proxy.api.lilifamily.com` 受限回源入口、`*.proxy.api.lilifamily.com` proxy-ng 节点扩展
> 2026-05-26 决策变更:`catproxy.lilifamily.com` 不再做 IP 白名单限制,作为 3c4g 公开直连主站域名和回滚入口;只有 `origin.proxy.api.lilifamily.com` 保持来源 IP 白名单和 `X-Proxy-Ng-Token` 校验。本文中早期关于 `catproxy` 受限访问的草案描述已被此决策覆盖,当前运行事实以 `ops/remote/us-racknerd-0526-sub2api/` 为准。
## 1. 结论
当前 JPpro 入口中转已经可以用于稳定性测试,但还不是生产加固架构。后续不依赖 Cloudflare Load Balancing;公开入口用普通 DNS 记录指向 proxy-ng 节点,防攻击和主站隐藏由 nginx、防火墙、origin 白名单和 token 完成。
当前已执行链路是:
```mermaid
flowchart LR
U[用户] -->|HTTPS| P[公开入口
proxy.api.lilifamily.com]
P -->|HTTPS| J[JPpro nginx
jp.proxy.api.lilifamily.com]
J -->|HTTPS + 节点 Token| O[3c4g origin nginx
origin.proxy.api.lilifamily.com]
O -->|127.0.0.1:8080| S[Sub2API]
C[直连用户] -->|HTTPS| D[3c4g nginx
catproxy.lilifamily.com]
D -->|127.0.0.1:8080| S
```
推荐生产加固链路是:
```mermaid
flowchart LR
U[用户] -->|HTTPS| D[公开入口
proxy.api.lilifamily.com
普通多 A / 手工切换]
D --> J[JPpro nginx
jp.proxy.api.lilifamily.com]
D --> N[其它 proxy-ng 节点
*.proxy.api.lilifamily.com]
J -->|HTTPS + 节点 Token| O[3c4g origin nginx
origin.proxy.api.lilifamily.com]
N -->|HTTPS + 节点 Token| O
O -->|本机回环| S[Sub2API :8080]
C[普通公网用户] -.->|403 / 拒绝| O
X[未知服务器] -.->|IP 不在白名单| O
```
前置阶段先做域名迁移和入口角色调整:
1. 公开服务入口保留为 `proxy.api.lilifamily.com`,但它不再指向 3c4g,而是指向 proxy-ng 节点。
2. 3c4g 原直连入口从 `proxy.api.lilifamily.com` 改为 `catproxy.lilifamily.com`,作为公开直连主站域名和回滚入口。
3. DNS 增加 `origin.proxy.api.lilifamily.com` A 记录,指向 3c4g,只给 proxy-ng 回源使用。
4. 其它 proxy-ng 节点统一使用 `.proxy.api.lilifamily.com` 命名。
第一阶段加固只建议做三件事:
1. 在 3c4g 增加独立 origin 入口:`origin.proxy.api.lilifamily.com`。
2. origin 入口只允许 JPpro IP 访问,并校验 `X-Proxy-Ng-Token`。
3. JPpro 从回源公网主域名改为回源 origin 入口。
4. `proxy.api.lilifamily.com` 的 A 记录只指向 proxy-ng 节点,不再暴露 3c4g。
这三项完成后,proxy-ng 才从“能用的公网反代”升级成“信任边界清楚的入口中转”。
## 2. 当前已验证事实
| 项目 | 当前事实 |
|---|---|
| JPpro 服务器 | `个人-JPpro-akile-0504-1c1g` |
| JPpro 公网 IP | `151.242.164.72` |
| JPpro 入口域名 | `jp.proxy.api.lilifamily.com` |
| JPpro 当前职责 | 只运行 nginx proxy-ng,不运行 Sub2API、PostgreSQL、Redis、Caddy 或账号出口代理 |
| JPpro 当前回源 | 测试期可临时回源 `https://catproxy.lilifamily.com`,加固后必须改为 `https://origin.proxy.api.lilifamily.com` |
| 3c4g 服务器 | `个人-US-racknerd-0526-3c4g` |
| 公开服务入口域名 | `proxy.api.lilifamily.com`,目标是普通多 A 指向 proxy-ng 节点 |
| 3c4g 公开直连主站域名 | `catproxy.lilifamily.com`,不做 IP 白名单限制 |
| 3c4g origin 域名 | `origin.proxy.api.lilifamily.com`,只给 proxy-ng 回源 |
| 3c4g 当前职责 | nginx 反代到本机 Sub2API `127.0.0.1:8080` |
| 当前风险边界 | 直接回源公网主域名,尚未做独立 origin、后端来源 IP 白名单、`X-Proxy-Ng-Token` 校验 |
## 2.1 域名命名与 DNS 记录
本方案按以下域名边界规划:
| 域名 | DNS 记录 | 角色 | 说明 |
|---|---|---|---|
| `proxy.api.lilifamily.com` | 多个 A -> proxy-ng 节点 IP | 公开服务入口 | 不用 Cloudflare LB;普通 DNS 多 A 或手工切换 |
| `catproxy.lilifamily.com` | A -> `216.45.59.243` | 3c4g 公开直连主站入口 | 可直接访问主站,也可作为 proxy-ng 故障时的回滚入口 |
| `origin.proxy.api.lilifamily.com` | A -> `216.45.59.243` | 3c4g 回源入口 | 只给 proxy-ng 节点访问,必须加 IP 白名单和 token |
| `jp.proxy.api.lilifamily.com` | A -> `151.242.164.72` | JPpro proxy-ng 节点 | 当前第一台入口中转节点 |
| `*.proxy.api.lilifamily.com` | 每个节点独立 A/AAAA | 后续 proxy-ng 节点命名空间 | 例如 `us.proxy.api.lilifamily.com`、`sg.proxy.api.lilifamily.com` |
约束:
- `proxy.api.lilifamily.com` 是用户看到的正式入口,但它只指向 proxy-ng 节点,不直接指向 3c4g。
- `catproxy.lilifamily.com` 是独立公开直连入口;proxy-ng 回源不使用它,也不应在 `proxy.api.lilifamily.com` 链路中把它暴露到 `Location`、`Host`、页面链接或错误页中。
- `*.proxy.api.lilifamily.com` 在本文表示节点命名规则,不等于必须使用一条 wildcard A 记录。
- 多个节点如果 IP 不同,应为每个节点创建独立 A/AAAA 记录;`proxy.api.lilifamily.com` 可以配置多个 A 记录,但这是普通 DNS 轮询,不具备健康检查和按网络质量调度能力。
- `origin.proxy.api.lilifamily.com` 即使有公网 DNS,也不应作为用户入口;访问控制必须在 nginx 和防火墙层生效。
- `origin` 默认限制公网访问,证书签发不应依赖长期开放 HTTP-01;优先使用 DNS-01,或在签发/续期窗口临时开放并立即恢复限制。`catproxy` 公开访问,可使用常规 HTTP-01。
当前配置留痕:
| 文件 | 说明 |
|---|---|
| `ops/remote/jppro-sub2api/README.md` | JPpro proxy-ng 部署记录 |
| `ops/remote/jppro-sub2api/current/nginx-jp-proxy-sub2api.conf` | JPpro 当前 nginx 配置副本 |
| `ops/remote/us-racknerd-0526-sub2api/current/nginx-sub2api.conf` | 3c4g 当前 nginx 主入口配置副本 |
| `docs/memorys/2026-05-25-proxy-ng职责边界.md` | proxy-ng 不承担账号出口代理的边界记录 |
| `proxy-ng/README.md` | 多节点 proxy-ng 方案说明 |
| `proxy-ng/nginx/proxy-node.conf.template` | proxy-ng 节点 nginx 模板 |
| `proxy-ng/nginx/backend-gateway.conf.template` | 后端入口网关模板 |
## 3. 目标和非目标
### 目标
- 把用户公开入口和 proxy-ng 回源入口分离。
- 让 3c4g 后端能识别“这是受控 proxy-ng 节点发来的请求”。
- 防止普通公网用户绕过 proxy-ng 直接访问 origin。
- 防止客户端伪造 `X-Proxy-Ng-*` 或 `X-Forwarded-*` 头影响日志和安全判断。
- 公开入口 `proxy.api.lilifamily.com` 只解析到 proxy-ng 节点,避免 3c4g 主站直接暴露。
- 保留 `catproxy.lilifamily.com` 作为公开直连主站域名和紧急回滚入口。
- 为后续新增 proxy-ng 节点提供标准流程。
### 非目标
- 不把 JPpro 改造成账号出口代理。
- 不在 proxy-ng 节点部署 Clash、sing-box、Xray、SOCKS、mixed 等代理服务。
- 不改 Sub2API 业务代码作为第一阶段方案。
- 不在仓库提交真实 token、`.env`、API key、数据库密码。
- 不立即强制所有用户流量都走 JPpro。
- 不立即建设复杂的多地域调度系统。
## 4. 设计项解释和实施方案
### 4.1 独立 origin 入口
#### 作用
独立 origin 用来区分两类入口:
| 入口 | 面向对象 | 是否公开给用户 |
|---|---|---|
| `proxy.api.lilifamily.com` | 用户公开访问,解析到 proxy-ng 节点 | 是 |
| `catproxy.lilifamily.com` | 运维直连、临时回滚 | 否 |
| `origin.proxy.api.lilifamily.com` | proxy-ng 节点回源 | 否 |
如果 JPpro 继续回源 `https://catproxy.lilifamily.com`,那么 `catproxy` 就会被放进常规服务链路。它能用于短期迁移,但不能作为生产回源目标。生产回源必须使用 `origin.proxy.api.lilifamily.com`,并通过 IP 白名单和 token 限制访问。
独立 origin 的意义是:回源链路可以单独配置 IP 白名单、token 校验、日志格式和限流策略,不影响用户公开入口。
#### 实施方案
DNS 增加:
```text
proxy.api.lilifamily.com -> 151.242.164.72
proxy.api.lilifamily.com -> <其它 proxy-ng 节点 IP>
catproxy.lilifamily.com -> 216.45.59.243
origin.proxy.api.lilifamily.com -> 216.45.59.243
```
3c4g 新增 nginx server block:
```nginx
server {
server_name origin.proxy.api.lilifamily.com;
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/origin.proxy.api.lilifamily.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/origin.proxy.api.lilifamily.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Host proxy.api.lilifamily.com;
proxy_set_header X-Forwarded-Host $http_x_forwarded_host;
proxy_set_header X-Forwarded-Proto https;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_buffering off;
proxy_request_buffering off;
}
}
```
JPpro 后续改为:
```nginx
proxy_pass https://origin.proxy.api.lilifamily.com;
proxy_ssl_server_name on;
proxy_ssl_name origin.proxy.api.lilifamily.com;
proxy_set_header Host origin.proxy.api.lilifamily.com;
proxy_set_header X-Forwarded-Host $host;
```
#### 风险
- 新增域名证书签发失败会导致 origin 不可用。
- 如果直接切 JPpro 回源而未保留回滚,会造成 JPpro 入口不可用。
#### 缓解
- 先用 `curl --resolve` 验证 origin,再切 JPpro。
- JPpro 原配置保留备份;失败时把 `proxy_pass` 改回 `https://catproxy.lilifamily.com`。
### 4.2 来源 IP 白名单
#### 作用
来源 IP 白名单保证 origin 只接受你控制的 proxy-ng 节点访问。
即使别人知道 `origin.proxy.api.lilifamily.com`,只要请求来源 IP 不是 JPpro 或后续受控节点,就会被拒绝。
#### 实施方案
3c4g origin nginx 增加:
```nginx
location / {
allow 151.242.164.72; # JPpro
deny all;
proxy_pass http://127.0.0.1:8080;
}
```
如果服务器防火墙可控,建议同时在系统防火墙层限制:
```text
允许 151.242.164.72 访问 443 或 origin 专用端口
拒绝其他公网来源访问 origin 入口
```
后续新增节点时,只追加节点 IP:
```nginx
allow 151.242.164.72; # JPpro
allow x.x.x.x; # 新 proxy-ng 节点
deny all;
```
#### 风险
- 节点 IP 写错会导致 proxy-ng 回源 403。
- VPS 如果更换 IP,origin 会继续拒绝旧配置之外的来源。
#### 缓解
- 每个节点目录必须记录公网 IP。
- 新增或迁移节点时,先更新 origin allowlist,再切 DNS 或入口。
### 4.3 `X-Proxy-Ng-Token`
#### 作用
IP 白名单证明“请求来自这台服务器 IP”,但不能证明“请求一定来自你配置的 nginx”。
`X-Proxy-Ng-Token` 是第二道校验:
- 防止同一节点上其他进程误打 origin。
- 防止节点配置被部分滥用。
- 为后续多节点密钥轮换提供边界。
正确边界是:
```text
来源 IP 白名单 + X-Proxy-Ng-Token + HTTPS 回源
```
Token 不是防火墙替代品。
#### 实施方案
JPpro 注入 token:
```nginx
proxy_set_header X-Proxy-Ng-Token "REDACTED_SECRET";
proxy_set_header X-Proxy-Ng-Node "jppro-akile-0504-1c1g";
```
3c4g origin 校验 token:
```nginx
map $http_x_proxy_ng_token $proxy_ng_token_ok {
default 0;
"REDACTED_SECRET" 1;
}
map $http_x_forwarded_host $sub2api_host {
default $http_x_forwarded_host;
"" "proxy.api.lilifamily.com";
}
server {
server_name origin.proxy.api.lilifamily.com;
location / {
if ($proxy_ng_token_ok = 0) {
return 403;
}
proxy_pass http://127.0.0.1:8080;
proxy_set_header X-Proxy-Ng-Token "";
}
}
```
真实 token 保存建议:
| 位置 | 规则 |
|---|---|
| 远程服务器 root-only 配置 | 可以保存真实 token,权限建议 `600` |
| 仓库 `ops/remote/**/current/` | 只保存脱敏副本 |
| 仓库 `secrets/` | 如需本机私有备份,必须被 `.gitignore` 排除 |
#### 风险
- 所有节点共用一个 token 时,泄露后需要同步更新所有节点。
- nginx 配置中使用明文 token,远程文件权限要控制好。
#### 缓解
- 第一阶段可以单 token。
- 第二阶段支持双 token:先加新 token,再滚动节点,最后删旧 token。
- 真实 token 不写入 Git。
### 4.4 覆盖客户端伪造 header
#### 作用
客户端可以自己发送这些 header:
```text
X-Proxy-Ng-Token
X-Proxy-Ng-Node
X-Forwarded-For
X-Real-IP
X-Forwarded-Host
```
如果 proxy-ng 不覆盖,后端日志和安全判断可能被伪造。
#### 实施方案
JPpro 对安全相关 header 使用 `proxy_set_header` 覆盖:
```nginx
proxy_set_header X-Proxy-Ng-Token "REDACTED_SECRET";
proxy_set_header X-Proxy-Ng-Node "jppro-akile-0504-1c1g";
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Host $host;
```
3c4g origin 校验后清除 token,不继续传给 Sub2API:
```nginx
proxy_set_header X-Proxy-Ng-Token "";
proxy_set_header X-Proxy-Ng-Node $http_x_proxy_ng_node;
```
#### 风险
- 如果 `X-Forwarded-For` 处理不当,Sub2API 可能拿不到真实客户端 IP。
- 如果信任任意 `X-Forwarded-For`,日志和限流可能被客户端伪造。
#### 缓解
- 第一阶段先让 JPpro 覆盖 `X-Forwarded-For` 为 `$remote_addr`,保证不可伪造。
- 后续如接入多级代理,再单独设计可信真实 IP 链路。
### 4.5 入口策略
#### 作用
入口策略决定用户应该访问哪条链路。
如果同时存在多个入口但没有规则,会出现:
- 有的用户走 JPpro。
- 有的用户绕过 JPpro 直连 3c4g。
- 出问题时无法快速判断是哪条链路坏了。
#### 推荐阶段
| 阶段 | 入口策略 | 目的 |
|---|---|---|
| P0 迁移期 | `catproxy.lilifamily.com` 可短期直连,`jp.proxy.api.lilifamily.com` 用于测试中转 | 建立回滚入口 |
| P1 加固期 | JPpro 改走 origin + IP 白名单 + token | 验证安全边界 |
| P2 稳定期 | `proxy.api.lilifamily.com` 多 A 指向 proxy-ng 节点 | 常规用户不直连 3c4g |
#### 实施方案
当前不依赖 Cloudflare LB。公开入口采用普通 DNS 多 A 或手工切换,接受“坏节点不会自动摘除”的限制。
先保持:
```text
proxy.api.lilifamily.com -> 一个或多个 proxy-ng 节点
jp.proxy.api.lilifamily.com -> JPpro proxy-ng
*.proxy.api.lilifamily.com -> 后续 proxy-ng 节点命名空间
origin.proxy.api.lilifamily.com -> 3c4g origin,仅供 proxy-ng 回源
catproxy.lilifamily.com -> 3c4g 受限直连/管理/回滚入口
```
稳定期约束:
```text
proxy.api.lilifamily.com 不能解析到 3c4g
proxy-ng 节点不能把 catproxy.lilifamily.com 暴露给用户
catproxy.lilifamily.com 不能作为常规公开业务入口
```
### 4.6 监控
#### 作用
proxy-ng 是多段链路。用户报慢或报错时,需要区分:
- JPpro nginx 是否正常。
- JPpro 到 3c4g origin 是否正常。
- 3c4g nginx 是否正常。
- Sub2API 是否正常。
- 证书是否即将过期。
#### 实施方案
最小监控项:
| 检查项 | 期望 |
|---|---|
| `https://jp.proxy.api.lilifamily.com/health` | `200` |
| `https://proxy.api.lilifamily.com/health` | `200`,且解析到 proxy-ng 节点 |
| `https://catproxy.lilifamily.com/health` 从管理 IP/Tailscale 访问 | `200` |
| `https://catproxy.lilifamily.com/health` 从普通公网访问 | `403` 或 `444` |
| `https://origin.proxy.api.lilifamily.com/health` 从非白名单来源访问 | `403` 或连接被拒绝 |
| `https://origin.proxy.api.lilifamily.com/health` 从 JPpro 访问 | `200` |
| `POST /v1/responses` 无授权访问 | `401 API_KEY_REQUIRED` |
| 证书剩余天数 | 小于 15 天告警 |
| nginx 服务状态 | 非 active 告警 |
可以用 Uptime Kuma,或先用 cron + webhook。
#### 风险
- 只监控 `/health` 不足以发现 API 路由问题。
- 从本机监控 origin 可能因为 IP 白名单而得到 403,这是预期,不是故障。
#### 缓解
- 同时监控 `/health` 和未授权 API 路由。
- origin 监控要从 JPpro 节点执行。
### 4.7 日志可观测
#### 作用
响应慢时要知道慢在哪里。
关键区分:
| 字段 | 含义 |
|---|---|
| `request_time` | 当前 nginx 从接收请求到响应完成的总耗时 |
| `upstream_response_time` | 当前 nginx 等上游响应的耗时 |
| `upstream_status` | 上游返回状态,例如 200、401、502、504 |
| `X-Proxy-Ng-Node` | 哪个 proxy-ng 节点处理了请求 |
| `request_id` | 一次请求的追踪 ID |
#### 实施方案
JPpro 和 3c4g origin 都建议增加专用 log format:
```nginx
log_format proxy_ng '$remote_addr "$request" status=$status '
'request_time=$request_time '
'upstream_status=$upstream_status '
'upstream_time=$upstream_response_time '
'node=$sent_http_x_proxy_ng_node '
'request_id=$request_id';
access_log /var/log/nginx/proxy-ng-access.log proxy_ng;
```
#### 风险
- access log 过大导致磁盘占满。
#### 缓解
- 配置 logrotate。
- 稳定后可只保留错误日志和采样日志。
### 4.8 无 Cloudflare LB 的防攻击方案
#### 作用
不依赖 Cloudflare LB 后,公网攻击会直接打到 proxy-ng 节点。防攻击目标不是承诺抗大规模 DDoS,而是降低常见扫描、CC、小流量洪泛、慢连接和暴力破解对服务的影响,并保证攻击不会绕过 proxy-ng 打到 3c4g 主站。
API 网关类服务常见风险:
- 大量并发连接。
- 大请求体。
- 慢速上传。
- 登录和验证码接口被刷。
- 流式响应占用长连接。
- 路径扫描,例如 `/.env`、`/.git`、`/wp-login.php`。
- 直接扫描 3c4g IP、`catproxy` 或 `origin`。
#### 实施方案
第一层:proxy-ng 节点系统防火墙。
```text
入站允许:
- 80/tcp、443/tcp:公网用户访问 proxy-ng
- 22/tcp:只允许管理 IP 或 Tailscale/内网管理入口
入站拒绝:
- 其它所有端口
- 1080、7890、mixed、socks、xray、sing-box 等出口代理端口
```
第二层:proxy-ng 节点 nginx 基础抗压。
```nginx
worker_processes auto;
worker_rlimit_nofile 65535;
events {
worker_connections 4096;
multi_accept on;
}
http {
sendfile on;
tcp_nopush on;
reset_timedout_connection on;
keepalive_timeout 20s;
keepalive_requests 100;
client_header_timeout 10s;
client_body_timeout 30s;
send_timeout 30s;
client_max_body_size 100m;
client_body_buffer_size 256k;
large_client_header_buffers 4 16k;
limit_conn_zone $binary_remote_addr zone=conn_per_ip:20m;
limit_req_zone $binary_remote_addr zone=req_per_ip:20m rate=10r/s;
limit_req_zone $binary_remote_addr zone=auth_per_ip:10m rate=10r/m;
}
```
第三层:proxy-ng 节点 nginx 路由限流。
```nginx
location ~ /\.(git|svn|hg|env|aws|ssh) {
return 444;
}
location ~* ^/(wp-admin|wp-login\.php|phpmyadmin|cgi-bin|vendor|boaform) {
return 444;
}
location / {
limit_req zone=req_per_ip burst=30 nodelay;
limit_conn conn_per_ip 50;
proxy_pass https://origin.proxy.api.lilifamily.com;
}
location ~ ^/api/v1/auth/(login|login/2fa|register|send-verify-code|forgot-password|reset-password) {
limit_req zone=auth_per_ip burst=3 nodelay;
limit_conn conn_per_ip 10;
proxy_pass https://origin.proxy.api.lilifamily.com;
}
```
第四层:3c4g origin 防绕过。
```text
3c4g 防火墙:
- 只允许 proxy-ng 节点 IP 访问 origin 入口端口
- 只允许管理 IP 或 Tailscale 访问 SSH
- Sub2API 8080 只监听 127.0.0.1 或 Docker 内网,不直接暴露公网
3c4g nginx:
- origin server 只 allow proxy-ng 节点 IP
- 必须校验 X-Proxy-Ng-Token
- default_server 对未知 Host 返回 444
- catproxy 入口只用于受限直连/回滚,不作为公开业务入口
```
第五层:fail2ban 和日志轮转。
```text
fail2ban 建议规则:
- nginx-404:短时间大量 404/444 扫描封禁
- nginx-limit-req:短时间大量 429 封禁
- sshd:SSH 暴力破解封禁
logrotate:
- nginx access/error 日志按大小轮转
- 保留 3-7 份
- 防止攻击流量把磁盘写满
```
第六层:Sub2API 成本保护。
```text
- API Key 配额和余额限制
- 单 key 并发限制
- 单 key 速率限制
- 异常 401/429/5xx 告警
- 高额模型调用成本预警
```
#### 风险
- 这些措施不能替代专业 DDoS 清洗;大流量打满 VPS 带宽时仍会不可用。
- 太严格会误伤正常 API 客户端。
- 流式响应连接时间长,连接数限制过低会误伤。
- 多 A 普通 DNS 没有健康摘除;坏节点仍可能被解析到。
#### 缓解
- 先在 JPpro 测试入口启用。
- 观察 429 和连接拒绝日志后再调参。
- 对 `/v1/responses` 等长连接路径不要过度收紧。
- `proxy.api.lilifamily.com` TTL 设低,例如 60-300 秒,故障时手工删除坏节点 A 记录。
- 保留至少一个受限直连回滚入口,但不公开给普通用户。
### 4.9 备份和回滚
#### 作用
proxy-ng 节点无业务数据,真正不能丢的是 3c4g 的数据库、Redis、Sub2API 数据目录和运行配置。
入口改造前必须有回滚方案,否则一次 nginx 配置错误可能导致入口不可用。
#### 实施方案
改动前备份:
```text
3c4g:
- /etc/nginx/sites-available/sub2api.conf
- /etc/nginx/sites-enabled/sub2api.conf
- /opt/sub2api/docker-compose.yml
- /opt/sub2api/.env
- PostgreSQL dump
- /opt/sub2api/data
JPpro:
- /etc/nginx/sites-available/jp-proxy-sub2api.conf
- /etc/nginx/conf.d/proxy-ng-map.conf
```
回滚路径:
| 故障 | 回滚方式 |
|---|---|
| origin 配置失败 | JPpro `proxy_pass` 改回 `https://catproxy.lilifamily.com` |
| token 校验误拦截 | 临时关闭 origin token 校验,恢复后重新加固 |
| IP 白名单误拦截 | 修正 `allow` 节点 IP 或临时回滚到公网主入口 |
| JPpro 故障 | 用户切回 `https://catproxy.lilifamily.com` |
| 3c4g 服务异常 | 使用 3c4g 备份和 Docker Compose 回滚 |
### 4.10 多节点扩展
#### 作用
多节点用于提升入口稳定性,但不是简单“节点越多越好”。
不使用 Cloudflare LB 时,多 A 记录会把用户随机分配到节点。某个节点故障后,DNS 仍可能返回坏 IP,表现为部分用户随机失败。
#### 实施方案
新增节点标准流程:
```text
1. 新建 ops/remote/-sub2api/
2. 安装 nginx + certbot
3. 配置 proxy-ng node id
4. 回源 origin.proxy.api.lilifamily.com
5. 3c4g origin allow 新节点 IP
6. 给新节点配置 token
7. 验证 /health、/、/v1/responses
8. 加入 `proxy.api.lilifamily.com` 多 A 记录
```
DNS 策略:
| 阶段 | 方案 | 风险 |
|---|---|---|
| P0 | 单节点入口 | 简单,但节点故障需手工切换 |
| P1 | `proxy.api.lilifamily.com` 多 A 记录,TTL 60-300 秒 | 成本低,但坏节点不会自动摘除 |
| P2 | 自建监控 + 手工或脚本更新 DNS | 不依赖 Cloudflare LB,但需要自己处理摘除 |
当前确认不采用 Cloudflare Load Balancing,因此方案不承诺按用户网络质量或节点健康自动调度。节点健康只能通过监控告警后手工移除 DNS 记录,或后续自建 DNS 更新脚本实现。
## 5. 推荐实施顺序
### 阶段 0:主站隐藏和入口域名迁移基线
先把公开入口和 3c4g 直连入口拆开,避免 proxy-ng 节点或公开 DNS 暴露主站。
动作:
1. `proxy.api.lilifamily.com` 从 3c4g 移走,改为 A 到 proxy-ng 节点,例如 JPpro。
2. DNS 增加或确认 `catproxy.lilifamily.com -> 216.45.59.243`,但它只作为受限直连、管理和回滚入口。
3. 3c4g nginx 为 `catproxy.lilifamily.com` 签发证书,但访问限制为管理 IP、Tailscale 或临时回滚窗口。
4. Sub2API 对外公开域名应保持为用户访问入口 `proxy.api.lilifamily.com`,避免页面、回调或错误信息暴露 `catproxy.lilifamily.com`。
5. JPpro 临时回源可用 `https://catproxy.lilifamily.com`,但只作为切 origin 前的过渡。
目标基线链路:
```text
proxy.api.lilifamily.com -> JPpro/proxy-ng -> catproxy.lilifamily.com(临时) -> 3c4g -> Sub2API
```
验收:
- `proxy.api.lilifamily.com/health` 返回 200,且解析到 proxy-ng 节点 IP。
- `jp.proxy.api.lilifamily.com/health` 返回 200。
- `jp.proxy.api.lilifamily.com/` 返回 200。
- 普通公网访问 `catproxy.lilifamily.com` 被拒绝,或仅在明确回滚窗口内允许。
- 未授权 `POST /v1/responses` 返回 401。
### 阶段 1:3c4g 增加 origin
动作:
1. DNS 增加 `origin.proxy.api.lilifamily.com -> 216.45.59.243`。
2. 3c4g nginx 新增 origin server block。
3. certbot 为 origin 域名签发证书。
4. 暂不切 JPpro,先验证 origin 本身。
验收:
- 从 3c4g 本机访问 `127.0.0.1:8080/health` 正常。
- 从 JPpro 访问 `https://origin.proxy.api.lilifamily.com/health` 正常。
- 从非白名单来源访问 origin 被拒绝。
### 阶段 2:origin 加 IP 白名单和 token
动作:
1. 3c4g origin 允许 JPpro IP `151.242.164.72`。
2. 3c4g origin 配置 `X-Proxy-Ng-Token` 校验。
3. JPpro 配置注入 token。
4. token 真实值只保存在远程 root-only 配置。
验收:
- JPpro 带 token 访问 origin 返回 200。
- JPpro 不带 token 访问 origin 返回 403。
- 非 JPpro IP 访问 origin 返回 403 或连接拒绝。
- origin 不把 `X-Proxy-Ng-Token` 继续传给 Sub2API。
### 阶段 3:JPpro 切换 origin 回源
动作:
1. 备份 JPpro 当前 nginx 配置。
2. JPpro `proxy_pass` 从 `https://catproxy.lilifamily.com` 改为 `https://origin.proxy.api.lilifamily.com`。
3. `nginx -t` 后 reload。
4. 公网验证 JPpro 入口。
验收:
- `https://jp.proxy.api.lilifamily.com/health` 返回 200。
- `https://jp.proxy.api.lilifamily.com/` 返回 200。
- 未授权 `POST /v1/responses` 返回 401。
- 3c4g origin access log 能看到 `X-Proxy-Ng-Node=jppro-akile-0504-1c1g`。
### 阶段 4:完善监控、日志、限流
动作:
1. 增加 proxy-ng 专用 access log。
2. 配置证书到期检查。
3. 配置 JPpro 和 3c4g 健康检查。
4. 先在 JPpro 入口启用保守限流、连接数限制和扫描路径拦截。
5. 配置 fail2ban 和 logrotate。
6. 3c4g 防火墙确认只允许 proxy-ng 节点访问 origin。
验收:
- 能通过日志区分 JPpro 耗时和 3c4g origin 耗时。
- 证书剩余天数低于阈值能告警。
- 429 数量可观测,不误伤正常请求。
- 访问 `/.env`、`/.git/config`、`/wp-login.php` 等扫描路径返回 444 或 403。
- 3c4g `8080` 不对公网开放。
### 阶段 5:评估是否增加第二个 proxy-ng 节点
动作:
1. 选择第二台节点。
2. 按同样目录规范留痕。
3. 先不进正式 DNS,用 `--resolve` 验证。
4. 通过 `--resolve proxy.api.lilifamily.com:443:<节点IP>` 逐个验证。
5. 验证通过后加入 `proxy.api.lilifamily.com` 多 A 记录。
验收:
- 第二节点独立验证通过。
- origin allowlist 已补第二节点 IP。
- 节点配置副本已保存到项目 `ops/remote/-sub2api/`。
- `proxy.api.lilifamily.com` 的多 A 记录不包含 3c4g IP。
## 6. 配置草案
### 6.1 3c4g origin nginx 草案
```nginx
map $http_x_proxy_ng_token $proxy_ng_token_ok {
default 0;
"REDACTED_SECRET" 1;
}
map $http_x_forwarded_host $sub2api_host {
default $http_x_forwarded_host;
"" "proxy.api.lilifamily.com";
}
server {
server_name origin.proxy.api.lilifamily.com;
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/origin.proxy.api.lilifamily.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/origin.proxy.api.lilifamily.com/privkey.pem;
server_tokens off;
underscores_in_headers on;
client_max_body_size 100m;
location / {
allow 151.242.164.72;
deny all;
if ($proxy_ng_token_ok = 0) {
return 403;
}
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Host $sub2api_host;
proxy_set_header X-Real-IP $http_x_real_ip;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Host $http_x_forwarded_host;
proxy_set_header X-Proxy-Ng-Token "";
proxy_set_header X-Proxy-Ng-Node $http_x_proxy_ng_node;
proxy_redirect https://origin.proxy.api.lilifamily.com/ https://$sub2api_host/;
proxy_redirect https://catproxy.lilifamily.com/ https://$sub2api_host/;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_buffering off;
proxy_request_buffering off;
}
}
```
待审阅点:`X-Real-IP` 和 `X-Forwarded-For` 是否按第一阶段“不可伪造优先”处理,还是按“保留真实客户端 IP 链路”处理。当前草案偏向保留链路,但需要结合 Sub2API 的 `trusted_proxies` 配置验证。
### 6.2 JPpro nginx 草案
```nginx
server {
server_name proxy.api.lilifamily.com jp.proxy.api.lilifamily.com;
server_tokens off;
underscores_in_headers on;
client_max_body_size 100m;
add_header X-Proxy-Ng-Node "jppro-akile-0504-1c1g" always;
location / {
proxy_pass https://origin.proxy.api.lilifamily.com;
proxy_ssl_server_name on;
proxy_ssl_name origin.proxy.api.lilifamily.com;
proxy_http_version 1.1;
proxy_set_header Host origin.proxy.api.lilifamily.com;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Proxy-Ng-Node "jppro-akile-0504-1c1g";
proxy_set_header X-Proxy-Ng-Token "REDACTED_SECRET";
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_buffering off;
proxy_request_buffering off;
}
}
```
待审阅点:当前 JPpro 配置已有 `client_max_body_size 256m`,草案写 `100m` 是为了和 3c4g 主入口一致。是否保留 `256m` 需要根据实际请求体规模确认。
### 6.3 3c4g catproxy 受限直连草案
`catproxy.lilifamily.com` 不作为常规用户入口。它的作用是运维验证和紧急回滚,因此默认应限制来源。
```nginx
server {
server_name catproxy.lilifamily.com;
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/catproxy.lilifamily.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/catproxy.lilifamily.com/privkey.pem;
server_tokens off;
underscores_in_headers on;
client_max_body_size 100m;
# 管理 IP、Tailscale 出口或临时回滚窗口内的来源。
allow ;
deny all;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Host proxy.api.lilifamily.com;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Host proxy.api.lilifamily.com;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
proxy_buffering off;
proxy_request_buffering off;
}
}
server {
listen 443 ssl default_server;
server_name _;
return 444;
}
```
如果管理 IP 不固定,优先使用 Tailscale、SSH 隧道或临时开放窗口,不建议长期把 `catproxy.lilifamily.com` 暴露给公网。
## 7. 验收清单
### 7.1 基础链路
- [ ] `https://proxy.api.lilifamily.com/health` 返回 200,且 DNS 解析结果只包含 proxy-ng 节点 IP。
- [ ] 从管理 IP/Tailscale 访问 `https://catproxy.lilifamily.com/health` 返回 200。
- [ ] `https://jp.proxy.api.lilifamily.com/health` 返回 200。
- [ ] `https://jp.proxy.api.lilifamily.com/` 返回 200。
- [ ] 未授权 `POST https://jp.proxy.api.lilifamily.com/v1/responses` 返回 `401 API_KEY_REQUIRED`。
- [ ] 普通公网访问 `https://catproxy.lilifamily.com/` 返回 403/444,除非处于明确回滚窗口。
### 7.2 origin 边界
- [ ] JPpro 带 token 访问 `https://origin.proxy.api.lilifamily.com/health` 返回 200。
- [ ] JPpro 不带 token 访问 origin 返回 403。
- [ ] 非 JPpro IP 访问 origin 返回 403 或连接被拒绝。
- [ ] origin 日志能看到 `X-Proxy-Ng-Node`。
- [ ] origin 不把 `X-Proxy-Ng-Token` 继续传给 Sub2API。
### 7.3 回滚验证
- [ ] JPpro 配置可一条改回 `proxy_pass https://catproxy.lilifamily.com`。
- [ ] 3c4g 受限入口 `catproxy.lilifamily.com` 对管理 IP/Tailscale 可访问,对普通公网不可访问。
- [ ] 远程配置备份路径已记录。
### 7.4 运维验证
- [ ] `nginx -t` 通过。
- [ ] `systemctl is-active nginx` 返回 `active`。
- [ ] 证书到期时间已记录。
- [ ] 访问日志可区分 `request_time` 和 `upstream_response_time`。
- [ ] 限流启用后没有误伤正常 API 请求。
- [ ] `/.env`、`/.git/config`、`/wp-login.php` 等扫描路径返回 444/403。
- [ ] fail2ban 对高频 404/429 和 SSH 爆破生效。
- [ ] logrotate 已限制 nginx 日志增长。
- [ ] `proxy.api.lilifamily.com` 的任一 A 记录故障时,有手工摘除流程。
## 8. 风险和取舍
| 风险 | 严重性 | 原因 | 缓解 |
|---|---|---|---|
| origin 配置错误导致 JPpro 入口不可用 | 高 | JPpro 切到 origin 后依赖新链路 | 先验证 origin,再切 JPpro;保留回滚 |
| IP 白名单误配 | 高 | 节点 IP 写错或 VPS 换 IP | 操作前核对公网 IP;变更留痕 |
| token 泄露 | 中 | nginx 远程配置含明文 token | 权限 `600`;仓库只保存脱敏副本 |
| 真实客户端 IP 丢失 | 中 | 为防伪造而覆盖 `X-Forwarded-For` | 明确第一阶段优先级;后续再做可信 IP 链路 |
| 限流误伤正常请求 | 中 | API 客户端并发高、流式请求长 | 先保守配置并观察日志 |
| 大流量 DDoS 打满 proxy-ng 带宽 | 高 | 不使用 Cloudflare LB/清洗时,攻击流量直达 VPS | 接受该边界;多节点分散;必要时换高防/CDN |
| `catproxy` 被公网发现并直接访问 | 高 | 3c4g 直连入口暴露会绕过 proxy-ng | 默认 deny all,仅管理 IP/Tailscale/临时回滚允许 |
| 多 A 记录随机命中坏节点 | 中 | 普通 DNS 无健康检查 | TTL 60-300 秒;监控告警后手工摘除坏 A 记录 |
| 日志占满磁盘 | 低 | access log 增多 | 配置 logrotate 或采样 |
## 9. 待确认项
- [x] 公开服务入口采用 `proxy.api.lilifamily.com`,但只解析到 proxy-ng 节点。
- [x] `catproxy.lilifamily.com` 作为 3c4g 受限直连、管理和紧急回滚入口。
- [x] origin 回源入口采用 `origin.proxy.api.lilifamily.com`。
- [x] proxy-ng 节点统一使用 `.proxy.api.lilifamily.com` 命名。
- [x] 不依赖 Cloudflare Load Balancing;`proxy.api.lilifamily.com` 使用普通 A 记录指向 proxy-ng 节点。
- [ ] `catproxy.lilifamily.com` 的长期访问策略:仅 Tailscale/管理 IP,还是完全不开放公网。
- [ ] JPpro 到 origin 是否继续使用 443,还是改用 origin 专用端口。
- [ ] 第一阶段是否优先保证 `X-Forwarded-For` 不可伪造,接受真实客户端 IP 链路暂时简化。
- [ ] token 采用单 token 还是一开始就做双 token 轮换。
- [ ] 是否立即启用限流、fail2ban、扫描路径 444,还是先只增强 origin 安全边界。
- [ ] 监控使用 Uptime Kuma、cron + webhook,还是暂时手工巡检。
- [ ] 下一台 proxy-ng 节点是否已经确定。
## 10. 下一步
建议先审阅并确认以下决策:
1. origin 域名和是否使用 443。
2. JPpro 回源切换窗口和回滚策略。
3. `X-Forwarded-For` 第一阶段处理策略。
4. token 保存位置和轮换方式。
确认后再输出执行版文档,执行版应包含:
- 远程配置文件路径。
- 备份命令。
- 变更步骤。
- 验证命令。
- 回滚命令。
- 本地 `ops/remote/` 留痕更新清单。