feat(proxy): 安全加固与架构优化

- 引入 SafeDnsResolver 消除 DNS rebinding TOCTTOU 漏洞,DNS 缓存改为多地址存储
- 扩展私有 IP 检测范围(CGNAT 100.64/10、基准测试 198.18/15、保留 240/4)
- 请求处理增加 hop-by-hop 头过滤、URL scheme 校验、超时范围限制
- 动态配置从 RwLock 切换到 ArcSwap 实现无锁读取
- 启动注册失败的服务器支持后台自动重试
- WebSocket 帧大小上限提升至 64MiB 匹配 Python 端
- 心跳支持动态间隔更新,新增 failed_requests/dns_failures/stream_errors 指标
- 配置启动校验、systemd UMask=0077、配置文件权限 600
- Python 端支持 per-connection max_streams(X-Tunnel-Max-Streams)
This commit is contained in:
fawney19
2026-02-28 01:32:28 +08:00
parent 2a0c684e88
commit e748277902
22 changed files with 746 additions and 149 deletions

View File

@@ -3,7 +3,11 @@ import asyncio
import pytest
from starlette.websockets import WebSocketState
from src.services.proxy_node.tunnel_manager import TunnelConnection, TunnelManager
from src.services.proxy_node.tunnel_manager import (
TunnelConnection,
TunnelManager,
TunnelStreamError,
)
from src.services.proxy_node.tunnel_protocol import Frame, MsgType
@@ -117,3 +121,45 @@ async def test_removed_connection_frames_ignored() -> None:
pong = Frame.decode(ws2.sent[0])
assert pong.msg_type == MsgType.PONG
assert pong.payload == b"hello"
@pytest.mark.asyncio
async def test_max_streams_from_header() -> None:
"""TunnelConnection respects proxy-advertised max_streams (clamped)"""
ws = _DummyWebSocket()
# Explicit value within range
conn = TunnelConnection("n", "n", ws, max_streams=256) # type: ignore[arg-type]
assert conn.max_streams == 256
# Clamped to minimum 64
conn_low = TunnelConnection("n", "n", ws, max_streams=10) # type: ignore[arg-type]
assert conn_low.max_streams == 64
# Clamped to maximum 2048
conn_high = TunnelConnection("n", "n", ws, max_streams=9999) # type: ignore[arg-type]
assert conn_high.max_streams == 2048
# None falls back to TunnelManager.MAX_STREAMS_PER_CONN
conn_default = TunnelConnection("n", "n", ws) # type: ignore[arg-type]
assert conn_default.max_streams == TunnelManager.MAX_STREAMS_PER_CONN
@pytest.mark.asyncio
async def test_send_request_respects_per_conn_max_streams() -> None:
"""send_request raises TunnelStreamError when per-connection limit is reached"""
manager = TunnelManager()
ws = _DummyWebSocket()
# Set a very low max_streams (clamped to minimum 64)
conn = TunnelConnection("node-1", "node-1", ws, max_streams=64) # type: ignore[arg-type]
manager.register(conn)
# Fill up to max_streams
for i in range(64):
conn.create_stream(i * 2 + 2)
assert conn.stream_count == 64
# Next send_request should fail
with pytest.raises(TunnelStreamError, match="stream limit reached"):
await manager.send_request("node-1", method="GET", url="https://example.com", headers={})