sub2api/docs/agent-specs/2026-05-21-国际互联网络优化-执行版.md
zizi 6814fa2a62 docs(ops): record proxy-ng deployment hardening
Add deployment traces for the JPpro proxy node, RackNerd origin hosts, catproxy boundary changes, local speed checks, and Sub2API rate-limit behavior.

Keep remote secrets excluded while tracking sanitized nginx, compose, plan, and operational memory documents.
2026-05-26 23:27:41 +08:00

330 lines
10 KiB
Markdown
Raw Permalink 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.

# 国际互联网络优化 — 执行版
日期2026-05-21 | 前置:审阅版已通过
## 1. 目标与范围
在 1C/1G 轻量 VPS 上部署 nginx 反向代理,通过 Cloudflare CDN 实现中国 IP 白名单访问控制,将流量转发到后端 2C4G 高性能服务器的 sub2api 服务。
## 2. 前置假设
- 轻量 VPS 已就绪OS Debian/Ubuntu有公网 IP已配置 SSH 密钥登录
- 高性能服务器已部署 sub2apiDocker Compose监听 8080`config.yaml` 可编辑
- 域名 `proxyapi.cyan2000.uk` 的 NS 管理权限在手
- 两台 VPS 之间通过公网 IP 通信(无内网)
- 目标用户全部在中国大陆(+ 酌情香港/澳门)
- Cloudflare 免费计划账户已注册
## 3. 涉及文件与路径
### 轻量 VPS新部署
| 路径 | 操作 | 说明 |
|---|---|---|
| `/etc/nginx/nginx.conf` | 写入 | 主配置,定义 upstream、限流区域 |
| `/etc/nginx/ssl/fullchain.pem` | certbot 签发 | Let's Encrypt 证书 |
| `/etc/nginx/ssl/privkey.pem` | certbot 签发 | 私钥 |
| `/etc/systemd/system/nginx.service.d/override.conf` | 创建 | MemoryMax + Restart |
| `/etc/fail2ban/jail.local` | 写入 | nginx-404 + nginx-limit-req 规则 |
| `/etc/fail2ban/filter.d/nginx-404.conf` | 写入 | 404 扫描过滤 |
| `/usr/local/bin/nginx-defense.sh` | 创建 | 内存压力自动降级 |
| `/usr/local/bin/cf-iptables.sh` | 创建 | CF IP 白名单更新 |
| `/usr/local/bin/rebuild-nginx.sh` | 创建 | 一键重建 |
| `/etc/cron.d/cf-iptables` | 写入 | 每日 3:00 执行 |
| `/etc/cron.d/nginx-defense` | 写入 | 每分钟执行 |
### 高性能服务器(配置变更)
| 路径 | 操作 | 说明 |
|---|---|---|
| `config.yaml` | 编辑 | `server.trusted_proxies` 加 nginx IP |
| `config.yaml` | 编辑(条件) | `security.trust_forwarded_ip_for_api_key_acl: true` |
| `/etc/ssl/private/sub2api-key.pem` | 生成 | 回源自签证书私钥 |
| `/etc/ssl/certs/sub2api-cert.pem` | 生成 | 回源自签证书 |
| `docker-compose.yml` | 编辑(可选) | nginx sidecar 做 TLS 终止 |
## 4. 数据流与依赖
```
用户浏览器
│ HTTPS 443 (Let's Encrypt)
Cloudflare CDN
│ 加头: CF-Connecting-IP, CF-IPCountry
│ HTTPS 443 (Let's Encrypt)
轻量VPS nginx (1C/1G)
│ iptables: 仅接受 CF IP 段
│ nginx: CF-IPCountry 非 CN → 403
│ 加头: X-Real-IP, X-Forwarded-For
│ HTTPS 8443 (自签证书)
后端 nginx sidecar (TLS 终止)
│ HTTP :8080
sub2api (Gin)
│ ip.GetClientIP() → CF-Connecting-IP → X-Real-IP → X-Forwarded-For
PostgreSQL / Redis
```
依赖:
- 轻量 VPS → 后端公网 IP 可达(防火墙允许 8080 或 8443
- 后端 → Redis / PostgreSQL已有
- 轻量 VPS → cloudflare.comcron 拉取 IP 段)
## 5. 接口契约与字段变化
### sub2api 配置变化
```yaml
# config.yaml 新增
server:
trusted_proxies:
- "<轻量VPS公网IP>"
- "173.245.48.0/20" # Cloudflare IP 段(如需)
# ... 或信任所有 CF IP 段
# 如果启用了 API Key IP 白名单功能
security:
trust_forwarded_ip_for_api_key_acl: true
```
### nginx 向下游传递的头
| 头名称 | 值来源 | 用途 |
|---|---|---|
| `X-Real-IP` | `$remote_addr`CF 边缘 IP | 传统兼容 |
| `X-Forwarded-For` | `$proxy_add_x_forwarded_for` | 代理链 |
| `X-Forwarded-Proto` | `$scheme`https | 协议感知 |
| `CF-Connecting-IP` | `$http_cf_connecting_ip`CF 原始头) | sub2api 优先读取 |
| `Host` | `$host`proxyapi.cyan2000.uk | 虚拟主机 |
### sub2api IP 解析优先级(`ip.go` 已有,不变)
```
CF-Connecting-IP → X-Real-IP → X-Forwarded-For(第一个公网IP) → c.ClientIP()
```
`c.ClientIP()`(限流器使用)仅在 `trusted_proxies` 含来源 IP 时才信任转发头。**必须配置**。
## 6. 边界情况与失败路径
| 场景 | 预期行为 |
|---|---|
| 用户绕过 CF 直连源站 IP | iptables DROP连接超时 |
| 用户通过 CF 但伪造 CF-IPCountry 头 | CF 会覆盖此头或剥离nginx 看到空 → 403 |
| 用户通过 CF 但来自非中国 IP | CF WAF Block第一层nginx `$http_cf_ipcountry != CN` → 403第二层 |
| CF IP 段更新iptables 未同步 | 受影响的新 CF IP 回源被 DROP用户看到 502每日 cron 最小化窗口 |
| 轻量 VPS OOM | systemd MemoryMax=800M 杀 nginx 而非全机 OOMRestart=always 5s 恢复 |
| 轻量 VPS 彻底宕机 | 备机 DNS 切换;或一键重建脚本 |
| 后端 502/504 | nginx 返回 502 给用户;`proxy_next_upstream error timeout http_502` 有限重试 |
| sub2api `trusted_proxies` 未配 | 限流器把所有用户视为同一 IPnginx IP共享限流桶 |
| CF-Connecting-IP 传递失败nginx 没透传) | sub2api 回退到 X-Forwarded-For 解析,仍能拿到真实 IP |
| Let's Encrypt 证书过期 | certbot renew 自动续期;需确保 80 端口可访问用于验证 |
| 前端磁盘满 | access_log 生产环境关掉error_log logrotate maxsize 50M |
## 7. 实施步骤
### 第一阶段:后端配置(零中断)
1. 登录高性能服务器
2. 确认 `config.yaml` 路径,备份后编辑:
```yaml
server:
trusted_proxies:
- "<轻量VPS公网IP>"
```
3. 生成自签证书:
```bash
openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
-keyout /etc/ssl/private/sub2api-key.pem \
-out /etc/ssl/certs/sub2api-cert.pem \
-subj "/CN=<后端公网IP>"
```
4. 在 `docker-compose.yml` 中添加 nginx sidecar 做 TLS 终止(监听 8443 → 转发 8080或直接用 sub2api 的 HTTPS 配置
5. 重启 sub2api / Docker Compose
6. 验证:`curl --noproxy '*' -k https://<后端公网IP>:8443/health` 返回 200
### 第二阶段Cloudflare DNS可能中断 1-2 分钟)
1. 在 Cloudflare 添加站点 `proxyapi.cyan2000.uk`
2. 将域名 NS 改为 Cloudflare 分配的 NS
3. 等 NS 生效后,添加 DNS 记录A `@` → 轻量 VPS 公网 IP橙色云朵开启
4. SSL/TLS 模式设为 Full (strict)
5. WAF 规则:`(ip.geoip.country eq "CN")` → Allow新建规则
6. 验证:`curl -H "Host: proxyapi.cyan2000.uk" https://<轻量VPS_IP>/health --resolve proxyapi.cyan2000.uk:443:<轻量VPS_IP>` — 此时 nginx 还没配,先确认 CF 能到 VPS
### 第三阶段:轻量 VPS 部署
按顺序执行:
```bash
# 1. 基础软件
apt update && apt install -y nginx certbot fail2ban iptables-persistent
# 2. 获取证书(先关 nginx 80/443或 certbot standalone 模式)
certbot certonly --standalone -d proxyapi.cyan2000.uk
# 3. 写入 /etc/nginx/nginx.conf见附录 A
# 4. 写入 systemd override见附录 B
mkdir -p /etc/systemd/system/nginx.service.d
# 5. 写入 fail2ban 配置(见附录 C
# 6. 写入脚本(见附录 D
# - /usr/local/bin/cf-iptables.sh
# - /usr/local/bin/nginx-defense.sh
# - /usr/local/bin/rebuild-nginx.sh
# 7. 执行 iptables 白名单(先 accept 所有,确认 nginx 正常后再启用 DROP
bash /usr/local/bin/cf-iptables.sh
# 8. cron
echo "0 3 * * * root /usr/local/bin/cf-iptables.sh" > /etc/cron.d/cf-iptables
echo "* * * * * root /usr/local/bin/nginx-defense.sh" > /etc/cron.d/nginx-defense
# 9. 关闭 access_log生产环境
# 编辑 nginx.conf: access_log off;
# 或保留但 logrotate: maxsize 50M rotate 3
# 10. 启动
systemctl daemon-reload
systemctl enable --now nginx fail2ban
```
### 第四阶段:端到端验证
见第 8 节验证清单。
## 8. 测试与验证
### 自动化测试
```bash
# 1. 基本连通性
curl -I https://proxyapi.cyan2000.uk/health
# 预期: 200 OK
# 2. 中国 IP 访问控制 (CF-IPCountry 头)
curl -I https://proxyapi.cyan2000.uk/ -H "CF-IPCountry: CN"
# 预期: 正常转发,非 403
curl -I https://proxyapi.cyan2000.uk/ -H "CF-IPCountry: US"
# 预期: 403 Forbidden
curl -I https://proxyapi.cyan2000.uk/
# 预期: 403 (CF-IPCountry 为空 = 直连)
# 3. 压力测试 — 限流
ab -n 200 -c 50 https://proxyapi.cyan2000.uk/health
# 预期: 部分请求返回 429 / 503
# 4. Slowloris 抗性
slowhttptest -c 50 -H -u https://proxyapi.cyan2000.uk/
# 预期: 连接不耗尽,正常请求仍能响应
# 5. 源站 IP 直连
curl -I --connect-timeout 5 https://<轻量VPS_IP>/
# 预期: 超时或被拒绝 (iptables DROP)
# 6. 限流验证 — 真实 IP
for i in $(seq 1 35); do
curl -s -o /dev/null -w "%{http_code}\n" https://proxyapi.cyan2000.uk/auth/login
done
# 预期: 前 20 个 200/401后续 429
```
### 验证清单
- [ ] `curl https://proxyapi.cyan2000.uk/health` → 200
- [ ] CF 关闭橙色云朵(直连 VPS→ `CF-IPCountry` 为空 → 403
- [ ] `CF-IPCountry: US` → 403
- [ ] `CF-IPCountry: CN` → 正常转发
- [ ] sub2api 访问日志 `client_ip` = 真实用户 IP查看日志验证
- [ ] auth 限流:同 IP 超频返回 429不同 IP 独立计数
- [ ] 流式 API SSE 逐块到达
- [ ] 源站 IP 直连超时
- [ ] `ab` 压测限流生效
- [ ] `slowhttptest` 连接不耗尽
- [ ] `fail2ban-client status` 正常
- [ ] `systemctl show nginx | grep Restart` → `Restart=always`
- [ ] `systemctl show nginx | grep MemoryMax` → `MemoryMax=838860800`
- [ ] nginx error_log 无异常
## 9. 回滚 / 降级
### 快速回滚 DNS
1. Cloudflare DNS 关闭 A 记录橙色云朵(变灰色 = DNS only
2. 或将 A 记录直接指向后端服务器 IP绕过轻量 VPS
### 回滚 sub2api 配置
```bash
# 注释掉 trusted_proxies 行,恢复原状
# 重启 sub2api
docker compose restart
```
### 轻量 VPS 紧急降级
如果 nginx 出现异常但 iptables 正常:
```bash
# 紧急放行所有流量
iptables -F CF-WHITELIST
iptables -A CF-WHITELIST -j ACCEPT
# 重启 nginx
systemctl restart nginx
```
---
## 附录 Anginx 主配置
见 `NETWORK_OPTIMIZATION.md` 3.2 节完整配置。
## 附录 Bsystemd override
```ini
# /etc/systemd/system/nginx.service.d/override.conf
[Service]
Restart=always
RestartSec=5s
OOMScoreAdjust=-500
MemoryMax=800M
MemoryHigh=700M
```
## 附录 Cfail2ban 配置
```ini
# /etc/fail2ban/jail.local
[nginx-404]
enabled = true
port = http,https
filter = nginx-404
logpath = /var/log/nginx/access.log
maxretry = 30
findtime = 60
bantime = 600
[nginx-limit-req]
enabled = true
port = http,https
filter = nginx-limit-req
logpath = /var/log/nginx/error.log
maxretry = 5
findtime = 60
bantime = 600
```
```ini
# /etc/fail2ban/filter.d/nginx-404.conf
[Definition]
failregex = ^<HOST> -.*"(GET|POST|HEAD).*" 404
ignoreregex =
```
## 附录 D脚本
见 `NETWORK_OPTIMIZATION.md` 3.4 节:
- `cf-iptables.sh` — CF IP 白名单(含 IPv6
- `nginx-defense.sh` — 内存压力自动降级
- `rebuild-nginx.sh` — 一键重建