diff --git a/backend/app/routers/console.py b/backend/app/routers/console.py index b2f3901..cb30c31 100644 --- a/backend/app/routers/console.py +++ b/backend/app/routers/console.py @@ -9,15 +9,16 @@ 3. Проксирует бинарный VNC-поток между браузером клиента и Proxmox. Особенности Proxmox: - - PVE может вернуть HTTP redirect (302) на `https://...` вместо `wss://...` - при WebSocket-handshake. Библиотека `websockets` не умеет следовать за - такими redirect (https ≠ ws/wss), поэтому перехватываем redirect - и переписываем scheme. + - В Proxmox 8.x+ vncticket может содержать спецсимволы (#, /, %, :), + которые ломают парсинг URL (RFC 3986). Кодируем их через quote(). + - PVE может вернуть HTTP redirect (302) на `https://...` вместо `wss://...`; + перехватываем и переписываем scheme через process_redirect. """ import asyncio import logging from typing import Optional, Tuple +from urllib.parse import quote import httpx import websockets @@ -93,9 +94,14 @@ async def console_ws( guest_path = "qemu" if guest_type == "vm" else "lxc" pve_host_only = settings.pve_host.replace("http://", "").replace("https://", "") + # quote() экранирует спецсимволы в тикете и порте по правилам RFC 3986, + # не затрагивая уже экранированные символы. Без этого в Proxmox 8.x+ + # vncticket может содержать # / % : которые ломают URL. + safe_ticket = quote(ticket, safe="") + safe_port = quote(str(port), safe="") upstream_url = ( f"wss://{pve_host_only}/api2/json/nodes/{node}/{guest_path}/{vmid}/vncwebsocket" - f"?port={port}&vncticket={ticket}" + f"?port={safe_port}&vncticket={safe_ticket}" ) # Определяем способ аутентификации.