Files
dht/docs/xray-transparent-proxy.md
2026-08-10 19:34:30 +08:00

188 lines
4.9 KiB
Markdown

# Xray 透明代理与 DHT 直连处理
本文只适用于宿主机启用了 Xray TProxy 或其他全局透明代理的特殊部署环境
普通 Docker 主机直接使用根目录 `compose.yaml` 不需要这里的专用网络或 nftables 规则
## 为什么会发生
DHT 会向大量公网节点发送 UDP 查询 并尝试连接大量 Peer 的随机 TCP 端口
全局 TProxy 如果在 `prerouting` 中接管所有公网 TCP 和 UDP Docker 容器流量也会进入 Xray
Xray 即使最终选择直连出站仍然需要维护这些 UDP 会话 因此可能积累大量 socket 并让 Metadata TCP 连接失败
## 典型现象
- DHT 节点 Peer 和采样 infohash 持续增长
- Metadata 成功长期为零或远低于正常水平
- Metadata 失败几乎全部是连接失败
- Xray 文件描述符和宿主机 UDP socket 持续增长
- SSH 或其他新连接开始偶发断开
- `dht-search` 容器自身文件描述符并不高
仅看到 Metadata 超时不能直接判断为 Xray 问题 公网 DHT 本身就包含大量不可达 Peer
## 如何确认
查看服务统计
```shell
docker run --rm --network web-network curlimages/curl:8.16.0 -fsS http://dht-search:8080/stats
```
重点比较以下字段
```text
metadata_ok
metadata_failed
metadata_failure_connect
metadata_failure_timeout
metadata_in_flight
```
查看宿主机和 Xray socket
```shell
cat /proc/net/sockstat
xray_pid="$(pidof xray)"
find "/proc/${xray_pid}/fd" -maxdepth 1 -type l | wc -l
grep "Max open files" "/proc/${xray_pid}/limits"
```
查看 DHT 容器自身 socket
```shell
docker exec dht-search cat /proc/net/sockstat
docker exec dht-search sh -c 'ls /proc/1/fd | wc -l'
```
查看透明代理规则
```shell
nft list ruleset
ip rule show
```
如果存在类似规则 Docker 公网流量会进入 Xray
```nft
meta l4proto { tcp, udp } tproxy to :12345 meta mark set 1 accept
```
## 推荐解决方案
让 DHT 同时连接反向代理网络和独立直连网络
```yaml
networks:
web-network:
external: true
name: web-network
dht-direct:
name: dht-direct
driver: bridge
ipam:
config:
- subnet: 172.30.0.0/24
services:
dht-search:
networks:
web-network:
dht-direct:
gw_priority: 1
```
`web-network` 只负责 Caddy 到 `dht-search:8080`
`dht-direct` 具有更高网关优先级并负责 DHT 和 Peer 公网流量
部署前必须确认 `172.30.0.0/24` 没有与宿主机路由或其他 Docker 网络冲突
```shell
ip route show 172.30.0.0/24
docker network inspect dht-direct
```
## 配置 Xray 来源旁路
在 Xray 持久化 nftables 文件中增加来源集合
```nft
set bypass_source_ipv4 {
type ipv4_addr
flags interval
elements = {
172.30.0.0/24
}
}
```
`prerouting` 链进入 TProxy 之前增加
```nft
ip saddr @bypass_source_ipv4 return
```
必须修改 Xray 服务启动时实际加载的持久化文件 只向当前运行规则临时执行 `nft add rule` 会在重启后失效
## 安全实施顺序
1. 使用 `docker compose stop` 优雅停止 DHT
2. 备份 Compose 和 Xray nftables 文件
3. 增加 `dht-direct` 网络和来源旁路
4. 使用 `docker compose config --quiet` 校验 Compose
5. 使用 `nft -c -f <规则文件>` 校验 nftables
6. 重启 Xray 释放已经积累的旧会话
7. 使用 `docker compose up -d` 重新创建 DHT 容器
8. 验证默认网关 Metadata 成功率和 socket 数量
重启 Xray 会短暂中断现有代理连接 应选择允许短暂中断的时间执行
## 验收
确认容器同时加入两个网络且没有宿主机端口绑定
```shell
docker compose ps
docker inspect dht-search --format '{{json .HostConfig.PortBindings}}'
docker inspect dht-search --format '{{json .NetworkSettings.Networks}}'
docker exec dht-search cat /proc/net/route
```
默认网关应该属于 `dht-direct` 然后持续观察
- `metadata_ok``persistence_inserted` 开始增长
- Metadata 失败不再全部是连接失败
- Xray 文件描述符不再随 DHT 流量快速增长
- 宿主机 UDP socket 恢复到正常范围
- Caddy 仍能通过 `web-network` 访问服务
## 实际案例
一次远端 Debian 部署中修复前 Metadata 尝试超过一百万次但成功为零 Xray 文件描述符增长到约三十九万 主机 UDP socket 超过三十三万
增加独立直连网络并重启 Xray 后两分钟内 Metadata 成功 1222 条并入库 1218 条 Xray 文件描述符稳定在 12 主机 UDP socket 稳定在 6
## 不建议的处理
- 只提高 Xray `LimitNOFILE`
- 只增加机器内存
- 只降低 Metadata 并发
- 让 DHT 流量进入 Xray 后再选择 direct outbound
这些方法只能延迟资源耗尽或仍然让 Xray 维护海量 UDP 会话 不能消除错误流量边界
## 回滚
如果专用网络启动失败
1. 停止 DHT 容器
2. 恢复原 Compose 和 nftables 备份
3. 校验并重启 Xray
4. 保持 DHT 停止直到重新确认路由方案
不要在恢复了全局 TProxy 且没有来源旁路的情况下重新启动高并发 DHT