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.
330 lines
10 KiB
Markdown
330 lines
10 KiB
Markdown
# 国际互联网络优化 — 执行版
|
||
|
||
日期:2026-05-21 | 前置:审阅版已通过
|
||
|
||
## 1. 目标与范围
|
||
|
||
在 1C/1G 轻量 VPS 上部署 nginx 反向代理,通过 Cloudflare CDN 实现中国 IP 白名单访问控制,将流量转发到后端 2C4G 高性能服务器的 sub2api 服务。
|
||
|
||
## 2. 前置假设
|
||
|
||
- 轻量 VPS 已就绪,OS Debian/Ubuntu,有公网 IP,已配置 SSH 密钥登录
|
||
- 高性能服务器已部署 sub2api(Docker 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.com(cron 拉取 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 而非全机 OOM;Restart=always 5s 恢复 |
|
||
| 轻量 VPS 彻底宕机 | 备机 DNS 切换;或一键重建脚本 |
|
||
| 后端 502/504 | nginx 返回 502 给用户;`proxy_next_upstream error timeout http_502` 有限重试 |
|
||
| sub2api `trusted_proxies` 未配 | 限流器把所有用户视为同一 IP(nginx 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
|
||
```
|
||
|
||
---
|
||
|
||
## 附录 A:nginx 主配置
|
||
|
||
见 `NETWORK_OPTIMIZATION.md` 3.2 节完整配置。
|
||
|
||
## 附录 B:systemd override
|
||
|
||
```ini
|
||
# /etc/systemd/system/nginx.service.d/override.conf
|
||
[Service]
|
||
Restart=always
|
||
RestartSec=5s
|
||
OOMScoreAdjust=-500
|
||
MemoryMax=800M
|
||
MemoryHigh=700M
|
||
```
|
||
|
||
## 附录 C:fail2ban 配置
|
||
|
||
```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` — 一键重建 |