mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 01:10:23 +08:00
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:
@@ -99,8 +99,18 @@ async def proxy_tunnel_ws(ws: WebSocket) -> None:
|
||||
node_id: str = auth[0]
|
||||
node_name: str = auth[1]
|
||||
|
||||
# Read proxy-advertised max concurrent streams (backward-compatible:
|
||||
# old proxies don't send this header, we fall back to the default).
|
||||
max_streams_raw = ws.headers.get("x-tunnel-max-streams", "").strip()
|
||||
max_streams: int | None = None
|
||||
if max_streams_raw:
|
||||
try:
|
||||
max_streams = int(max_streams_raw)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
manager = get_tunnel_manager()
|
||||
conn = TunnelConnection(node_id, node_name, ws)
|
||||
conn = TunnelConnection(node_id, node_name, ws, max_streams=max_streams)
|
||||
manager.register(conn)
|
||||
|
||||
# 更新 DB: tunnel_connected = True
|
||||
|
||||
@@ -30,16 +30,30 @@ class TunnelConnection:
|
||||
"node_name",
|
||||
"ws",
|
||||
"connected_at",
|
||||
"max_streams",
|
||||
"_pending_streams",
|
||||
"_write_lock",
|
||||
"_next_stream_id",
|
||||
)
|
||||
|
||||
def __init__(self, node_id: str, node_name: str, ws: WebSocket) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
node_id: str,
|
||||
node_name: str,
|
||||
ws: WebSocket,
|
||||
max_streams: int | None = None,
|
||||
) -> None:
|
||||
self.node_id = node_id
|
||||
self.node_name = node_name
|
||||
self.ws = ws
|
||||
self.connected_at = time.time()
|
||||
# Per-connection max concurrent streams: use proxy-advertised value
|
||||
# (from X-Tunnel-Max-Streams header), clamped to [64, 2048].
|
||||
# Falls back to TunnelManager.MAX_STREAMS_PER_CONN if not provided.
|
||||
if max_streams is not None:
|
||||
self.max_streams = max(64, min(max_streams, 2048))
|
||||
else:
|
||||
self.max_streams = TunnelManager.MAX_STREAMS_PER_CONN
|
||||
self._pending_streams: dict[int, _StreamState] = {}
|
||||
self._write_lock = asyncio.Lock()
|
||||
# Per-connection stream ID 分配器(Aether 端使用偶数,从 2 开始)
|
||||
@@ -286,12 +300,12 @@ class TunnelManager:
|
||||
if not conn:
|
||||
raise TunnelStreamError(f"tunnel not connected for node {node_id}")
|
||||
|
||||
if conn.stream_count >= self.MAX_STREAMS_PER_CONN:
|
||||
if conn.stream_count >= conn.max_streams:
|
||||
raise TunnelStreamError(
|
||||
f"tunnel stream limit reached ({self.MAX_STREAMS_PER_CONN}) for node {node_id}"
|
||||
f"tunnel stream limit reached ({conn.max_streams}) for node {node_id}"
|
||||
)
|
||||
|
||||
stream_id = conn.alloc_stream_id(self.MAX_STREAMS_PER_CONN)
|
||||
stream_id = conn.alloc_stream_id(conn.max_streams)
|
||||
stream_state = conn.create_stream(stream_id)
|
||||
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user